LoginSignup
12
1

More than 1 year has passed since last update.

CDKを使ってGitHub Actions OpenID Connect (OIDC) で多段 AssumeRoleする

Last updated at Posted at 2021-12-02

この記事は ハンズラボ AdventCalendar2021 3日目の記事です。

はじめに

今回は、こちらの記事のCDK化(Python)を行っていきます。
構成やcfnテンプレートなど詳細はこちらをご参照ください。

前提条件

実装

ではさっそくやっていきます。

初期設定

ディレクトリを作成し、cdk initを実行してプロジェクトを立ち上げます。

$ mkdir github_oicd
$ cd github_oicd

$ cdk init app --language python

これで準備完了ですので、早速コーディングしていきます。

Provider作成

まずはProviderを作成していきます。
ここはドキュメント通りに値をセットするだけですので、簡単に実装できました。
ドキュメントはこちら

github_oicd_stack.py
github_oidc_provider = iam.OpenIdConnectProvider(self,
    client_ids=["sts.amazonaws.com"],
    thumbprints=["a031c46782e6e6c662c2c87c76da9aa62ccabd8e"],
    id="GithubOicdProvider",
    url="https://token.actions.githubusercontent.com",
)

bastion_role(踏み台ロール)作成

ここから少し難易度が上がった印象です。
Principalに先ほど作成したProviderを設定する箇所でかなり苦戦しましたが、最後はドキュメントが助けてくれました。
(最初からドキュメントちゃんと読めという話ですが、、)

助けてくれたドキュメント達 ( Role, FederatedPrincipal, PolicyDocument, PolicyStatement )

github_oicd_stack.py
GitHubOrgName = "example"
GitHubRepoName = "example"

conditions = {
    "StringEquals" : {
        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
    },
    "StringLike" : {
        "token.actions.githubusercontent.com:sub" : f"repo:{GitHubOrgName}/{GitHubRepoName}:*"
    }
}

bastion_role = iam.Role(self,
    assumed_by=iam.FederatedPrincipal(
        github_oidc_provider.open_id_connect_provider_arn,
        conditions=conditions,
        assume_role_action="sts:AssumeRoleWithWebIdentity"
    ),
    path="/",
    inline_policies={
        "AssumeRolePolicy": iam.PolicyDocument(
            statements=[
                iam.PolicyStatement(
                    effect=iam.Effect.ALLOW,
                    actions=["sts:GetCallerIdentity"],
                    resources=['*']
                 ),
                 iam.PolicyStatement(
                     effect=iam.Effect.ALLOW,
                     actions=[
                         "sts:AssumeRole",
                         "sts:TagSession"
                      ],
                      resources=["*"]
                 )
             ]
        )
    },
    id="BastionRole",
    role_name="BastionRole",
)

deploy_role(デプロイ用ロール)作成

最後のロール作成です。
ここが一番難しかったです。
一度に複数のStatementを設定したかったのですが、想定している設定がなかなかできなかったので、最初に1つ設定して後付けしています。

新たに助けてくれたドキュメント達 ( ArnPrincipal, PrincipalWithConditions )

github_oicd_stack.py
ExternalId = "example1234"

aws_principal = iam.ArnPrincipal(
    arn=bastion_role.role_arn,
)

# add condition
aws_principal_with_condition = iam.PrincipalWithConditions(aws_principal, {
    "StringEquals": {
        "sts:ExternalId": ExternalId
    }
})

github_actions_deploy_role = iam.Role(self,
    assumed_by=aws_principal_with_condition,
    id="GitHubActionsDeployRole",
    path="/",
    role_name="GitHubActionsDeployRole",
)

# add policy
github_actions_deploy_role.assume_role_policy.add_statements(iam.PolicyStatement(
    actions=["sts:TagSession"],
    principals=[
        aws_principal.grant_principal,
    ],
))

この様に書くと以下の様なポリシーになります。
完璧です。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::{AccoutID}:role/BastionRole"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "example1234"
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::{AccoutID}:role/BastionRole"
      },
      "Action": "sts:TagSession"
    }
  ]
}

完成系

github_oicd_stack.py
from aws_cdk import (
    aws_iam as iam,
    core,
)


class AwsCdkOicdStack(core.Stack):

    def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        ExternalId = "example1234"
        GitHubOrgName = "example"
        GitHubRepoName = "example"

        github_oidc_provider = iam.OpenIdConnectProvider(self,
            client_ids=["sts.amazonaws.com"],
            thumbprints=["a031c46782e6e6c662c2c87c76da9aa62ccabd8e"],
            id="GithubOicdProvider",
            url="https://token.actions.githubusercontent.com",
        )

        conditions = {
            "StringEquals" : {
                "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
            },
            "StringLike" : {
                "token.actions.githubusercontent.com:sub" : f"repo:{GitHubOrgName}/{GitHubRepoName}:*"
            }
        }

        bastion_role = iam.Role(self,
            assumed_by=iam.FederatedPrincipal(
                github_oidc_provider.open_id_connect_provider_arn,
                conditions=conditions,
                assume_role_action="sts:AssumeRoleWithWebIdentity"
            ),
            path="/",
            inline_policies={
                "AssumeRolePolicy": iam.PolicyDocument(
                    statements=[
                        iam.PolicyStatement(
                            effect=iam.Effect.ALLOW,
                            actions=["sts:GetCallerIdentity"],
                            resources=['*']
                        ),
                        iam.PolicyStatement(
                            effect=iam.Effect.ALLOW,
                            actions=[
                                "sts:AssumeRole",
                                "sts:TagSession"
                            ],
                            resources=["*"]
                        )
                    ]
                )
            },
            id="BastionRole",
            role_name="BastionRole",
        )

        aws_principal = iam.ArnPrincipal(
            arn=bastion_role.role_arn,
        )

        # add condition
        aws_principal_with_condition = iam.PrincipalWithConditions(aws_principal, {
            "StringEquals": {
                "sts:ExternalId": ExternalId
            }
        })

        github_actions_deploy_role = iam.Role(self,
            assumed_by=aws_principal_with_condition,
            id="GitHubActionsDeployRole",
            path="/",
            role_name="GitHubActionsDeployRole",
        )

        # add policy
        github_actions_deploy_role.assume_role_policy.add_statements(iam.PolicyStatement(
            actions=["sts:TagSession"],
            principals=[
                aws_principal.grant_principal,
            ],
        ))

デプロイ & 動作確認

では、実際にデプロイして動作確認してみます。
Github Actions Workflowはこちらを参照してください。

$ cdk deploy

無事、それぞれのroleでAWSアカウント情報を取得することができました。
スクリーンショット_2021-12-02_12_48_02.png

所感

個人的にはcfnよりもコーディングが楽しかったです。
今回初めてCDKを使って構築してみましたが、普段使用している言語だったためか、かなりスムーズに構築できたと思います。
cfnを初めて触った時はひどいもんでした。

みなさんも気分転換に一度CDKで構築してみてください。楽しいはずです!
以上!

12
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
1