はじめに
何番煎じ案件ですが、社内にて OIDC 経由で GitHub Actions に AWS アクセスしようぜが話題に上がっていたため、自分の方でも素振りをしてみたので備忘録を残しておきます。 社内の強い人たちの足跡をほぼほぼ辿っただけでしたが、永続的な Credentials を Actions に残したくない気持ちは同じだったので本当に助かります 🙇♂️
内容
OIDC 自体の解説はコチラが参考になるかと思います。🙏
まず、AWS 側で GitHub 向けの OIDC と role を設定します。
CDK のサンプルソースが見当たらず、公式が提供する AWS 向けの OIDC カスタムアクション aws-actions/configure-aws-credentials に CFn テンプレートが記述されていたため、それを参考に CDK を起こしています。 CDK で私が記述した場合は以下になります 💦
1// bin/index.ts
2const { principal: githubOIDCPrincipal } = new GitHubOIDC(app, "github-oidc", {
3 env,
4 subject: "repo:sample-org/sample-rep:*",
5});
6
7new IamRole(app, "build-role", {
8 env,
9 roleProps: {
10 roleName: "build",
11 assumedBy: githubOIDCPrincipal,
12 // 操作するリソース向けの policy を設定する必要があります。
13 // 以下の policy では、当然 Lambda のデプロイ等はできません。
14 managedPolicies: [
15 iam.ManagedPolicy.fromAwsManagedPolicyName("AWSCloudFormationFullAccess"),
16 iam.ManagedPolicy.fromAwsManagedPolicyName("IAMFullAccess"),
17 ],
18 },
19});
1// lib/open-id-connect.ts
2import * as cdk from "@aws-cdk/core";
3import * as iam from "@aws-cdk/aws-iam";
4
5type GitHubOIDCProps = cdk.StackProps & {
6 subject: string;
7};
8
9export class GitHubOIDC extends cdk.Stack {
10 public readonly principal: iam.FederatedPrincipal;
11
12 constructor(scope: cdk.App, id: string, props: GitHubOIDCProps) {
13 super(scope, id, props);
14
15 const { subject } = props;
16
17 // https://github.com/aws-actions/configure-aws-credentials#sample-iam-role-cloudformation-template
18 const { openIdConnectProviderArn } = new iam.OpenIdConnectProvider(
19 this,
20 id,
21 {
22 url: "https://token.actions.githubusercontent.com",
23 clientIds: ["sts.amazonaws.com"],
24 thumbprints: ["a031c46782e6e6c662c2c87c76da9aa62ccabd8e"],
25 }
26 );
27
28 // https://docs.github.com/ja/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#configuring-the-role-and-trust-policy
29 this.principal = new iam.FederatedPrincipal(
30 openIdConnectProviderArn,
31 {
32 StringLike: {
33 "token.actions.githubusercontent.com:sub": subject,
34 },
35 },
36 "sts:AssumeRoleWithWebIdentity"
37 );
38 }
39}
1// lib/iam.ts
2
3import * as cdk from "@aws-cdk/core";
4import * as iam from "@aws-cdk/aws-iam";
5
6type IamRoleProps = cdk.StackProps & {
7 roleProps: iam.RoleProps;
8};
9
10export class IamRole extends cdk.Stack {
11 public readonly role: iam.Role;
12
13 constructor(scope: cdk.App, id: string, props: IamRoleProps) {
14 super(scope, id, props);
15
16 const { roleProps } = props;
17
18 this.role = new iam.Role(this, id, {
19 ...roleProps,
20 });
21 }
22}
次に Actions になります。 以下は、認証を済ませた後に、Container Image を build、push する CI のサンプルになります。 コチラを参考にしています。
1name: sample
2
3on:
4 pull_request:
5 paths-ignore:
6 - "**.md"
7
8jobs:
9 build:
10 runs-on: ubuntu-20.04
11 #
12 permissions:
13 id-token: write
14 contents: read
15 env:
16 AWS_ROLE_ARN: arn:aws:iam::${お使いのaws-account-id}:role/build
17 AWS_WEB_IDENTITY_TOKEN_FILE: /tmp/awscreds
18 AWS_DEFAULT_REGION: ap-northeast-1
19 AWS_REGION: ap-northeast-1 # cdk内部でsdkを利用する関係で AWS_REGION が必要
20 AUDIENCE: sts.amazonaws.com
21
22 steps:
23 - uses: actions/checkout@v2
24
25 # ここで認証する
26 - name: Configure AWS
27 run: |
28 curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=$AUDIENCE" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE
29
30 - name: Login to Amazon ECR
31 id: login-ecr
32 uses: aws-actions/amazon-ecr-login@v1
33
34 - name: Build, tag, and push image to Amazon ECR
35 working-directory: packages/graphql
36 env:
37 ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
38 ECR_REPOSITORY: sample
39 IMAGE_TAG: ${{ github.sha }}
40 run: |
41 docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
42 docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
おわりに
永続的な credentials を排除できたのですが、role の 細かい policy 管理が出てきたため面倒だなとも思っています 💦 と言いつつ、今までいい加減に管理していたフルフルな role を正すのに良い機会だとも思っています 🙇♂️