GitHub ActionsでIAMロールを使ってAWSのリソースにアクセスできる仕組みと実装方法について解説
ここ最近、新しい家探しと引越し、それに伴う作業が忙しくて全く更新できていませんでした。
4ヶ月ぶりのアウトプットです。
4ヶ月ぶりの今回は、GitHub ActionsからAWSのIAMロールを使って、AWSリソースを操作する方法についての記事を書きます。
今回はやり方をただ解説するだけじゃなくて、IAMロールを使ってアクセスした方がいい理由についても書いていきたいと思います。
インフラ得意な人からしたら、基本的なことしか書いていないかもしれませんし、なんなら間違った情報も書いているかもしれません。
間違った情報書いている場合は、TwitterのリプやDMなどに意見いただけると嬉しいです。
従来の方法について
まず初めにGitHub ActionsでAWSリソースを操作するための従来の方法について解説していきたいと思います。
従来は、AWSのアクセスキーとシークレットキーを使用して、AWSリソースの操作を行なっていました。
GitHubでアクセスキー・シークレットキーを利用してAWSリソースを操作するフローは以下のようになります。
- デプロイ用のIAMユーザーを作成する
- アクセスキー・シークレットキーを発行する
- GitHubの管理画面で作成したアクセスキー・シークレットキーを環境変数として設定する
- GitHub Actions側で管理画面側で設定したアクセスキーとシークレットキーを取得して、AWSリソースの操作を実行する
この方法だと、AWS外のサービスに永続的なクレデンシャル情報を保持する必要があるため、セキュリティ面の課題があります。
誤操作で、アクセスキーとシークレットキーを含めたコードを本番環境のサーバにアップロードしてしまい、アクセスキーとシークレットキーを抜き取られる危険性もあると思います。
上記のような問題を解決することができるのが、次の見出しで解説しているIAMロールを使用した方法です。
IAMロールを使用した方法は何が違うのか
では、IAMロールを使用した方法はアクセスキー・シークレットキーを用いた方法と何が違うのでしょうか?
IAMロールでAWSリソースを操作操作するためには、AWS STS(AWS Security Token Service)と呼ばれる、一時的な認証情報を発行してくれるサービスを利用します。
AWS STSによって作成された一時的な認証情報には以下のような特徴があります。
・一時的セキュリティ認証情報は、その名前が示すとおり、使用期限が短くなっています。有効期限は数分から数時間に設定できます。認証情報が失効すると、AWS はそれらを認識しなくなります。また、その認証情報によって作成された API リクエストによるあらゆるタイプのアクセスが許可されなくなります。
・一時的セキュリティ認証情報はユーザーとともに保存されることはなく、ユーザーのリクエストに応じて動的に生成され、提供されます。一時的セキュリティ認証情報が失効すると(または失効する前でも)、ユーザーは新しい認証情報をリクエストできます。ただし、リクエストするユーザーがまだその権限を持っている場合に限ります。
引用: https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_temp.html
公式ドキュメントの情報から、アクセスキーとシークレットキーを利用した認証とは以下の点が異なることがわかります。
- アクセスキーシークレットキー・・・AWSのコンソール上で無効にするか削除しない限り、永続的に利用可能。
- STSを使用した一時認証・・・ユーザーのリクエストに応じて動的に生成され、提供される上に、短時間しか利用できない。(認証情報が失効すると、AWS側が認証情報を認識しなくなる)
ここでAWS詳しくない(苦手意識めちゃある)自分は、IAMロールという文脈でSTSっていうまた訳わからん横文字出てきたけど、STSというサービスを使ってどうやって一時的な認証情報を取得するのかという点に疑問を持ちました。
AWS STSを使用した一時的な認証情報の利用方法に関しても公式ドキュメントに記述されていました。
AWS STS と AWS リージョン
一時的な認証情報は AWS STS によって生成されます。デフォルトでは、AWS STS は
https://sts.amazonaws.com
に 1 つのエンドポイントのあるグローバルサービスです。ただし、他のサポートされているリージョンにあるエンドポイントへの AWS STS API 呼び出しを実行することもできます。地理的に近い場所にあるリージョンのサーバーに対してリクエストを送信することによって、レイテンシー (サーバーのラグ) を低減できます。認証情報を取得したリージョンに関係なく、認証情報はグローバルに使用できます。詳細については、「AWS リージョンでの AWS STS の管理」を参照してください。引用: https://docs.aws.amazon.com/jajp/IAM/latest/UserGuide/idcredentials_temp.html
https://sts.amazonaws.com
というエンドポイントに「一時的な認証情報ください。」というリクエストを実行することで、AWSのリソースを操作することができるようなイメージです。
一時的な認証情報の要求を受けたSTSはIAMロールに設定された権限を持った一時キー(アクセスキー・シークレットキー・セッショントークンなど)を発行してあげる必要があります。
この一時的なAWSリソースの操作を行うために、AssumeRoleと呼ばれる操作を利用します。
AssumeRoleを利用することで、通常はアクセスできないAWSリソースへのアクセスに使用できる一時的なセキュリティ認証情報のセットを取得する権利を付与することができます。
ここまでの流れを箇条書きでまとめると以下のような感じになります。
- AWS STS(Security Token Service)に対してリクエストを送る
- AssumeRoleと呼ばれる操作を実行して、IAMロールと同じ操作を一時的にできるようにする
AssumeRoleの仕組みに関しては、クラスメソッドが図解で解説しているサイトが非常に分かりやすかったので、紹介させていただきます。
https://dev.classmethod.jp/articles/iam-role-passrole-assumerole/
実際にやってみる
では、実際にIAMロールを使って、GitHub ActionsからAWSリソースにアクセスしてみましょう。
今回は、GitHub ActionsからAWSにアクセスして、特定のバケットの中身を一覧で表示する処理を実装してみます。
IDプロバイダの作成
まずは、AWSのIDプロバイダから、GitHubが提供するOIDC(OpenID Connect)プロバイダを追加していきます。
以下の値を設定していきます。
- プロバイダのタイプ: OpenID Connect
- プロバイダのURL: https://token.actions.githubusercontent.com
- 対象者: sts.amazonaws.com
設定する値に関しては、GitHubの公式ドキュメントに記述されています。
IDポリシーの作成
次にAWSのポリシーから、IAMロールにアタッチするIAMポリシーを作成します。
今回は、特定のS3バケットの中身を一覧で表示するだけなので、Action(許可ないし拒否する操作を指定する項目)の部分にはs3:ListBucketを指定します。
以下は、github-actions-s3-testというバケットに対して、一覧を取得する動作のみを許可するIAMポリシーになります。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": [
"arn:aws:s3:::github-actions-s3-test",
"arn:aws:s3:::github-actions-s3-test/*"
]
}
]
}
Resourceの部分には、作成したS3バケットの名前を指定します。
IDロールの作成
次にIAMロールの作成を行います。
AWSのIAMロールのロールを作成をクリックして、ウェブアイデンティティを選択します。
ウェブアイデンティティを選択すると、アイデンティティプロバイダーとAudienceを選択できるようになるので、それぞれ以下のように設定します。
- アイデンティティプロバイダー・・・token.actions.githubusercontent.com
- Audience・・・sts.amazonaws.com
次へを押すと、作成したポリシーを選択する画面が表示されるので、作成したポリシーにチェックをつけてアタッチします。
→AWSがデフォルトで提供しているポリシーではないものが表示されていたので、念のために隠してます。
名前をつけて保存します。
これでGitHub Actionsから特定のS3バケットを一覧で表示するためのIAMロールが完成です!
このままでも問題はありませんが、StringLikeという項目を追加すると、特定のユーザー(organizations)が管理する特定のリポジトリからしか処理を実行できないようにできます。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<AWSアカウントID>:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:<GitHubユーザー名>/<GitHubリポジトリ名>:*"
}
}
}
]
}
この設定を行うと、今回のようなS3バケットを操作するようなケースだと、リポジトリとS3のバケットを1対1の関係で紐づけることができるので、より高い安全性を担保できるのかなと思います。
ビルドしたリソースをS3にアップロードする必要があるときなどは、設定した方が良さそうです。
GitHub actionsのワークフローを作成する
次にGitHub actionsのワークフローを作成します。.github/workflows
配下に以下のようなymlファイルを作成します。
sample.yml
name: Amazon S3 Sample
on:
workflow_dispatch:
env:
AWS_REGION: ap-northeast-1
AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }}
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
id-token: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Configure AWS credentials from IAM Role
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ env.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Execute s3 ls
env:
S3_BUCKET: ${{ secrets.S3_BUCKET }}
run: |
aws s3 ls s3://$S3_BUCKET
AWS_ROLE_ARNには、作成したIAMロールのARN(AWSリソースを一意に識別するためのもの)を設定します。→ARNは作成したIAMロールから確認することができます。
環境変数の設定も必要なので、GitHubの管理画面側で設定します。
リポジトリがすでに用意できている場合は、事前に設定しておくと良いでしょう。
ワークフローを実行する
ファイルの追加・変更が完了したらリモートにプッシュします。
sample.yml側でworkflow_dispatchという項目を設定しているので、管理画面側から手動実行が可能です。
管理画面側でワークフローの実行を行なって、S3バケットの中身を一覧で取得・表示できたらうまくいっています。
まとめ
- GitHub actionsでAWSのリソースの操作を行うときは、セキュリティ的な観点からIAMロールを使って行うべきである。
- IAMロールを使ってAWSのリソースの操作を行うためには、一時的な認証情報をAWS STS(AWS Security Token Service)経由で取得する必要がある。
- AssumeRoleと呼ばれる操作を実行して、IAMロールと同じ操作を一時的にできるようにする
IAMロールを利用することで、AWS外からAWSリソースにアクセスするという結構リスキーな行為をより安全に行うことができるので、積極的に導入していきたいところですね!
おまけ
今回の記事ですが、半分くらいは引越した先の近所のおしゃれなカフェで書きました。
新居の近くにはおしゃれなカフェがたくさんあるので、外出するのがとても楽しいです。