【小ネタ】direnv + assume-roleでクレデンシャルを取得、有効期限を設定してみる

こんにちは(U・ω・U)
AWS事業部の深澤です。

皆さん、AWSのクレデンシャルを取得される際、どのような方法を取られていますか。色々な方法がありますが、例えば環境変数から取得する方法がまず考えられますよね。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-envvars.html

例えば以下のような変数でクレデンシャル情報を取得できます。

サポートされている環境変数 AWS CLI は次の環境変数をサポートしています。
・ AWS_ACCESS_KEY_ID – IAM ユーザーまたはロールに関連付けられる AWS アクセスキーを指定します。
・ AWS_SECRET_ACCESS_KEY – アクセスキーに関連付けられるシークレットキーを指定します。これは、基本的にアクセスキーの「パスワード」です。

ただ、色んな案件や環境(AWSアカウント)があった場合にはこれは煩雑です。
他には以下を参考にしながら、事前に名前付きプロファイルを設定しておき、 https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-profiles.html

SHELL変数を用いて次のように個別のAWSアカウントを呼び出すこともできます。~/.aws/credentialsに以下のように記載し、

[user1]
aws_access_key_id=AKIAXXXXXXXXXXXXXXXX
aws_secret_access_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

次のように実行します。

AWS_PROFILE=user1 aws s3 ls

しかし、この方法もいちいちSHELL変数込みでコマンドを打つのは煩雑ですよね。
ここにdirenvと呼ばれるものがあります。

https://github.com/direnv/direnv

これはディレクトリを遷移すると環境変数を設定してくれるというものです。
早速インストールしてみましょう。

direnvのセットアップ

Macの場合はbrewで提供されています。便利ですね。

$ brew install direnv

その後、shellにフックを設置してやる必要があります。
https://github.com/direnv/direnv/blob/master/docs/hook.md

自分はzshを使用しているので、~/.zshrcに次のように書き込みました。

eval "$(direnv hook zsh)"

次に環境変数を設定したいディレクトリに遷移して以下のコマンドを実行し、環境変数を設定します。

$ cd user1
$ direnv edit .

以下のコマンドを書き込みます。

export AWS_PROFILE=user1

こんな感じのメッセージが出れば設定完了です。

direnv: loading .envrc
direnv: export +AWS_PROFILE

ちなみにdirenv editでファイルを開く場合は、$EDITOR環境変数に好みのエディタを設定しておく必要があります。
https://github.com/direnv/direnv/blob/master/man/direnv.1.md

Note that direnv edit . is a handy shortcut that open the file in your $EDITOR and automatically allows it if the file's modification time has changed.

僕はzshとvimを使っているので次のようになります。
~/.zshrcに以下を記載します。

EDITOR=vim

これでディレクトリ遷移にてAWSのクレデンシャルが取得できるようになりました。しかし、ローカルに各環境のアクセスキーやシークレットキーを入れるのはあまり良くありません。さてここからが本題です。

スイッチロール

AWSにはスイッチロールという機能があります。
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_use_switch-role-console.html
AWSアカウントを踏み台にして、他のAWS環境に一時的な認証を行いログインする仕組みです。つまりこれを用いることで1つのAWSアカウントのみアクセスキーとシークレットキーを記載するだけで他の環境に入れるので、余計なキーを手元のconfigに記載せずに済みます。本記事では具体的にこのスイッチロールをどのように設定するかについては設定を割愛させていただきますが、参考までに遷移先のロールに付けた信頼関係ポリシーを貼らせていただきます。sample_switch_roleという名前でロールを作成しました。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::遷移元アカウントの番号:user/遷移元アカウントのIAMユーザ名"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    }
  ]
}

assume-role

assume-roleで有名なのは以下の2つかと思います。
https://github.com/remind101/assume-role
https://github.com/coinbase/assume-role

僕がこれまで使ってきたのはremind101/assume-roleでしたが、※こちらはセッションの継続時間を指定することができません。そこで今回はcoinbase/assume-roleをセットアップし、セッションの時間を検証してみようと思います。

※ こちらですがremind101/assume-roleでも以下のようにコマンドを用いればセッション継続時間を指定できると指摘をいただきました。

$ assume-role -duration 2h

こちらは改めて検証し後日修正させていただきますm(_ _)m

2019年10月9日追記

検証した結果、「remind101/assume-roleではセッションの継続時間を指定することができない」は誤りであることが後の検証で分かりました。詳細は以下のブログをご覧ください。
【小ネタ】remind101/assume-roleのduration機能について 〜assume-roleツールの比較もあるよ〜

coinbase/assume-roleのセットアップ

インストール方法に関しては使用されているOS別に公式ドキュメントを参照して下さい。
https://github.com/coinbase/assume-role#installation
自分はMacを使用していますので、brewで簡単にインストールできました。

$ brew tap coinbase/assume-role
$ brew install assume-role

これでassume-roleコマンドが使えるようになりました。実際に使うには毎回事前にスクリプトを呼び出す必要があります。

