AWSでIAMユーザを使い捨てにする

## はじめに
私が所属する[aslead DevOps](https://aslead.nri.co.jp/solution/aslead_devops.html)チームでは、日々変化するユーザの開発サーバ構成に対して、セキュリティを保ちつつ開発業務の効率化・自動化ができないかを検討しています。

この記事では、AWSにログインして作業するIAMユーザの扱い方についてご紹介します。AWSの中でもセキュリティの基礎となる重要サービスであり、ユーザの棚卸しや権限の管理に時間を割かれているチームも多いのではないでしょうか。
そこで、Hashicorp社が提供しているVaultを利用し、作業のタイミングでIAMユーザを作成し、終わったら削除するアプローチを考えてみます。IAMユーザの作成自体はVaultの標準機能ですが、複数アカウントの権限制御ができるスイッチロールとの組み合わせを提案します。
 
## Vaultとは


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/acb29790-db2d-5752-73e1-0bfa11fc2d36.png)
(画像は　https://medium.com/hashicorp-engineering/vault-enterprise%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6-%EF%BC%91-e5579c2fb8d5 より引用)

[公式サイト](https://www.vaultproject.io/)より、Vaultは主に以下のような機能を持っています。

* Secret Management：様々な環境のパスワードのライフサイクルを管理する
* Data Encryption：データの暗号化/複合化を行う（データ自体は保持せず、変換だけ）
* Identity-based Access：同一人物の様々な環境上のアカウントを一つに取りまとめる

本記事ではSecret Managementに分類される機能を取り扱います。

## 想定するIAMユーザ運用
以下のようなAWSの不正利用のリスクがある運用ケースを想定し、解消を試みてみます。

- チームから移動したメンバのIAMユーザは手動で削除している
- 強い権限を持った、アクセスキーが固定の機械ユーザがいる
- アクセスキーの発行操作に特に制限をかけていない


## Vaultによる解決策
上記のリスクに対し、今回は[AWS Secret Engine](https://learn.hashicorp.com/tutorials/vault/getting-started-dynamic-secrets)でIAMユーザを作成し、スイッチロールで作業するアプローチをとってみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/9d918cb7-a866-2d9e-8fb8-f99ee76b40d7.png)


これにより、以下のようなメリットがもたらされます。

* Vault以外に固定のIAMユーザが不要になり、IAMユーザの管理が容易になる
* 利用時のみ機械ユーザを作成することで、アクセスキーの流出リスクを低減できる
* Vault側でLDAPなどと組み合わせれば、アクセスキー(IAMユーザ)の発行を制御できる

以下では具体的にVaultを構築し、発行したIAMユーザでスイッチロールする部分について実装していきます。なお、上記では作業対象のAWSアカウント上にVaultを構築していますが、異なるAWSアカウントに対してもIAMユーザの発行が可能です。

## 実装
###実装する環境
今回は以下の環境で実装を行います。
AWS上でVaultを構築していますが、ローカルでも確認可能な内容になっています。

* Vault：ver1.5.4
* OS：Amazon Linux 2
* IaaS：AWS EC2サービス

###OS部分の構築

本記事の主な解説対象ではないため、割愛します。
[Acrovision社の記事](https://www.acrovision.jp/service/aws/?p=1829)などが参考になるかと思います。

以降は、Vaultが稼働するOSを`vault`というホスト名で作成した前提で進みます。

###Vaultのインストール

2020/07/24より、[Linuxレポジトリからの取得が可能に](https://www.hashicorp.com/blog/announcing-the-hashicorp-linux-repository/)なりましたので、こちらを利用して`vault`に対してインストールしていきます。

```bash
$ sudo yum install -y yum-utils
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
amzn2-core                                                       | 3.7 kB  00:00:00
Package yum-utils-1.1.31-46.amzn2.0.1.noarch already installed and latest version
Nothing to do

$ sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
adding repo from: https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
grabbing file https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo to /etc/yum.repos.d/hashicorp.repo
repo saved to /etc/yum.repos.d/hashicorp.repo

$ sudo yum install -y vault
～中略～
Complete!
```

説明の簡便のため、起動は開発者モードで行います。
`-dev-listen-address`は`vault`のIPアドレスを含めて下さい。EC2の場合は下記のようにメタデータから取得することができます。

```bash
$ vault server -dev -dev-root-token-id="root" -dev-listen-address="$(curl -s http://169.254.169.254/latest/meta-data/local-ipv4):8200"
==> Vault server configuration:

             Api Address: http://172.31.39.141:8200
                     Cgo: disabled
         Cluster Address: https://172.31.39.141:8201
              Go Version: go1.14.7
              Listener 1: tcp (addr: "172.31.39.141:8200", cluster address: "172.31.39.141:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
               Log Level: info
                   Mlock: supported: true, enabled: false
           Recovery Mode: false
                 Storage: inmem
                 Version: Vault v1.5.4
             Version Sha: 1a730771ec70149293efe91e1d283b10d255c6d1
～中略～
==> Vault server started! Log data will stream in below:
～省略～
```

ブラウザから`Api Address`に記載されたアドレス（上記では`http://172.31.39.141:8200`）にアクセスし、下記のようにログイン画面が表示されればインストールと起動は完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/fd9e3a08-37cb-358d-a3b3-ecc5971936ee.png)

### IAMユーザの発行

#### AWS管理コンソールの作業

Vaultが利用するIAMユーザを作成します。ここでは`vault`という名称にしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/42189269-6bd7-ba47-e59a-6eaeed55a7b4.png)

作成の際、以下のようにIAMの操作権限を付与してください。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "iam:*",
            "Resource": "*"
        }
    ]
}
```

IAMユーザが作成されたら、アクセスキーIDとシークレットアクセスキーを控えておきます。

#### Vaultの作業

IAMユーザの発行には[AWS Secret Engine](https://learn.hashicorp.com/tutorials/vault/getting-started-dynamic-secrets)という機能を利用します。
新規で`vault`のターミナルを開き、AWS Secret Engineを有効化します。
`access_key`と`secret_key`には先ほどIAMユーザを作成したときに控えておいた値を入れます。

```bash
$ export VAULT_ADDR="http://$(curl -s http://169.254.169.254/latest/meta-data/local-ipv4):8200"

