Vault
Hashicorp

HashiCorp Vaultを機密情報データベースとして検証する

More than 1 year has passed since last update.

あまりまだメジャーではないみたいですが、HashiCorpが機密情報管理用のツールとしてVaultを出しています。代替になるツールもあまり思い浮かばないし、物は試しと使ってみました。

Vaultでできること概要

  • 機密情報等をKey,Value形式で書き込むと暗号化して保存してくれる。
  • Secret Backendsという機能で、MySQL、PostgreSQL、AWS、LDAP等と連携し、Vaultを通じてユーザー情報の追加、変更、削除等を行える。このときLease(期限)を設定し、一定期間後にアカウントを自動削除したり、パスワードをRevokeさせることができる。
  • デフォルトの状態ではデータはすべて暗号化(Sealed)されており、Vault自身も復号する方法を知らない。復号には分散鍵による認証が必要になる。
  • Vaultへのアクセス方法はCLIかREST API。
  • Vaultへアクセスする際の認証はユーザーパスワード形式、GitHub連携、一時的なtokenの払い出しなどを扱える。
  • Vaultに対して行われた操作はすべて保存され、監査に対応できる。

今回すること

  • 試験的な運用のため、Secret Backendsはデフォルトのまま、シンプルに情報の保存と読み出しだけを試す。 (Secret BackendsはデフォルトだとGenericというものがマウントされており、これは単純に情報を保存すると、それを暗号化してファイルに書き込むのみ)
  • ユーザーパスワード形式で認証して使用することを想定する。
  • 開発用途で起動できるdevモードが存在しているが、本番相当の設定を検証したいので今回は使わない。

初期設定

インストール

zipを落としてきてunzipし、PATHの通っている場所に置いて完了。

設定

hclもしくはjsonで書いた設定ファイルを元として起動する。設定ファイルは Server Configuration - Vault by HashiCorp に記述方法が書かれているが、最低限2つの項目が必要とされる。

backend

Vaultのデータを保存するSecret Backendの設定。MySQLやPostgresqlといったRDBMS、S3やDynamoDB、プレーンのファイルなど様々選べる。Genericを使う場合はfileを設定することになる。

listener

APIリクエストを受け付けるリスナープロトコルの設定。現状はtcpのみ対応。tlsを使う場合の証明書の設定等もできる。デフォルトではtlsが有効化されているため、使用しない場合は無効化の明示が必要となる。

tls無効化

  • listenerでtls_disable = 1を指定する。
  • vaultへの接続はデフォルトでhttpsが使われるので、VAULT_ADDR環境変数でhttpのアドレスを指定する。
vault.hcl
backend "file" {
  path = "/home/ec2-user/vault"
}

listener "tcp" {
  address = "127.0.0.1:8200"
  tls_disable = 1
}

起動

先のhclファイルを指定して起動する。起動後にvault initで初期化する。

注意点

  • フロントで立ち上がり、ログを標準出力に流してくるため、リダイレクトとバックグラウンドでの起動が必要。
  • vaultはmlockシステムコールを使用するため、管理者権限で実行しなければエラーになる。configの中でmlockの無効化もできるが、非推奨。
  • vault initした際に表示されるUnseal KeyInitial Root Tokenはいずれも必要なので記録する。
$ sudo vault server -config /etc/vault.hcl &>/var/log/vault &
$ vault init
Unseal Key 1 (hex)   : XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Unseal Key 1 (base64): XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
...
Initial Root Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Vault initialized with 5 keys and a key threshold of 3. Please
securely distribute the above keys. When the Vault is re-sealed,
restarted, or stopped, you must provide at least 3 of these keys
to unseal it again.

Vault does not store the master key. Without at least 3 keys,
your Vault will remain permanently sealed.

Unseal

VaultはデフォルトだとSealed(暗号化された状態)となっているため、先程出力されたUnseal Keyを使ってunsealを行う必要がある。まずステータスを確認してみる。

$ vault status
Sealed: true
Key Shares: 5
Key Threshold: 3
Unseal Progress: 0
Version: Vault v0.6.1

Sealed: trueとなっている。Key SharesUnsealのために共有しているキーの数であり、このうちKey Threasholdに表示された閾値の数だけキーを入力するとUnsealされる。

キーは複数人で分散管理し、全員が持ち寄ることで初めて復号化できる、というのがこの機構の目的。技術的には「シャミアの秘密分散法」と呼ばれるものらしい。逆にSealするときはvault sealコマンドを使えば一人で暗号化できるので、キーが漏れてしまった場合などはSealすることで対処できる。

復号化する。vault unsealコマンドを実行し、vault initの際に表示されたキーから3つを順に入れていく。出力のUnseal Progressが徐々に進んでいき、Thresholdに達するとunsealされる。

$ vault unseal
Key (will be hidden):
Sealed: true
Key Shares: 5
Key Threshold: 3
Unseal Progress: 1

Auth

認証機構。デフォルトで使用できるユーザーはrootのみなので、まずはrootで認証する。vault initのときに出力されたrootのトークンを使う。

$ vault auth XXXXXXXXXXXXXXXXXXX
Successfully authenticated! You are now logged in.
token: XXXXXXXXXXXXXXXXXXXX
token_duration: 0

