これまで、AWS SDKをLambdaにデプロイする際のクレデンシャル(IAMユーザーのアクセスキーとシークレットアクセスキー)管理について課題を感じていました。
環境変数からリソースの強い操作権限をもったIAMユーザーのアクセスキーを読み込む方法だと、キーが流出したときのセキュリティリスクが大きいので、IAMロールを利用する方法に変更しました。
本記事では、この変更を実施する際の手順を備忘録としてまとめています。
手順
GitHub ActionsのデプロイワークフローでGitHub SecretsからAssumeRole権限をもったIAMユーザーのアクセスキーとシークレットアクセスキーを読み込み、AssumeRoleでIAMロールの権限を引き受けるようにしました。
IAMユーザーの作成
コンソールやIaCツールからIAMユーザーを作成し、以下のカスタムポリシーを作成して関連付けます。
Resource
欄については、IAMロール作成後にARNをコピペします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole",
"sts:TagSession"
],
"Resource": "arn:aws:iam::<Account ID>:role/<Role Name>"
}
]
}
追記
sts:AssumeRole
だけじゃなく、sts:TagSession
も必須です。
次に、「セキュリティ認証情報 > アクセスキー」からアクセスキー(およびシークレットアクセスキー)を発行します。
このアクセスキーは、GitHub ActionsのデプロイワークフローでAssumeRole(IAMロールの権限を引き受ける)際に必要となります。
注意
このIAMユーザーはリソースの操作権限を持っていません。
もしキーが第三者に漏れても、重大なリソース操作は行えないため、セキュリティのリスクは低いですが、それでも流出を防ぐための対策(キーのローテーションなど)が必要です。
追記 2024-01-07
OpenID Connectを使用すれば、IAMユーザーの作成は不要です。
IAMロールの作成
IAMユーザーと同様に、コンソールやIaCからIAMロールを作成します。
デプロイワークフロー用のIAMロール
こちらのポリシーには、デプロイに必要なリソースの操作権限(CloudFormation, API Gateway, Lambda, ECR, IAMなど)を付与します。
次に、IAMロールの「信頼関係」タブに移動し、「信頼されたポリシー」の編集を選択します。信頼ポリシーでは、どのエンティティがロールを引き受けることができるかを定義します。
Principal
のAWS
には、先ほど作成したIAMユーザーのARNをコピペします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<Account ID>:user/<User Name>"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
],
"Condition": {}
}
]
}
Lambda用のIAMロール
こちらのポリシーには、アプリケーションで必要なリソースの操作権限(S3, SESなど)を付与します。
Lambda詳細画面の「設定タブ > アクセス権限」から、実行ロールとして作成したIAMロールを関連付けます。
これでアプリケーションのコード上でクレデンシャルを読み込む必要がなくなります。
GitHub Secretsへの環境変数の登録
GitHubリポジトリのSettingsタブから「Secrets and variables > Actions」を選択し、「New repository secret」からワークフローで使用する環境変数(AWS_ACCESS_KEY_ID
, AWS_SECRET_ACCESS_KEY
, AWS_IAM_ROLE_TO_ASSUME
)を登録します。
AWS_IAM_ROLE_TO_ASSUME
にはIAMロールのARNを入力します。
GitHub Actionsデプロイワークフローの修正
以下がデプロイワークフローの一部です。
configure aws
のステップで先ほど設定した環境変数を読み込んでいます。
name: Lambda Deploy
on:
push:
branches:
- develop
paths:
- "**"
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- name: install dependencies
run: yarn install
working-directory: ./
- name: configure aws
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 }}
role-to-assume: ${{ secrets.AWS_IAM_ROLE_TO_ASSUME }}
role-duration-seconds: 3600
注意
role-duration-seconds: 3600
の秒数はIAMロールの最大セッション時間以下に設定する必要があります。
ローカル環境での開発
Lambdaデプロイ後の環境ではLambdaの実行ロールでAWSリソースの操作が可能になりますが、ローカル環境(Dockerなど)では実行ロールがないため、従来通りクレデンシャルを環境変数から読み込む運用となります。
そのため、アプリケーションのコードを以下のように修正する必要があります。
private createS3Client(): S3Client {
const options: S3ClientConfig = {
region: 'ap-northeast-1',
...(process.env.ENV === 'local' && {
credentials: {
accessKeyId: process.env.ACCESS_KEY_ID as string,
secretAccessKey: process.env.SECRET_ACCESS_KEY as string
}
})
}
return new S3Client(options)
}
おわりに
ECSにデプロイする場合も大体同じような手順です。
ECSタスク定義でタスクロールを指定することにより、ECSタスクが実行されるEC2インスタンスやFargate上で動作するコンテナがAWSリソースにアクセスできるようになります。
このタスクロールは、特定のAWSサービスやリソースに対するアクセス権限を定義するIAMポリシーをアタッチすることでカスタマイズ可能です。