シークレット管理サービスのVaultを使ってみた

# 1. はじめに
普段terraformでInfrastructure as codeを実現しています。
ソースコードに触れる機会が増えてから、シークレット情報の取り扱いをより気にするようになってきました。
例えば、シークレット情報はハードコーディングしないという鉄則があるかと思います。
この鉄則に則った時、terraformから実行する際の認証情報をコーディングせずに管理することが難しく感じていました。
そんな中、HashiCorp社のVaultというシークレット管理サービスを見つけました。
特に動的シークレットがとても気になったので具体的に何をしてくれるのか触ってみました。

# 2. vaultの機能

![vault_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329544/919ab2e9-7b9f-5469-7518-55276283a915.png)

**Vault**とはHashiCorp社が開発しているシークレット管理ツールです。
シークレットとは、DBなどのパスワードやAPIキー、証明書、暗号鍵などのことを指します。
Vaultはクライアントサーバーシステム形式で動作します。
主な機能としては以下が挙げられます。

- データ保存前に暗号化してストレージに保存する機能
- AWSやAzureなど一部のシステムに対して動的にシークレットを生成する機能
- シークレットの更新機能
- シークレットの無効化機能
- アクセスポリシーによる権限操作機能

今回はこの中でも動的シークレット生成機能を試していきます。
Azureを使用して検証します。

# 3. Azure事前準備

Azureで動的シークレットは、サービスプリンシパル自動生成(*)とロール割り当ての自動化が行われます。
そのため以下のロールが必要になります。

- Subscriptionの「所有者」
- Azure ADの「アプリケーションの読み取りと書き込み」

私は今回上記の権限をつけたサービスプリンシパルを作成しました。
今回は検証のためAAD(Azure Active Directory)の権限は強いものをつけています。

<img width="1425" alt="subscription_role.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329544/2f4e4356-a961-b2d2-e0ca-fd7bd953c4ac.png">
vault-testというサービスプリンシパルにサブスクリプションの所有者権限を付与

<img width="1434" alt="global_admin.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329544/32bce658-223f-eb3f-bd0e-87a6edf6e9f4.png">
vault-testというサービスプリンシパルにグローバル管理者を付与

後ほど以下の項目を使用するため、環境変数にいれておきます。
ちなみにAzure内のVMでVaultを起動するとclient_idとcilent_secretは不要になります。

- subscription id
- tenant id
- client id
- client secret

~~~bash
export AZURE_SUBSCRIPTION_ID="<subscription_id>"
export AZURE_TENANT_ID="<tenant_id>"
export AZURE_CLIENT_ID="<client_id>"
export AZURE_CLIENT_SECRET="<client_secret>"
~~~

(*)既存のサービスプリンシパルのobject_idを指定すると、既存のサービスプリンシパルを使用するよう設定可能ですが、新規作成が推奨されるため、今回は新規作成の方針を採用しています。


# 4. vaultの環境構築

## 4.1. vaultのインストール

まずはvaultのインストールです。
以下のどちらかから実施します。
私はmac OS のローカル端末でテストするため、homebrewパターンで実施しました。

**OS X homebrew パターン**

~~~bash
$ brew install vault
~~~

以下サイトから使用端末の環境に合わせダウンロードします。
その後ダウンロードしたファイルを実行します。
https://www.vaultproject.io/downloads

ubuntu OS で実行する場合は以下のコマンドでvaultのインストールが可能です。

~~~bash
# zip fileの取得
wget https://releases.hashicorp.com/vault/1.4.2/vault_1.4.2_linux_amd64.zip

# unzip install
sudo apt install unzip

# unzip
unzip vault_1.4.2_linux_amd64.zip

# mv
sudo mv vault /usr/bin
~~~

インストール完了後以下コマンドを入力し、インストールされていることを確認します。

~~~bash
$ vault

# 出力結果
Usage: vault <command> [args]

Common commands:
    read        Read data and retrieves secrets
    write       Write data, configuration, and secrets
    delete      Delete secrets and configuration
    list        List data or secrets
    login       Authenticate locally
    agent       Start a Vault agent
    server      Start a Vault server
    status      Print seal and HA status
    unwrap      Unwrap a wrapped secret

