CircleCIとAWSのOIDC連携で特定のProjectやUserにのみAssumeRoleを許可させてみた

2022.03.30

こんにちは、CX事業本部 IoT事業部の若槻です。

前回のエントリでは、CircleCIAWSをOIDC連携させて、永続的な認証情報を使用しないセキュアなAssumeRoleを試してみました。

今回は、CircleCIとAWSのOIDC連携でさらにセキュリティを高める対応として、特定のCircle CI Project(GitHub Repository)やUserにのみAssumeRoleを許可する設定を試してみました。

やってみた

同じCircleCI Organization(GitHub Account)に属するProject(Repository)であるrepository_arepository_bで試してみます。

準備

まず準備として、ProjectおよびUserの制限なくOIDCによるAssumeRoleが可能なOIDC設定を構築します。(前回のエントリとほぼ同じ構築内容です)

Circle CIのOrganization SettingsにOIDCに使用するContextsを作成します。Organization IDを控えます。

AWSには上記Contextsに対応したID Providerを作成します。

またAssumeRole用のIAM Roleを作成済します。

RoleのTrust Policyは次のように設定します。特定のOrganizationのID ProviderとのみFederation可能としていますが、ProjectおよびUserの制限はしていません。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "<id-providor-arn>"
      },
      "Action": "sts:AssumeRoleWithWebIdentity"
    }
  ]
}

Contexts内に、AssumeRoleに必要なEnvironment Variablesとして、AWS_ROLE_ARNAWS_DEFAULT_REGIONを作成します。

それぞれのRepositoryに次のCircleCI Config(.circleci/config.yml)を作成します。

.circleci/config.yml

version: 2.1

executors:
  node:
    docker:
      - image: circleci/node # deprecated

orbs:
  aws-cli: circleci/aws-cli@2.1.0

jobs:
  assume:
    executor: node
    steps:
      - checkout
      - aws-cli/install
      - run:
          name: Assume role
          command: |
            aws_sts_credentials=$(aws sts assume-role-with-web-identity \
              --role-arn ${AWS_ROLE_ARN} \
              --web-identity-token ${CIRCLE_OIDC_TOKEN} \
              --role-session-name "circleci-oidc" \
              --duration-seconds 900 \
              --query "Credentials" \
              --output "json")
            echo export AWS_ACCESS_KEY_ID="$(echo $aws_sts_credentials | jq -r '.AccessKeyId')" >> $BASH_ENV
            echo export AWS_SECRET_ACCESS_KEY="$(echo $aws_sts_credentials | jq -r '.SecretAccessKey')" >> $BASH_ENV
            echo export AWS_SESSION_TOKEN="$(echo $aws_sts_credentials | jq -r '.SessionToken')" >> $BASH_ENV
            source $BASH_ENV
      - run: 
          name: Some deploy
          command: aws sts get-caller-identity

workflows:
  version: 2
  release:
    jobs:
      - assume:
          context: aws-deploy

IAM Roleで何も制限を掛けなければ、同じOrganization(GitHub Account)内のProject間でContextを共有できるため、ContextからOIDC用のIAM Roleを取得してAssumeRoleが可能となります。

それぞれのRepositoryでWorkflowを実行すると、AssumeRoleは正常に行えています。

Project単位で許可する場合

それでは、まずProject単位でのOIDCによるAssumeRoleを許可してみます。

CircleCIのダッシュボードで、AssumeRoleを許可したいProject(ここではrepository_a)のProject IDを控えます。

IAM RoleのTrust Policyを次のように編集します。先程控えたOrganization IDとProject IDを指定します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "<id-providor-arn>"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringLike": {
          "oidc.circleci.com/org/<organization-id>:sub": [
            "org/<organization-id>/project/<project-id>/user/*"
          ]
        }
      }
    }
  ]
}

repository_aのWorkflowを実行すると、AssumeRoleは正常に行えました。

一方、repository_bのWorkflowを実行すると、AssumeRoleはAccessDeniedエラーとなり失敗しました。Project単位での許可設定がちゃんとできているようです。

An error occurred (AccessDenied) when calling the AssumeRoleWithWebIdentity operation: Not authorized to perform sts:AssumeRoleWithWebIdentity

User単位で許可する場合

続いて、User単位でのOIDCによるAssumeRoleを許可してみます。

CircleCIのダッシュボードで、AssumeRoleを許可したいUser(ここではcm-rwakatsuki)のUser IDを控えます。

IAM RoleのTrust Policyを次のように編集します。subの末尾の*をUser IDに置き換えます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "<id-providor-arn>"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringLike": {
          "oidc.circleci.com/org/<organization-id>:sub": [
            "org/<organization-id>/project/<project-id>/user/<user-id>"
          ]
        }
      }
    }
  ]
}

許可されたユーザーでWorkflowを実行すると、AssumeRoleは正常に行えました。

一方、許可されていないユーザーでWorkflowを実行すると、AssumeRoleはAccessDeniedエラーとなり失敗しました。User単位での許可設定がちゃんとできているようです。

An error occurred (AccessDenied) when calling the AssumeRoleWithWebIdentity operation: Not authorized to perform sts:AssumeRoleWithWebIdentity

おわりに

CircleCIとAWSのOIDC連携で特定のProjectやUserにのみAssumeRoleを許可させてみました。

ProjectおよびUserの両方が難しければどちらか一方だけでも良いと思います。さらなるセキュリティ向上のために今回の許可設定は是非とも行っておきたいところです。

参考

以上