この記事は
YAMAP エンジニア Advent Calendar 2020 の四日目になる予定です。
tl;dr
Github Actions (CircleCI でもほぼ同じアプローチでイケると思います) で AWS リソースを操作する際、強い権限を付与したアクセスキー、シークレットアクセスキーを使い続けたくないので、Assume Role を設定して、AWS リソースを操作する時だけ (デプロイする時だけ) 強い権限を払い出すようにしてみたのでメモします。
Assume Role とは
Assume Role
Assume Role とは、直訳すると、「Assume Role (役割を引き受ける)」となります。ある Role に紐付いているポリシー (権限) をユーザーやグループ、他の AWS アカウントのユーザーやグループ、別の Role に委譲する機能です。
以下は、Assume Role のイメージです。
How to Use a Single IAM User to Easily Access All Your Accounts by Using the AWS CLI より引用
Assume Role を利用するメリット
Assume Role を利用することのメリットとしては、以下のようなことが挙げられます。
- Assume Role されるユーザーに払い出す権限はデプロイ用ロールに対する
sts:AssumeRole
だけで良いので、シンプルに保つことが出来る - Assume Role される権限の、アクセスキー、シークレットアクセスキーは一時的に払い出されるもので、期限が切れると全くの無力である為、安心安全
登場人物
Assume Role でググると、複数の AWS 間での権限委譲の実装例を良く見ますが、本記事で紹介する実装例では、とてもシンプルな一つの AWS アカウント内のお話となります。
そして、本記事内で登場する登場人物は以下の通りです。
- Assume Role されるユーザー (以後、デプロイ用ユーザーと記載)
- Assume Role されるユーザーに付与するポリシー (以後、デプロイ用ユーザーポリシー)
- Assume Role するロール (AWS リソースを操作する為に必要なポリシーが付与されている、以後、デプロイ用ロールと記載)
実装例
リポジトリ
このサンプルですが、Github Actions を使って、S3 バケットのオブジェクト一覧を取得したり、オブジェクトを削除したり、オブジェクトを追加します。
準備
以下のリソースを Terraform を使って作成します。
- Assume Role されるユーザー (以後、デプロイ用ユーザーと記載)
- Assume Role されるユーザーに付与するポリシー (以後、デプロイ用ユーザーポリシー)
- Assume Role するロール (AWS リソースを操作する為に必要なポリシーが付与されている、以後、デプロイ用ロールと記載)
- S3 バケット
以下のように実行します。
$ git clone git@github.com:inokappa/deploy-sample-for-assume-role.git $ cd deploy-sample-for-assume-role $ cd setup $ make plan $ make apply
これらのリソースを作成する際には、それなりの権限が付与された IAM ユーザーで作成しましょう。
以下のように、出力されますので、それぞれの値を控えておきましょう。
... Apply complete! Resources: 0 added, 0 changed, 0 destroyed. Outputs: id = AKxxxxxxxxxxxxxxxxxxxxxxx role = arn:aws:iam::012345678901:role/deploy-user-role secret = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Github リポジトリの設定
上記で出力された、id
や secret
及び secret
等の情報を、下図のように Github リポジトリの Settings
> Secrets
に設定します。
Github Actions の YAML
以下のように設定します。
name: sandbox on: push: branches: - 'master' - 'main' jobs: sandbox: runs-on: ubuntu-latest env: S3_TARGET_BUCKET: ${{ secrets.S3_TARGET_BUCKET }} steps: - name: Checkout uses: actions/checkout@v2 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 role-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }} role-duration-seconds: 900 - name: List up Objects run: | aws s3 ls s3://${S3_TARGET_BUCKET}/ - name: Put Objects run: | aws s3 cp test.txt s3://${S3_TARGET_BUCKET}/test.txt - name: Remove Objects run: | aws s3 rm s3://${S3_TARGET_BUCKET}/test.txt
肝心なのは、以下の部分で、AWS から Configure AWS Credentials が提供されているので、これを利用することで、とても簡単に設定することが出来ました。
steps: ... 略 ... - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 role-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }} role-duration-seconds: 900 ... 略 ...
以下の内容を設定します。
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
には、デプロイ用ユーザーのアクセスキーを設定しますaws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
には、デプロイ用ユーザーのシークレットアクセスキーを設定しますrole-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }}
には、デプロイ用ロール名を設定します
デプロイ用ユーザーのアクセスキーとシークレットアクセスキーだけでは、S3 バケットにアクセスことは出来ません。あくまでも、Assume Role でロールを引き受けるだけの権限が付与されている状態です。
尚、role-duration-seconds
では、Assume Role された権限の猶予時間を指定します。上記の例では、900
秒 (15
分) を設定しています。
git push
適当に test.txt を修正して、git push
します。
$ echo 'foo' > test.txt $ git add && git commig -m 'Add foo' && git push
すると、下図のように正常に S3 バケットにアクセスしてオブジェクトの操作が行えていることが判ります。
試しに、YAML ファイルを以下のように修正して push してみると...
diff --git a/.github/workflows/sandbox.yml b/.github/workflows/sandbox.yml index b946edd..2fae2d7 100644 --- a/.github/workflows/sandbox.yml +++ b/.github/workflows/sandbox.yml @@ -22,8 +22,8 @@ jobs: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - role-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }} - role-duration-seconds: 900 + # role-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }} + # role-duration-seconds: 900 - name: List up Objects run: |
Assume Role 出来ておらず、権限が付与されていないので、下図のようにエラーとなりました。
いい感じです!
以上
正直、思ったよりも簡単に Assume Role を使って、Github Action から AWS リソースを操作することが出来たので、まだまだ、俺も捨てたもんじゃないなって自画自賛しています :sweat_smile:
Assume Role を利用することで、Github Actions や CircleCI 等の外部サービス用の IAM ユーザーに対して、個別に細かい権限を付与する必要がなく、また、時限的に権限が付与される為、アクセスキー、シークレットアクセスキーの漏洩に対して、安心安全に運用出来そうです。
ということで、素敵な Assume Role ライフをお過ごし下さい。
ちょっと待った、CircleCI だとどうする?
いくつか Assume Role の為の orbs が提供されていましたが、どれを使ったら良いのか判断出来なかったので、泥臭くシェルスクリプトで書いてみました。
# reference: https://dev.classmethod.jp/articles/circleci-use-aws-cli-v2/ version: 2.1 executors: my-executor: docker: - image: cimg/base:2020.11 working_directory: ~/work environment: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$HOME/work/aws-cli/v2/current/bin commands: restore: steps: - restore_cache: key: work-v1-{{ .Revision }} save: steps: - save_cache: paths: - "aws-cli" key: work-v1-{{ .Revision }} install: steps: - run: name: install command: | if [[ ! -d aws-cli ]]; then curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip sudo ./aws/install --install-dir ~/work/aws-cli fi assume_role: steps: - run: name: Assume Role command: | AWS_STS_CREDENTIALS="$(aws sts assume-role \ --role-arn=${AWS_ASSUME_ROLE_ARN} \ --role-session-name "deply-user-session" \ --external-id 00001 \ --duration-seconds 900 \ --query "Credentials" \ --output "json")" echo "export AWS_ACCESS_KEY_ID=$(echo ${AWS_STS_CREDENTIALS} | jq -r '.AccessKeyId')" >> ${BASH_ENV} echo "export AWS_SECRET_ACCESS_KEY=$(echo ${AWS_STS_CREDENTIALS} | jq -r '.SecretAccessKey')" >> ${BASH_ENV} echo "export AWS_SESSION_TOKEN=$(echo ${AWS_STS_CREDENTIALS} | jq -r '.SessionToken')" >> ${BASH_ENV} access: steps: - run: name: List up Objects command: | aws s3 ls s3://$S3_TARGET_BUCKET/ - run: name: Put Object command: | aws s3 cp test.txt s3://$S3_TARGET_BUCKET/test.txt - run: name: Remove Object command: | aws s3 rm s3://$S3_TARGET_BUCKET/test.txt jobs: sandbox: executor: my-executor steps: - checkout - restore - install - save - assume_role - access workflows: sandbox: jobs: - sandbox
以下のように意図した通りに AWS リソースの操作が出来ています。