hashicorpのvaultでldap認証でMySQLアカウントを期限つき作成する

なんか監査的なアレでMySQLの共通アカウントを卒業したい的な需要があるようだったのでkikumotoさんのスライドと公式マニュアルをChromeに翻訳してもらいながらガン見しつついつもどおりググりまくりつつやってみましたので備忘録しておきます。

hashicorpvaultの詳しい話はほかのサイトを見たらいいと思いますがザックリいうと暗号化データを管理してくれる仕組みです。
ansibleのvaultやgoogleのvaultとは違うやつです。

####参考
http://kikumoto.hatenablog.com/entry/2016/07/30/112618
http://qiita.com/chroju/items/5f982bafecd3a1c36a9b
http://qiita.com/yunano/items/bfaac7868ce4fb946ee5
https://github.com/hachiojipm/yapcasia-8oji-2016mid-timetable/issues/26
http://kiririmode.hatenablog.jp/entry/20150429/1430279218
https://www.vaultproject.io/docs/secrets/index.html
https://www.vaultproject.io/docs/secrets/databases/index.html
[RDSまわりのアカウント権限のマニュアル](http://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.MasterAccounts.html)

####今回試した構成

- ストレージバックエンド：ローカルストレージ([filesystem](https://www.vaultproject.io/docs/configuration/storage/filesystem.html))、HAなし
  （HAサポートしてるのは、[Consul](https://www.vaultproject.io/docs/configuration/storage/consul.html)、[DynamoDB](https://www.vaultproject.io/docs/configuration/storage/dynamodb.html)、[Etcd](https://www.vaultproject.io/docs/configuration/storage/etcd.html)、[Zookeeper](https://www.vaultproject.io/docs/configuration/storage/zookeeper.html)）
- 認証バックエンド：[ldap](https://www.vaultproject.io/docs/auth/ldap.html)（今回はテストのためだけのシングル構成でデータもグループ1個用意しただけ）
- SecretBackend：[MySQL](https://www.vaultproject.io/docs/secrets/databases/mysql-maria.html)（RDS）。ldapのグループに基づいたpolicyからMySQLアカウントに権限を割り当てられる

というかんじ。ちなみにhttpで待ち受けてるのでHA仕込んだらスレーブはマスタに更新をリダイレクトする挙動らしい。

####vaultのinstall

hashicorp製品はGoで作られてるのでアーキテクチャに合ったバイナリをダウンロードしてきてpathを通すと使える。
/usr/local/sbinあたりに置く。

```
$ wget https://releases.hashicorp.com/vault/0.8.2/vault_0.8.2_linux_amd64.zip
$ file vault_0.8.2_linux_amd64.zip 
$ unzip vault_0.8.2_linux_amd64.zip 
$ sudo ln -s /usr/local/src/vault /usr/local/sbin/vault
$ sudo which vault
$ file vault
$ vault -help
```

####vaultのストレージバックエンドの設定と起動

centos6だったので[こちらの参考サイト](http://qiita.com/yunano/items/bfaac7868ce4fb946ee5)からたどって[落ちてた起動スクリプト](https://gist.github.com/yunano/c4e641bad3fb9f78d6cf)を保存してつかわせていただきました。

```
# vi /etc/init.d/vault
# chmod +x /etc/init.d/vault
# chkconfig --add vault
# chkconfig --list vault
```

起動スクリプトに定義されてるpathに設定ファイルを定義して起動

```
# vi /etc/vault.d/vault.hcl
storage "file" {
  path = "/root/vault/data"
}

listener "tcp" {
  address = "127.0.0.1:8200"
  tls_disable = 1
}
# service vault start
```

filesystemならEBSなどディスク分離してマウントしたとこがいいのかもしれない

※クイックスタートの`vault server -dev`だとメモリにしか保存されない


####vaultの環境変数の定義

`export VAULT_ADDR='http://127.0.0.1:8200'`
これ↑をやらないとvaultコマンド打ってもエラーになる。

####vaultのunseal

```
# vault init
と打つとunsealkeyとrootのトークンなどが表示されて後で使うので必ず記録する
# vault unseal
これを3回打って3つの鍵を持ち寄ってシールド解除する
# vault status
Sealed: false　#シールド解除された状態
```

####rootのトークンで初回のauth

```
# vault auth 359ac453-53e5-b289-4dc9-5d9838773***
```

####ldapのデータのグループの仕込みなど

オブジェクトクラスposixGroupにしてたのでmemberUidを定義。
https://fumiyas.github.io/2015/12/12/group-access.openldap-advent-calendar.html

```
# ldapsearch -x -D "cn=Manager,dc=hoge,dc=local" -W -b "cn=test01,ou=Group,dc=hoge,dc=local"
dn: cn=test01,ou=Group,dc=hoge,dc=local
objectClass: posixGroup
objectClass: top
cn: system
cn: test01
gidNumber: 1000

#　vi add_group_20170914.ldif
dn: cn=test01,ou=Group,dc=hoge,dc=local
changetype: modify
add: memberUid
memberUid: test02
memberUid: test

# ldapmodify -x -D "cn=Manager,dc=hoge,dc=local" -w `cat /root/.ldap_pass` -f add_group_20170914.ldif
modifying entry "cn=test01,ou=Group,dc=hoge,dc=local"

# ldapsearch -x -D "cn=Manager,dc=hoge,dc=local" -W -b "cn=test01,ou=Group,dc=hoge,dc=local"
dn: cn=test01,ou=Group,dc=hoge,dc=local
objectClass: posixGroup
objectClass: top
cn: system
cn: test01
gidNumber: 1000
memberUid: test02
memberUid: test
```

####mysql（RDS）側のGrantOptionつきユーザの作成

作成できるアカウント以上の権限を必要とする模様。

```
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER ON *.* TO 'vault'@'%' IDENTIFIED BY 'xxxxxxxx' WITH GRANT OPTION;
```

RDSはALLはつかえなくて制限されており以下のリンク先に可能な権限がかいてありました。
http://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.MasterAccounts.html

####使用するAuth Backendを有効化、ldapを有効にする・接続設定

```
# vault auth-enable ldap
Successfully enabled 'ldap' at 'ldap'!
```

（ldapsは実際やるなら必須だけどもテストなので389で失礼。）

```
# vault write auth/ldap/config binddn="cn=Manager,dc=hoge,dc=local" bindpass="********" url="ldap://ldap01" userattr=uid userdn="ou=People,dc=hoge,dc=local" groupdn="ou=Group,dc=hoge,dc=local" upndomain="HOGE.LOCAL" insecure_tls=true starttls=false discoverdn=true
Success! Data written to: auth/ldap/config

# vault read auth/ldap/config
Key             Value
---             -----
binddn          cn=Manager,dc=hoge,dc=local
bindpass        ********
certificate    
deny_null_bind  true
discoverdn      true
groupattr       cn
groupdn         ou=Group,dc=hoge,dc=local
groupfilter     (|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))
insecure_tls    true
starttls        false
tls_max_version tls12
tls_min_version tls12
upndomain       HOGE.LOCAL
url             ldap://ldap01
userattr        uid
userdn          ou=People,dc=hoge,dc=local

The following warnings were returned from the Vault server:
* Read access to this endpoint should be controlled via ACLs as it will return the configuration information as-is, including any passwords.
```


####データベースのSecretBackendのマウント

```
# vault mount database
Successfully mounted 'database' at 'database'!
# vault mounts
Path        Type       Accessor            Plugin  Default TTL  Max TTL  Force No Cache  Replication Behavior  Description
cubbyhole/  cubbyhole  cubbyhole_e74259bf  n/a     n/a          n/a      false           local                 per-token private secret storage
database/   database   database_7c483728   n/a     system       system   false           replicated            
secret/     generic    generic_5203937e    n/a     system       system   false           replicated            generic secret storage
sys/        system     system_81574fb0     n/a     n/a          n/a      false           replicated            system endpoints used for control, policy and debugging
```

####MySQLのSecretBackendを実装
#####DBへの接続設定とroleの登録とポリシーの登録

さっき作ったアカウントの接続情報を設定する。role名がアカウントに含まれて長くなるので短めに指定。

```
# vault write database/config/mysql \
> plugin_name=mysql-rds-database-plugin \
> connection_url="vault:xxxxxxxx@tcp(vault-rds01:3306)/" \
> allowed_roles="read, mngr"

# vault read database/config/mysql
Key                     Value
---                     -----
allowed_roles           [read mngr]
connection_details      map[connection_url:vault:xxxxxxxx@tcp(vault-rds01:3306)/]
plugin_name             mysql-rds-database-plugin
```

※plugin_nameの指定は専用のやつでないとRDSならmysql-rds-database-plugin指定しないとDBアカウント名の字数制限のための刈込を勝手にやってくれる挙動にならなさそうであった。
※接続先増加したときの挙動は確かめてみないとフメイ

####ldapグループとvaultPolicyを紐づける設定

読み込み専用のroleを定義 

```
# vault write database/roles/read \
> db_name=mysql \
> creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT SELECT ON *.* TO '{{name}}'@'%';" \
> default_ttl="1h" \
> max_ttl="24h"
Success! Data written to: database/roles/read

```

ホストの定義は固定で`@'%'`以外にはできない模様です。

それ以外のパワーユーザーのroleを定義

```
# vault write database/roles/mngr \
> db_name=mysql \
> creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER ON *.* TO '{{name}}'@'%';" \
> default_ttl="1h" \
> max_ttl="24h"
Success! Data written to: database/roles/mngr
```

定義されたroleを確認

```
# vault read database/roles/read
Key                     Value
---                     -----
creation_statements     CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT SELECT ON *.* TO '{{name}}'@'%';
db_name                 mysql
default_ttl             3600
max_ttl                 86400
renew_statements     
revocation_statements
rollback_statements  

# vault read database/roles/mngr
Key                     Value
---                     -----
creation_statements     CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER ON *.* TO '{{name}}'@'%';
db_name                 mysql
default_ttl             3600
max_ttl                 86400
renew_statements     
revocation_statements
rollback_statements  
```

ポリシーの登録
https://www.vaultproject.io/docs/concepts/policies.html

defaultポリシーをみてみる

```
# vault read sys/policy/default
```

ポリシーのリストをみてみる

```
# vault read sys/policy
Key             Value
---             -----
keys            [default root]
policies        [default root]
```

ポリシーを定義して登録

```
# vi db-read.hcl
---
path "database/creds/read" {
  capabilities = ["read"]
}
---

# vi db-mngr.hcl
---
path "database/creds/mngr" {
  capabilities = ["read"]
}
---

# vault policy-write dbread db-read.hcl
# vault policy-write dbmngr db-mngr.hcl

# vault read sys/policy
Key             Value
---             -----
keys            [dbmngr default dbread root]
policies        [dbmngr default dbread root]

```


####vaultからldap認証してmysqlアカウント発行

```
# vault auth -method=ldap username=test02
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: 7f0702b4-5b54-c819-f1d2-80baedb1****
token_duration: 2764800
token_policies: [db-poweruser default]
```

####アカウントデータ取得

一回rootのトークンで認証しなおしてからアカウントデータを取得する

```
# vault auth 359ac453-53e5-b289-4dc9-5d9838773***
# vault read database/creds/mngr
Key             Value
---             -----
lease_id        database/creds/mngr/37e0f265-f0d9-1731-c4f4-2eb723cd7xxx
lease_duration  1h0m0s
lease_renewable true
password        A1a-2907p9y23xu7qxxx
username        v-mngr-v7s8s4ww6
```

`vault read`した時点でアカウントが作成される仕様。

####ログイン、期限切れとrenew

取得できたid/passでDBに接続する

```
$ mysql -u v-mngr-v7s8s4ww6 -p -h vault-rds01
```

lease_durationを過ぎるとアカウントは容赦なく削除される
期限が切れる前ならrenewで更新
期限が切れたらもう一回authとreadして再発行、となる模様（同じ名前にはならない）

```
# vault renew database/creds/mngr/85732db5-d8b6-d267-3768-a51e58a8fe4d 30
```
※renewのあとにreadで出てきたlease_idと伸ばしたい秒数を指定する。
　期限変えたい場合はroleの設定しなおすときに一緒にdefault_ttlとmax_ttlを指定する。　

とりあえず以上です。
（複数DBの場合のDBアカウントの共有方法の確認とストレージバックエンドのHAなどの残課題有）