Vault: CubbyholeとResponse Wrapping


Hashicorp VaultのCubbyholeバックエンドとResponse Wrappingに関するメモです。トークンの受け渡し時の漏洩リスクを最小限にする方法を提供してくれる機能です。

Cubbyhole

CubbyholeとはVaultのSecretバックエンドの一つです。genericバックエンドと同じくKey Value形式でデータを書くことができます。特徴はトークンごとにパスが別のスコープになっており、あるトークンで書いたデータはそのトークンからしか見ることができないことです。

example
# トークンAでデータを書き込む
$ VAULT_TOKEN=$TOKEN_A vault write cubbyhole/mysecret whoami=A
Success! Data written to: cubbyhole/mysecret

# トークンAからはデータが見れる
$ VAULT_TOKEN=$TOKEN_A vault read cubbyhole/mysecret
Key     Value
---     -----
whoami  A

# トークンBではデータは見えない
$ VAULT_TOKEN=$TOKEN_B vault read cubbyhole/mysecret
No value found at cubbyhole/mysecret

# トークンBで同じパスで書き込む
$ VAULT_TOKEN=$TOKEN_B vault write cubbyhole/mysecret whoami=B
Success! Data written to: cubbyhole/mysecret

# トークンAの同一パスはそのまま
$ VAULT_TOKEN=$TOKEN_A vault read cubbyhole/mysecret
Key     Value
---     -----
whoami  A

この仕組みとトークンのTTL、使用回数制限を利用することで、リスクを最小限にサーバーやコンテナ等にトークンを渡すことができます。

後述のVault 0.6で追加されたResponse Wrappingを使えば下記は不要ですが、仕組みの理解のために図にしてみました。

  • 永続トークン: 対象に渡したいトークン
  • 一時トークン: 永続トークンを取得するための一時的なトークン
    • 使用回数(2回)と短いTTLを設定したトークン

一時トークンは2回の使用制限を設定し下記のよう使います。

  1. デプロイを行うエンティティ(管理者など)が永続トークンを生成します
  2. 加えて使用制限を2回に設定した一時トークンを生成します
  3. 一時トークンを使ってcubbyhole永続トークンを書きます (使用1回目)
  4. デプロイ対象に一時トークンを渡します
  5. デプロイ対象は一時トークンを使ってcubbyholeから永続トークンを取得します (使用2回目)
    • この時点でこの一時トークンは無効になるので漏洩しても問題ない

トークン受け渡しのチャネルがサーバー生成時のuser_dataや環境変数など、セキュアではないチャネルであっても一時トークンを利用することで、永続トークンの漏洩リスクを最低限に抑えることができます。対象が永続トークンを取得される前に漏洩した一時トークンが使われる可能性もゼロではありませんが、対象が取得エラーになるため気づくことができます。また、監査ログを検査することでも検出が可能です。

Cubbyholeとトークンの受け渡しの詳細に関してはVault公式blogのVault: Cubbyhole Authentication Principlesという記事が詳しいです。

Reponse Wrapping

Response WrappingはVault 0.6で導入された機能で、Cubbyholeをより簡単に利用できる機能です。本来のレスポンスをラップし、実際のレスポンスの代わりにそのレスポンスにアクセスするための一回限り使えるトークン(ラッピングトークン)を発行してくれます。sys/エンドポイントを除く全てのリソースで利用可能です。

トークン発行の場合は下記のように-wrap-ttlを指定してリクエストをします。

$ vault token-create -policy=default -wrap-ttl=60s
Key                             Value
---                             -----
wrapping_token:                 d2fcb185-94f8-8402-b40f-4663df34b330 # ラッピングトークン
wrapping_token_ttl:             1m0s
wrapping_token_creation_time:   2016-10-02 14:29:12.421102311 +0000 UTC
wrapped_accessor:               a7bdc8ae-fc3d-2971-9870-e3da1601c924

ここで返ってくるラッピングトークンを使って、本来のトークンを取得します(unwrap)。レスポンスはラッピングトークンのスコープ内のcubbyhole/responseというパスに書かれています。unwrapにはトークンは必要ありません。

$ unset VAULT_TOKEN # トークンが不要なことを確認するために消しておく
$ vault unwrap d2fcb185-94f8-8402-b40f-4663df34b330
Key             Value
---             -----
token           20cb9352-8cfb-b130-0a11-5dad88ceaa02 # 本来渡したいトークン
token_accessor  a7bdc8ae-fc3d-2971-9870-e3da1601c924
token_duration  720h0m0s
token_renewable true
token_policies  [default]

ラッピングトークンを2回使おうとすると下記のようにエラーになります。

$ vault unwrap d2fcb185-94f8-8402-b40f-4663df34b330
error reading cubbyhole/response: Error making API request.

URL: GET http://127.0.0.1:8200/v1/cubbyhole/response
Code: 403. Errors:

* permission denied

前述のトークンの受け渡しにResponse Wrappingを使うと下記のようになります。一回のリクエストでラッピングトークンを取得できるので非常に簡単になりました。また、永続トークンを管理者が取得する必要がなくよりセキュアな仕組みになっています。

Wrapping Responseはトークン生成以外にも使える汎用的な仕組みです。例えばsecret/mysecretというパスの情報のラップレスポンスが欲しい場合以下のように通常のreadに-wrap-ttlを指定します(HTTPの場合X-Vault-Wrap-TTLヘッダ)。

$ vault read -wrap-ttl=60s secret/mysecret
Key                             Value
---                             -----
wrapping_token:                 c42b4bca-fc6f-95f0-a352-bf6b5e639a9c
wrapping_token_ttl:             1m0s
wrapping_token_creation_time:   2016-10-02 14:35:50.899576812 +0000 UTC

下記のようにunwrapでsecretのレスポンスを取得することができます。

$ vault unwrap c42b4bca-fc6f-95f0-a352-bf6b5e639a9c
Key                     Value
---                     -----
refresh_interval        720h0m0s
hello                   world

なお、レスポンスはcubbyholeに書かれているため、wrap/unwrap間に元データの更新があっても、wrap時点でのデータが取得されます。

参考