$ vault secrets enable -path=aws aws
Success! Enabled the aws secrets engine at: aws/

$ vault write aws/config/root \
    access_key=XXXXXXXXXXXXXXX \
    secret_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
    region=ap-northeast-1
```

作成するIAMユーザにはロールという単位で権限を付与します。このロールも作成していきます。
今回はスイッチロールを利用するため、以下のような`only-switch`というロールを作成します。

```bash
$ vault write aws/roles/only-switch \
         credential_type=iam_user \
         policy_document=-<<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": "*"
        }
    ]
}
EOF
Success! Data written to: aws/roles/only-switch
```

ブラウザから`vault`サーバに8200ポートでアクセスし、Tokenに`root`を入力してログインします。ログイン後、`Secrets>aws`にアクセスすると、`only-switch`のロールが表示されているはずです。右の三点リーダから`Generate credentials`を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/e9d57c94-b090-d3db-d962-cc36be0d1d53.png)


`Credential type`は`IAM User`を選択したまま、`Generate`をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/03cb37f8-ab8a-8dd5-ed4d-1308965c01ea.png)

IAMユーザが作成されました。アクセスキーIDとシークレットアクセスキーは画面遷移すると参照できなくなってしまうため控えておきます。
`Lease duration`で表示されているのがこのIAMユーザの有効期間です。本記事では割愛しますが、デフォルトの2,764,800秒(32日間)から60秒まで短くすることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/6cfdddd3-4e7f-3c25-3fd4-3f2fe531c635.png)

AWS管理コンソールでも確認すると、`only-switch`というポリシーが直接アタッチされたIAMユーザが作成されていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/66c925bd-a2e1-0c86-1398-6b3823442750.png)


### スイッチロールでの作業
ここまでの手順で作成したIAMユーザに様々な権限をロールとして付与していくことでAWSの不正利用のリスクは低減できるのですが、既に複数のAWSアカウントでIAMロールを用意して運用されているケースが多いかと思います。

Vaultで作成したIAMユーザでスイッチロールを可能にするすることで、複数アカウントの権限制御に対処します。
[AWSの公式ドキュメント](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_use_switch-role-cli.html)をもとに実装していきます。

#### AWS管理コンソールの作業
例えばIAMの操作を行うロールを作成することを想定し、`IAM-Admin`という名前で以下のようなポリシーを作成します。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "iam:*"
            ],
            "Resource": "*"
        }
    ]
}
```

`IAM-Admin`という名前でロールを作成します。信頼されたエンティティは`別のAWSアカウント`を指定し、アカウントIDにはVaultがIAMユーザを作成するAWSアカウントのIDを入力してください。
ポリシーは先ほど作成し`IAM-Admin`ポリシーをアタッチします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/3ae5885f-a02c-ab96-d42c-01ede80b18d2.png)

IAMロールが作成されたら、ロールARNを控えておきます。

#### AWS CLIの作業
AWS CLIをインストールしていない方は[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-install.html)をもとにインストールして下さい。
`aws configure`で作成したIAMユーザのアクセスキーIDとシークレットアクセスキーを設定します。

```bash
$aws configure
AWS Access Key ID []: XXXXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key []: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
Default region name []:ap-northeast-1
Default output format []:json
```

` .aws/config`ファイルに`iamadmin`というプロファイルを追記し、`IAM-Admin`ロールのARNを設定します。

```
[profile iamidmin]
    role_arn = arn:aws:iam::XXXXXXXXXXXX:role/IAM-Admin
    source_profile = default
```

動作検証として、以下のように`IAM-Admin`の情報が取得できればスイッチロールは成功です。

```bash
$aws iam get-role --role-name IAM-Admin --profile iamadmin
{
    "Role": {
        "Path": "/",
        "RoleName": "IAM-Admin",
~~中略~~        
    }
}
```

`--profile iamadmin`無しで実行するとエラーとなり、作成されたIAMユーザの認証情報だけでは権限がないことが確認できます。

```bash
$aws iam get-role --role-name IAM-Admin
An error occurred (AccessDenied) when calling the GetRole operation: 
User: arn:aws:iam::XXXXXXXXXXXXX:user/vault-token-only-switch-XXXXXXXXXXXXXXX
is not authorized to perform: iam:GetRole on resource: role IAM-Admin
```

## まとめ
本記事で伝えたかったことは以下の2点です。

* Hashicorp VaultでIAMユーザを使い捨てにすることでアクセスキーの流出リスクを低減できる
* スイッチロールと組み合わせることで既存のIAMロール運用から少ない修正で適用することができる