aws-vault についてのあれこれ

クラウドワークス SREチームの @kangaechu です。アンタッチャブルのコンビ復活に目が離せないこの頃です。
[クラウドワークス Advent Calendar 2019](https://qiita.com/advent-calendar/2019/crowdworks)の4日目として、最近SREチーム内で使われるようになったツール、 [aws-vault](https://github.com/99designs/aws-vault) を紹介します。

## 背景
aws-vaultの話をする前に、少しだけAssumeRoleの話をします。

### AssumeRole

assumeは引き受けるなどの意味を持つ単語で、AWSを使用するユーザやリソースが本来持っている権限とは別の権限を引き受けることができるしくみです。ものすごいざっくりいうと、sudoコマンドのような感じです。AssumeRoleはどのようなときに便利なのでしょうか。

### マルチアカウントでのIAMユーザ集約管理

リソースの分離や課金管理などを目的として、最近はAWS Organizationsを使用したマルチアカウントでの運用をすることが多くなりました。AWSの2019年のReference Network Architectureでも、環境ごとに複数のAWSアカウントを使用する構成を推奨しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36587/f068fb47-d9fb-f243-33c2-ee97e1ff3f87.png)
[Enterprise Network Architectures on AWS](https://www.slideshare.net/AmazonWebServices/enterprise-network-architectures-on-aws)

その際、各AWSアカウントのユーザ管理やログインはどのように行うべきなのでしょうか？
AWSアカウントごとに必要となる人数分のIAMユーザを作成し、アクセスキーを払い出し…のような管理をするのは現実的ではありません。

このような場合の解決策として、AWSへのログインを1つのAWSアカウントに集約し、そこから別のアカウントにAssumeRoleを使用してログインするという方法が考えられます。これにより、管理者は1つのアカウントのみユーザを管理すれば良くなります。ユーザも複数のAWSアカウントのパスワードを覚える必要はありません。
クラウドワークスではcustodian（守衛・番人）と呼ばれるアカウント経由で他のアカウントにログインする方式を取っています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36587/f6772434-2921-fcd7-09c9-f00b6e23d83f.png)
参考: [CustodianアカウントによるIAMユーザの集約管理](https://engineer.crowdworks.jp/entry/2018/07/17/103453#Custodian%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AB%E3%82%88%E3%82%8BIAM%E3%83%A6%E3%83%BC%E3%82%B6%E3%81%AE%E9%9B%86%E7%B4%84%E7%AE%A1%E7%90%86) 

### 最小権限の原則

また、１つのアカウントの中でもAssumeRoleを使用するケースがあります。最小権限の原則による権限の切り替えがそれにあたります。

最小権限の原則をWikipediaで調べてみると…

> 最小権限の原則は、ユーザーアカウントに対して、そのユーザーにとって必要な権限だけを与えることを意味する。例えば、バックアップ用ユーザーアカウントでは、ソフトウェアをインストールする必要はなく、バックアップ関連のアプリケーションだけを実行できればよいので、新規ソフトウェアをインストールする権限などは付与されない。この原則は、普通のユーザーアカウントで作業しているパーソナルコンピュータのユーザーにも当てはまる。つまり、事態が完全に特権を要求する場合に限って管理者権限を与えられ、パスワードで保護されたアカウント（すなわちスーパーユーザー）にログインする。
[最小権限の原則 - Wikipedia](https://ja.wikipedia.org/wiki/%E6%9C%80%E5%B0%8F%E6%A8%A9%E9%99%90%E3%81%AE%E5%8E%9F%E5%89%87)

AWSの運用時に、更新可能な権限が必要なシチュエーションは多くありません。ログの確認やEC2インスタンスの状態確認はReadOnlyの権限でアクセスし、設定の変更時のみ必要な権限を使用したいですね。
このような時にもAssumeRoleが使えます。必要な権限に応じてRoleを切り替えることで余分な権限を持つことなくアカウントを運用することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36587/97dee45a-b294-deb6-ba3b-a8a0906e6106.png)

## 課題

### 暗号化されていない認証情報

AssumeRoleにより、複数アカウントのログインができるアカウントや、強い権限に切り替え可能なアカウントなどを準備すると、１アカウントが持つ権限が強くなっていきます。それに伴い、アクセスキーの管理はますます重要になります。
通常、AWSのアクセスキーは `$HOME/.aws/credentials` に記述します。その場合、認証情報は暗号化しない状態で格納されています。macOSであれば、FileVaultによるファイルシステムレベルでの暗号化がされていますが、マルウェアにより不正に `$HOME/.aws/credentials` の内容を取得され、利用されてしまうことも考えられます。

## aws-vaultのしくみ

aws-vaultはAWSのアクセスキーをOSのキーストアに保存します。これによりアクセスキーを暗号化して保持することができます。macOSであればKeyChain、Windowsであれば資格情報マネージャに保存されます。これにより、マルウェア経由でのアクセスは拒否されます。

aws-vaultはSTSを使用することにより、一時的セキュリティ認証情報（AWSの公式な用語ですが硬いですね）を払い出します。一時的セキュリティ認証情報は短命であり、万が一漏れたとしてもリスクを減らすことができます。

また、一時的セキュリティ認証情報はMFAの情報を含むため、期限内でのMFAの再入力は必要ありません。

## インストール方法

macOSであれば、[Homebrew](https://brew.sh/)経由でインストールできます。

```shell
brew cask install aws-vault
```

Windows/Linuxは [Installing](https://github.com/99designs/aws-vault#installing) を参照してください。

## 認証情報の追加

IAMで払い出したアクセスキーをaws-vaultに登録します。

```shell
$ aws-vault add private
Enter Access Key Id: AKIAIOSFODNN7EXAMPLE
Enter Secret Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
```

また、`$HOME/.aws/config` に設定を追加します。

```ini
[profile private]

[profile private_admin]
source_profile=private
role_arn=arn:aws:iam::123456789012:role/admins-assume
role_session_name=user01
mfa_serial=arn:aws:iam::123456789012:mfa/user01
region=ap-northeast-1
```

まずはAssumeRole元のprofileである`private` です。
通常 `AWS_ACCESS_KEY_ID` `AWS_SECRET_ACCESS_KEY` を指定する必要がありますが、アクセスキーはaws-vaultで管理していますので、指定する必要はありません。

次にAssumeRole先のprofileである、`private_admin`です。
AssumeRoleに必要となるパラメータは `source_profile` と `role_arn` です。
`source_profile` はAssumeRole元のプロファイルを指定します。今回は `private` からAssumeRoleするので、 `profile` を指定します。
`role_arn`は引き受けるRoleのARNを指定します。

`role_session_name` はユーザがRoleを引き受ける時に指定するロールセッション名になります。ロールセッションのARNには`arn:aws:iam::123456789012:assumed-role/admins-assume/user01` のようにロールセッション名が含まれるので、正しく指定しましょう。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-role.html

`mfa_serial`にはMFAトークンのARNを指定します。
引き受け先のロールにアタッチされた信頼ポリシーが MFA を必要とする場合、この指定は必須となります。MFAの使用によりセキュリティを高めることができるのでおすすめです。

## キーの使用

`aws-vault exec` は認証情報を含んだ環境変数を追加し、`--` 以下のコマンドを実行します。

```shell
$ aws-vault exec private -- aws s3 ls
bucket_1
bucket_2
```

### direnvとの組み合わせ

[direnv](https://github.com/direnv/direnv)はカレントディレクトリに応じて環境変数をロードおよびアンロードすることができるツールです。
今まではディレクトリごとの `.envrc` や、 `$HOME/.config/direnv/direnvrc` を作成し、AWSの認証情報を記述することでAWSコマンドを使用していました。
しかし、設定の方法により、暗号化されてないアクセスキーが設定ファイルに残る可能性があります。

そこで、aws-vaultとdirenvとを組み合わせて使います。

```shell:$HOME/.config/direnv/direnvrc
use_aws_profile(){
  profile_name=$1

  export AWS_ASSUME_ROLE_TTL=1h
  export $(aws-vault exec $profile_name --prompt=osascript -- env | grep AWS_ | grep -v AWS_VAULT)
}
```

次に、direnvを使用するディレクトリに以下のファイルを追加します。

```shell:.envrc
use aws_profile private
```

これにより、`aws-vault exec` で払い出した一時的セキュリティ認証情報をdirenvで使用することができます。
セッションの有効期限切れになった場合は `direnv reload` を実行すると一時的セキュリティ認証情報を更新できます。

参考 : [aws-vaultをdirenvと組み合わせて使う](https://qiita.com/minamijoyo/items/89cdb1db803c5d7a656f)

## ブラウザでのログイン

`aws-vault login` で認証情報を含んだURLを元に、AWSのコンソールを開きます。

```shell
aws-vault login private
```

### 複数のAWSアカウントをaws-vaultから開く

AWSのマネージメントコンソールは複数のAWSアカウントを切り替えて使用することができないため、AWSアカウントの切り替え時にはログアウトが必要となります。
その対策として、Chromeのプロファイルを複数作成し、AWSアカウントごとに切り替えて使用することで複数のAWSマネージメントコンソールを同時に立ち上げることができます。

以下のaliasを.zshrcに追加します。

```shell
alias avl='(){ open -na "Google Chrome" --args --incognito --user-data-dir=$HOME/Library/Application\ Support/Google/Chrome/aws-vault/$@ $(aws-vault login $@ --stdout) }'
```

`avl <プロファイル名>` で任意のAWSアカウントのマネージメントコンソールをChromeから起動することができます。

参考: [aws-vault loginでChromeのウィンドウをAWSアカウント毎に分離する](https://qiita.com/minamijoyo/items/f3cbb003a34954a32970)

### Alfredとの組み合わせ

こうなればAlfredからも起動したいですね。というわけで自作のAlfred workflowを公開しています。Open AWS via aws-vault という名前です（[packal](http://www.packal.org/workflow/open-aws-aws-vault) / [GitHub](https://github.com/kangaechu/aws-vault-alfred-workflow)）。
`aws <プロファイル名>` で任意のAWSアカウントのマネージメントコンソールをChromeから起動することができます。

Alfredでの実行イメージはこんな感じです。

![b5515fe2-6cc2-9329-52fb-93b6df0d7f7a.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36587/93ba97dc-959a-7446-9a14-34c38b48ada8.gif)

## 備考

### AssumeRoleのセッションは最長1時間

AssumeRoleのセッションのタイムアウトはデフォルトで15分ですが、コマンド実行時の引数 `--assume-role-ttl`、または環境変数 `AWS_ASSUME_ROLE_TTL` で指定できます。最大 `12h` まで指定可能です。
しかし、複数のRoleを引き受ける（[ロールの連鎖](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_terms-and-concepts.html#iam-term-role-chaining)）場合、指定可能な値は最長 `1h` になります。

長時間実行するような処理の場合、処理途中でセッションがタイムアウトする場合がありますのでご注意ください。

### macOSのKeyChainのタイムアウトを伸ばす

デフォルトではmacOSを5分操作しない場合にパスワードの入力が必要となりますが、以下の設定を行うことにより、タイムアウトを変更することが可能です。
[aws-vaultのmacOSのキーチェーンのタイムアウトを伸ばす](https://qiita.com/minamijoyo/items/5ed3113434e51308ded1)

## まとめ

aws-vaultの導入により、コマンドラインやAlfredから複数のAWS環境を安全に使い分けることができるようになりました。皆様の安全なAWSライフに役立ててください！