VaultでAppRoleを使ったmachine-friendlyな認証

[Vault](https://www.vaultproject.io/)にはsecretにアクセスするための認証方式が[複数用意](https://www.vaultproject.io/docs/auth/index.html)されています。そのうち、アプリケーションやサーバーへの組み込み用途に[AppRole](https://www.vaultproject.io/docs/auth/approle.html)という認証方式が実装されています。

この記事では、AppRoleの認証を使って、Vaultに保存したsecretを読み出すまでの一連の流れについて紹介します。

AppRole認証の概要
-----------------------------

![image.png](https://qiita-image-store.s3.amazonaws.com/0/12187/7dba3d65-336a-d74a-fa64-ace7c7b0e8c9.png)
画像元: [Authenticating Applications with HashiCorp Vault AppRole](https://www.hashicorp.com/blog/authenticating-applications-with-vault-approle)

AppRole認証の概要について、HashiCorpの[チュートリアル](https://www.hashicorp.com/blog/authenticating-applications-with-vault-approle)を元に説明します。この認証は、RoleIDとSecretIDによって構成されています。

* RoleID: secretへのアクセス権限(ポリシー)をまとめたロールのID。事前に定義する
* SecretID: RoleIDで認証するためのパスワード。利用の都度払い出す

上の図をシーケンス図で書いてみると、下図のようになるでしょうか。大まかな流れとしては、

1. 事前にアクセス権限を定義したRoleIDを作っておき、
2. CIなどでSecretIDを生成してsecretにアクセスする


という形になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/12187/87c835d8-e9f6-79bf-96c5-df327b0fc7c9.png)


CLIで試してみる
---------------

それでは、[チュートリアル](https://www.hashicorp.com/blog/authenticating-applications-with-vault-approle)に沿って実際にAppRole認証をVault CLIを使って試してみます。

### 事前準備

事前に、VaultでAppRole認証を有効にします。

```sh
$ vault auth enable approle
Success! Enabled approle auth method at: approle/
```

### ロールを定義する

まず、適当なポリシー(`hello-world.hcl`)を作成し、それを割り当てた`hello-world`というロールを発行します。

```sh
$ cat  hello-world.hcl

path "secret/hello-world" {
 capabilities = ["read", "list"]
}
```

```sh
#ポリシーの作成
$ vault policy write hello-world hello-world.hcl
```

```sh
#ロールの作成
$ vault write auth/approle/role/hello-world  \
    secret_id_ttl=120m  \
    token_ttl=60m  \
    token_max_tll=120m  \
    policies="hello-world"
```

作成したロールには、前述したRoleID(`role_id`)が付与されます。

```sh
$  vault read auth/approle/role/hello-world/role-id
Key        Value
---        -----
role_id    69050b5d-347e-e94f-5d10-2b1a3a550111
```

実運用では、ロールの作成までを事前に行っておき、CIではSecretIDだけ払い出すようにするとよいでしょう。

### SecretIDの払い出し

認証に必要なSecretIDは以下のコマンドによって生成できます。

```sh
$ vault write -f auth/approle/role/hello-world/secret-id
Key                   Value
---                   -----
secret_id             2001d4ac-95d6-e473-e34d-e470ff70cd41
secret_id_accessor    7f0f6a44-7485-179a-ab45-88ec8f1ca639dbb43618d95
```

### RoleIDとSecretIDで認証する

先の手順で得られたRoleID/SecretIDで認証が通ることを確認します。

```sh
$ vault write auth/approle/login role_id=${ROLE_ID} secret_id=${SECRET_ID}
Key                     Value
---                     -----
token                   s.6lN6yzXjW3srMYqiXs8zyFW0
token_accessor          5ATZpJ4GMbLoLEdXdUAZ4KNC
token_duration          1h
token_renewable         true
token_policies          ["default" "hello-world"]
identity_policies       []
policies                ["default" "hello-world"]
token_meta_role_name    hello-world
```

CIの場合は、生成したSecretID(`secret_id`)を、認証が必要なセットアップ対象に渡す必要があります。SecretIDを直接送りたくない場合は、[Response Wrapping](https://qiita.com/tkusumi/items/04a99d21dea0a0365239#reponse-wrapping)を使用すると、SecretIDを通信経路に直接露出しない形での受け渡しが可能です。


```sh
# -wrap-ttl=${TTL}を指定するとwrapping_tokenでラップされたレスポンスが返る
# CIでは、SecretIDの代わりにwrapping_tokenをセットアップ対象に送る
$ vault write --wrap-ttl=1440s -f auth/approle/role/hello-world/secret-id
Key                              Value
---                              -----
wrapping_token:                  s.4iHbPgQKO9I7bPekP4cMgKlm
wrapping_accessor:               7aiuYHBf4m78Vk1fQMTj3kOY
wrapping_token_ttl:              24m
wrapping_token_creation_time:    2018-12-10 10:58:13.59285441 +0000 UTC
wrapping_token_creation_path:    auth/approle/role/hello-world/secret-id

# セットアップ先でwrapping_tokenを使ってレスポンスを復号し、SecretIDを得る
$ vault unwrap s.4iHbPgQKO9I7bPekP4cMgKlm
Key                   Value
---                   -----
secret_id             52c631bc-f70a-6d56-3fa5-3826639ee6fa
secret_id_accessor    0714e06e-cf01-5dc2-35a1-749b9ddbb354
```

まとめ
-------

AppRole認証を利用することで、予め定義しておいたロールで認証トークンの払い出しをほしいタイミングで都度行えます。また、ロールの作成[オプション](https://www.vaultproject.io/api/auth/approle/index.html#parameters)でip制限の仕組みやトークンの最大利用回数を設定することで、より強固な認証を組むこともできます。これまで説明したとおり、AppRoleは外部システムに依存せずにVaultのみで認証を完結できるので、小回りの効くシークレット管理の仕組みを作る場合に有用なのではないかと思います。

今回はAppRole認証の紹介でしたが、[プラグイン](https://learn.hashicorp.com/vault/secrets-management/sm-pki-engine#cli-command-2)を開発すればVaultに独自の認証を組み込むことも可能です。例えば、AppRoleの拡張として[インスタンスメタデータ](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)のようなインスタンス情報と連携して、認証時にマシンを識別する、というようなことができます。プラグインの具体的な開発方法については、また機会があれば紹介したいと思います。

参考資料
----------

* [Authenticating Applications with HashiCorp Vault AppRole](https://www.hashicorp.com/blog/authenticating-applications-with-vault-approle)
* [煩雑化するセキュリティ対策におけるクレデンシャル管理、あふれる課題をVaultで解決！【デブサミ2018 夏】 \(2/2\)：CodeZine（コードジン）](https://codezine.jp/article/detail/11032?p=2) 
* [VaultのAppRoleを使ってトークン取得 \- Carpe Diem](https://christina04.hatenablog.com/entry/vault-app-role)
* [Vault: CubbyholeとResponse Wrapping \- Qiita](https://qiita.com/tkusumi/items/04a99d21dea0a0365239)