source $(which assume-role)

続いて.aws/credentialsへ遷移元アカウントのクレデンシャル情報を書き込みます。僕は次のように書き込みました。これ以降のコマンド例等は以下の情報を前提とします。

[bastion]
aws_access_key_id=AKIAXXXXXXXXXXXXXXXX
aws_secret_access_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

続いて.aws/configにconfig情報も書き込みます。

[profile bastion]
region=ap-northeast-1

そしてこの遷移元(bastion)情報をAWS_PROFILE_ASSUME_ROLEという環境変数に反映します。

export AWS_PROFILE_ASSUME_ROLE=bastion

これも毎回叩くのは煩雑かと思いますので、zshなら~/.zshrcとかbashなら~/.bashrcに記載した方が良いかと思います。

最後に遷移先となるawsアカウント番号を.aws/accountsに記載しましょう。

{
  "private_aws": "123456789012"
}

これで準備完了です。

実際に一時的認証状を取得してみる

これまで準備してきた情報を次のようにコマンドに当てはめます。

assume-role .aws/accountsに記載したアカウント名 遷移先アカウントで用意したロール名

従って、本記事で準備してきた内容を当てはめると次のようになります。

$ assume-role private_aws sample_switch_role
MFA Token:

上記のようにMFA Tokenを聞かれるので入力しましょう。AWS_ACCESS_KEY_ID等の認証系情報が環境変数に書き込まれているか or 何かしらのawsコマンドでawsのリソース状況を確認する等の方法で正しく認証できたかを確認できます。

env | grep AWS_ACCESS_KEY_ID
AWS_ACCESS_KEY_ID=ASIAXXXXXXXXXXXXXXXX

有効期限の設定

有効期限はAWS_ROLE_SESSION_TIMEOUTという環境変数を設定することで設定ができます。

$ export AWS_ROLE_SESSION_TIMEOUT=43200

こちらに入力した秒数期間分、セッションが続きます。上記の例だと12時間続くことになりますね。実際に短く設定して検証しようとしたら下限は900秒(15分)でした。

$ assume-role private_aws sample_switch_role
Using assume-role default profile: bastion
MFA Token: 123456


Parameter validation failed:
Invalid range for parameter DurationSeconds, value: 60, valid range: 900-inf
Failed to export session envars.

実際に900に設定して待機した結果、セッションが切れることを確認できました。期限設定は正しく機能しているようです。

$ aws s3 ls
2019-09-19 11:42:45 XXXXXXXXXXXXXXXXXXXXXX-bucket
〜15分後〜
$ aws s3 ls
An error occurred (ExpiredToken) when calling the ListBuckets operation: The provided token has expired.

direnvとの組み合わせ

コマンドの事前に設定が必要なコマンドをdirenvに入れれば良いです。対象のディレクトリ配下でdirenv edit .を実行。次のコマンドを入れましょう。

source $(which assume-role)
export AWS_ROLE_SESSION_TIMEOUT=900
assume-role private_aws sample_switch_role

これでディレクトリに遷移するだけで次のようになります。

$ cd private_aws
Using assume-role default profile: bastion
MFA Token: 123456

Success! IAM session envars are exported.
direnv: export +AWS_ACCESS_KEY_ID +AWS_ACCOUNT_ID +AWS_ACCOUNT_NAME +AWS_ACCOUNT_ROLE +AWS_ROLE_SESSION_TIMEOUT +AWS_SECRET_ACCESS_KEY +AWS_SECURITY_TOKEN +AWS_SESSION_ACCESS_KEY_ID +AWS_SESSION_SECRET_ACCESS_KEY +AWS_SESSION_SECURITY_TOKEN +AWS_SESSION_SESSION_TOKEN +AWS_SESSION_TOKEN +GEO_ENV +ROLE_SESSION_START ~AWS_SESSION_START

注意

ロールが連鎖している場合にはセッションは1時間しか続きません。 https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_terms-and-concepts.html

ロールの連鎖では、AWS CLI または API ロールセッションは最長 1 時間に制限されます。AssumeRole API オペレーションを使用してロールを引き受ける場合は、DurationSeconds パラメータを使用してロールセッションの期間を指定できます。パラメータの値は、ロールの最大セッション期間設定によって、最大 43200 秒 (12 時間) まで指定できます。ただし、ロールの連鎖を使用してロールを引き受ける場合、DurationSeconds パラメータ値で 1 時間を超える値を指定すると、オペレーションは失敗します。

感想

今回は自分のPCの設定がてらcoinbase/assume-roleを検証してみました。よくterraformで環境に設定を適用中にセッションが切れて実際の環境とstateファイルに差分が発生して元に戻すのが大変ってことがあったので今回セッションの時間調整ができてすごい便利だなと感じました!懸念としてはshellベースで動いているのでパッケージをyumとかbrewで更新した時に動作がおかしくなったりすることもあるのかなと思いましたね。

以上、深澤(@shun_quartet)でした!