使用するAuth Backendを有効化する。今回はuserpass。有効化後に、ユーザーを作成して認証までしてみる。

$ vault auth-enable userpass
Successfully enabled 'userpass' at 'userpass'!

$ vault write auth/userpass/users/chroju password=******
Success! Data written to: auth/userpass/users/chroju

$ vault auth -method=userpass username=chroju
Password (will be hidden):
Successfully authenticated! You are now logged in.
The token below is already saved in the session. You do not
need to "vault auth" again with the token.
token: XXXXXXXXXXXXXXXXXXXXX
token_duration: 2592000
token_policies: [default]

認証が成功するとtokenが発行されるが、APIを使うときはこのtokenで認証することができる。CLIではこの状態でもう操作が可能。なおtokenはカレントディレクトリの.vault-tokenファイルに書き込まれており、CLI使用時はこれでセッションを維持しているっぽい。

ACL

単にユーザーを作っただけでは、すべてのリソースに対する操作権限はDenyに設定されているため、ユーザーがどのリソースに対してアクセス可能かはACLで定義する。Vaultにおける機密情報の保存場所は、いわゆるファイルパスのようなスラッシュで区切った形式で指定する。従って適切にフォルダを分けて、アクセス権を分散させることができる。

  • ACLはHCLもしくはjsonにより記述する。
  • フォルダ配下へのアクセスは自動で付与されない。例えばpath "secret"に対する許可を書くと、secretにはアクセスできてもsecret/fooにはアクセスできない。
  • ACLを付与しない限り、デフォルトで全アクセスはDenyされる。
  • userpassバックエンドでの認証はauth/token/lookup-selfへのread権限がなければ通らなかった。少し謎。
path "auth/token/lookup-self" {
  policy = "read"
}

path "secret/*" {
  policy = "write"
}
$ vault policy-write allow-secret /etc/vault_acl.hcl
$ vault write auth/userpass/users/chroju password=****** policies=allow-secret

利用

データの読み書き。

$ vault write secret/foo value=bar
Success! Data written to: secret/foo

$ vault read secret/foo

普通に入力するとコマンドラインの履歴に残って気持ち悪いので、標準入力やファイルからの入力を駆使する。

# 標準入力を使う
$ echo -n '{ "username":"foo", "password":"bar" }' | vault write secret/server01
Success! Data written to: secret/server01

# jsonファイルを使う 
$ cat server02.json
{
  "username" : "foo",
  "password" : "bar"
}

$ vault write secret/server02 @server02.json
Success! Data written to: secret/server02

APIからアクセスする。tokenをX-Vault-Tokenヘッダーに入力することで認証する。

$ curl http://localhost:8200/v1/secret/foo -H "X-Vault-Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
{"request_id":"b745ef53-ff32-9ffe-b8b2-a7dc03072f7e","lease_id":"","renewable":false,"lease_duration":2592000,"data":{"value":"bar"},"wrap_info":null,"warnings":null,"auth":null}

tokenはtoken-createコマンドを使って、一時的な払い出しができる。オプション-ttlで時間制約を持たせることができ、-use-limitで使用回数を制限できる。

$ vault token-create -policy=allow-secret -ttl=1h
Key             Value
---             -----
token           bcae0b31-55af-8349-dd9a-f0200fbe6b53
token_accessor  39be2d81-4860-d271-365d-42db4db41f0e
token_duration  1h0m0s
token_renewable true
token_policies  [allow-secret default]

その他留意点など

パスワードの変更

userpass backendを使う場合、当然各ユーザーがパスワードの変更をしたくなるが、これもまたvault writeコマンドによる書き込みになる。パスワード情報はauth/userpass/users/${username}/passwordというパスに保存されているため、このパスに対してACLで許可を与えなくては、パスワード変更はできない。

$ vault write auth/userpass/users/chroju/password username=chroju password=hogefuga
Success! Data written to: auth/userpass/users/chroju/password

しかし、そうなると各ユーザーに個別にACLを設定して、自分のパスワードパスだけに対するアクセス権を与えてやることになるので、ACLの種類がユーザーの数だけ増えて管理が大変になりそう。この点、もう少しいいソリューションはないのかなと思う。

雑感

  • APIアクセスが主であり、また認証(Auth Backends)もuserpassではなくGitHub等の他サービスと連携させることが基本なのかなと思う。先に書いたパスワード変更の件など、userpassでCLIを介して常時使うような設計にはなっていない気がした。
  • とはいえHashiCorpのツールらしく、各機能がシンプルに独立していて、プラガブルに使える点はとてもいい。「こうしなければ使用できない」となってしまうような変な制限がなく、使い方に幅がある。それだけに運用方法は練る必要がある。
  • スクリプト等でパスワードをハードコーディングするバッドプラクティスへの対策として、Vaultからパスワードを引っ張ってきたりできると思うので試したい。が、基本的にtokenにTTLがある状態で、どう設計すればいいのかよくわからない。軽くググってもいまいち事例が出てこないし、出てきたと思ったら唯一TTLの存在しないrootアカウントのtokenを使ってたりしてセキュリティ的によろしくない感じであった。