Other commands:
    audit          Interact with audit devices
    auth           Interact with auth methods
    debug          Runs the debug command
    kv             Interact with Vault`s Key-Value storage
    lease          Interact with leases
    namespace      Interact with namespaces
    operator       Perform operator-specific tasks
    path-help      Retrieve API help for paths
    plugin         Interact with Vault plugins and catalog
    policy         Interact with policies
    print          Prints runtime configurations
    secrets        Interact with secrets engines
    ssh            Initiate an SSH session
    token          Interact with token
~~~

チュートリアルを見ていたら、コマンドライン補完ツールのインストールもおすすめしていたので、実行しておきます。(コマンド実行後アウトプット値はありません。)

~~~bash
$ vault -autocomplete-install
~~~

## 4.2. vaultサーバの起動と環境変数設定

vaultサーバを以下コマンドで起動します。
今回はローカル環境にて検証するため開発モードで起動します。
開発モードで起動すると、TLS接続をしないため、通信が暗号化されません。(HTTP接続)

~~~bash
$ vault server -dev

# 出力結果
==> Vault server configuration:

             Api Address: http://127.0.0.1:8200
                     Cgo: disabled
         Cluster Address: https://127.0.0.1:8201
              Listener 1: tcp (addr: "127.0.0.1:8200", cluster address: "127.0.0.1:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
               Log Level: info
                   Mlock: supported: false, enabled: false
           Recovery Mode: false
                 Storage: inmem
                 Version: Vault v1.4.2
             Version Sha: 18f1c494be8b06788c2fdda1a4296eb3c4b174ce
・・・
You may need to set the following environment variable:

    $ export VAULT_ADDR='http://127.0.0.1:8200'

The unseal key and root token are displayed below in case you want to
seal/unseal the Vault or re-authenticate.

Unseal Key: uUdiPMxGuSxizoDtwDBUfgEgiP8T76p9alQPMKZMPYg=
Root Token: s.3uMfZKQLaBDwCsVjBbRBhhTH
・・・
~~~

サーバ起動時に環境変数の追加をしてくださいと言われているので、環境変数を追加します。
ついでにアクセスするためのトークンも一緒に環境変数にいれておきます(サーバー起動時に出力されたRoot Tokenの値)。

新しいターミナルを開き、以下コマンドを入力します。

~~~bash
$ export VAULT_ADDR='http://127.0.0.1:8200'

$ export VAULT_DEV_ROOT_TOKEN_ID="s.GIDKI5wPSt1NejCiDauwZRls"
~~~

念のため、以下コマンドでサーバーが稼働していることを確認します。

~~~bash
$ vault status

Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.4.2
Cluster Name    vault-cluster-344850dc
Cluster ID      f196f039-a6cd-7fc3-b025-8401b087d15b
HA Enabled      false
~~~


## 4.3. azure用シークレットエンジン設定

サーバが稼働していることを確認できたら、以下コマンドにてログインします。

~~~ bash
$ vault login
~~~

Azure のシークレットエンジンを有効化します。

~~~bash
$ vault secrets enable azure
# 出力結果
Success! Enabled the azure secrets engine at: azure/
~~~

アカウントの認証情報を構成します。

~~~bash
$ vault write azure/config \
subscription_id=$AZURE_SUBSCRIPTION_ID \
tenant_id=$AZURE_TENANT_ID \
client_id=$AZURE_CLIENT_ID \
client_secret=$AZURE_CLIENT_SECRET

# 出力結果
Success! Data written to: azure/config

# 以下コマンドで入力値の確認が可能
$ vault read azure/config
~~~

次にロールを構成します。
失効されるまでの期限(TTL)と作成されるサービスプリンシパルのロールとスコープを定義しています。
今回はサービスプリンシパルを"vault-test"というリソースグループに対して共同作成者権限をつけてみます。

~~~bash
$ vault write azure/roles/test-role ttl=1h azure_roles=-<<EOF
    [
        {
            "role_name": "Contributor",
            "scope":  "/subscriptions/<subscription_id>/resourceGroups/vault-test"
        }
    ]
EOF

# 出力結果
Success! Data written to: azure/roles/test-role
~~~

以下コマンドを実行し、サービスプリンシパルを発行します。

~~~bash
$ vault read azure/creds/test-role

# 出力結果
Key                Value
---                -----
lease_id           azure/creds/test-role/Dnld7c3osL1OWHeGGsdI43gg
lease_duration     1h
lease_renewable    true
client_id          df537b41-9ad3-4c72-931b-02c0e115746c
client_secret      88ee84e1-0e64-3197-e910-e641adcd5672
~~~

Azure Portalを見てみると以下のようにサービスプリンシパルの作成と権限付与が行われていました。

<img width="1425" alt="create_service_principal.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329544/19ce83bd-28d4-2888-6129-c7a9fdefc4f2.png">
サービスプリンシパルの発行

<img width="1430" alt="role_service_principal.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329544/36c350f2-8f71-57dc-8585-39b423261f1d.png">
権限付与


azure cli経由でのログインも確認できました。
一時間後確認すると、サービスプリンシパルは削除されており、azure cli経由でログインができないことまで確認できました。
無事動的シークレットができることがわかりました。

# 5. まとめ

vaultの概要、インストール方法、動的シークレットの検証内容をまとめました。
動的シークレットはクラウドサービスを使っていくうえでは、とてもありがたい機能だと思いました。
ただ、実際にvaultを運用に使用すると考えると、サーバ構成や権限等検討が必要そうな内容がたくさんあります。
コストパフォーマンスを見極めた上で導入を検討する必要がありそうだと感じました。

その他の基本的な操作はチュートリアルがおすすめなので、基本的な機能を試す際は以下を参照してください。
https://learn.hashicorp.com/vault/getting-started/install


# 参考資料

- vault チュートリアルサイト<br>
https://learn.hashicorp.com/vault/getting-started/install
- vault ダウンロードサイト<br>
https://www.vaultproject.io/downloads
- Azure Secrets Engine<br>
https://www.vaultproject.io/docs/secrets/azure