Hashicorp VaultのCubbyholeバックエンドとResponse Wrappingに関するメモです。トークンの受け渡し時の漏洩リスクを最小限にする方法を提供してくれる機能です。
Cubbyhole
CubbyholeとはVaultのSecretバックエンドの一つです。generic
バックエンドと同じくKey Value形式でデータを書くことができます。特徴はトークンごとにパスが別のスコープになっており、あるトークンで書いたデータはそのトークンからしか見ることができないことです。
# トークン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回の使用制限を設定し下記のよう使います。
- デプロイを行うエンティティ(管理者など)が永続トークンを生成します
- 加えて使用制限を2回に設定した一時トークンを生成します
-
一時トークンを使って
cubbyhole
に永続トークンを書きます (使用1回目) - デプロイ対象に一時トークンを渡します
- デプロイ対象は一時トークンを使って
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時点でのデータが取得されます。