GitHub ActionsからOIDCでassumeできるRoleをCDKで作成する

awsgithubauth

aws-actions/configure-aws-credentials は AWS の Role を assume する Actionで、 Access Key による認証もサポートしているが、GitHub の OIDC Provider が発行した JWT を用いることで認証情報を持つことなくセキュアにAPIを呼ぶことができる。

OpenID ConnectのIDトークンの内容と検証 - sambaiz-net

外部の Provider を信頼するにあたり、HTTPS通信する際のチェーン最上位の中間認証局の証明書の thumbprint を登録する必要がある。 thumbprint は次のようにして取得できる。 以前中間認証局が変更された際は併せて thumbprint も更新する必要があった。

$ openssl s_client -servername token.actions.githubusercontent.com -showcerts -connect token.actions.githubusercontent.com:443
CONNECTED(00000005)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
verify return:1
depth=0 C = US, ST = California, L = San Francisco, O = "GitHub, Inc.", CN = *.actions.githubusercontent.com
verify return:1
---
Certificate chain
 0 s:/C=US/ST=California/L=San Francisco/O=GitHub, Inc./CN=*.actions.githubusercontent.com
   i:/C=US/O=DigiCert Inc/CN=DigiCert TLS RSA SHA256 2020 CA1
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
 1 s:/C=US/O=DigiCert Inc/CN=DigiCert TLS RSA SHA256 2020 CA1
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
/* certificate.crt
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
*/
...

$ openssl x509 -in certificate.crt -fingerprint -noout
SHA1 Fingerprint=69:38:FD:4D:98:BA:B0:3F:AA:DB:97:B3:43:96:83:1E:37:80:AE:A1

trust policy の Condition で sub で渡ってくるリポジトリ名などによる制限をかける。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as s3 from 'aws-cdk-lib/aws-s3';

export class GitHubDeployStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const deployBucket = new s3.Bucket(this, 'DeployBucket', {
      bucketName: `${this.stackName.toLowerCase()}-${this.account}-deploy-bucket`
    })

    const provider = new iam.OpenIdConnectProvider(this, 'Provider', {
      url: 'https://token.actions.githubusercontent.com',
      thumbprints: ['6938fd4d98bab03faadb97b34396831e3780aea1'],
      clientIds: ['sts.amazonaws.com'],
    });
    const deployRole = new iam.Role(this, 'DeployRole', {
      roleName: `${this.stackName}_DeployRole`,
      assumedBy:  new iam.WebIdentityPrincipal(provider.openIdConnectProviderArn, {
        StringLike: {
          ['token.actions.githubusercontent.com:sub']: 'repo:<user_name>/<repository_name>:*',
        },
      })
    })
    deployRole.addToPolicy(new iam.PolicyStatement({
      actions: ["s3:PutObject*"],
      resources: [`${deployBucket.bucketArn}/*`],
    }));

    this.exportValue(deployRole.roleArn)
    this.exportValue(deployBucket.bucketName)
  }
}

この Role を assume することで aws s3 cp などが行えるようになる。

name: CI
on:
  push:
env:
  DEPLOY_ROLE_NAME: "arn:aws:iam::<AWSAccountID>:role/GitHubDeployStack_DeployRole"
  DEPLOY_BUCKET_NAME: "githubdeploystack-<AWSAccountID>-deploy-bucket"
  AWS_REGION: "ap-northeast-1"
permissions:
  id-token: write # This is required for requesting the JWT
  contents: read  # This is required for actions/checkout
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ env.DEPLOY_ROLE_NAME }}
          aws-region: ${{ env.AWS_REGION }}

参考

AWS CDKで Github ActionsのOIDC連携用のIAM Roleを作成する | DevelopersIO