[アップデート] AWS STSがOIDCフェデレーションでGitHub・Google・CircleCI・OCIのプロバイダー固有クレーム検証をサポートしました
はじめに
皆様こんにちは、あかいけです。
GitHub ActionsからAWSリソースにアクセスする際、OIDCフェデレーションを使っている方は多いと思います。
ただ、信頼ポリシーでsubクレームに頼った制御をしていると、「このリポジトリのこのブランチのこのEnvironmentだけ許可したい」みたいな複合条件が割とつらいですよね。
そんな中、AWS STSのアップデートがあり、OIDCフェデレーションにおけるIdP固有のクレーム検証がサポートされました。
今回のアップデートにより、
GitHub、Google、CircleCI、Oracle Cloud Infrastructure(OCI)のプロバイダー固有クレーム がIAMロール信頼ポリシーやリソースコントロールポリシーの条件キーとして使えるようになりました。
特にGitHub Actionsを使ったCI/CDパイプラインからAWSリソースにアクセスしている方にとっては、嬉しいアップデートかと思います。
というわけで今回はGitHubを例に、従来の設定方法と今回のアップデートで可能になった設定方法を比較しながら、具体的なポリシー例を交えて紹介します。
OIDCフェデレーションのおさらい
まずは前提知識として、OIDCフェデレーションの仕組みを簡単におさらいしておきます。
OIDCフェデレーションの一般的な仕組み
OIDCフェデレーションは、外部のIDプロバイダー(IdP)が発行するトークン(JWT)を使って、別のサービスに対して「自分が何者か」を証明する仕組みです。
JWTの中にはクレームと呼ばれる属性情報が含まれており、トークンを受け取る側はこのクレームを検証してアクセスを許可するかどうかを判断します。
AWSの場合
AWSではOIDCフェデレーションを利用する際、AWS STSのAssumeRoleWithWebIdentity APIを使います。
このAPIにIdPが発行したJWTを渡すと、AWS STSがトークンを検証し、IAMロールの信頼ポリシーの条件と照合した上で、一時的なAWS認証情報を発行します。
GitHub Actionsの場合を例にすると、以下のような流れです。
-
- GitHub Actionsのワークフローが実行される
-
- GitHubがOIDCトークン(JWT)を発行する
-
- ワークフローがそのトークンを使って
AssumeRoleWithWebIdentityを呼び出す
- ワークフローがそのトークンを使って
-
- AWS STSがトークンを検証し、IAMロールの信頼ポリシーの条件と照合する
-
- 条件を満たしていれば一時的な認証情報が発行される
上記4の「信頼ポリシーの条件と照合する」部分で、JWTのクレームを条件キーとして使えるわけですが、今回のアップデートでこの条件キーが大幅に増えました。
従来の条件キー
従来はGitHub固有のものは特に用意されておらず、
OIDCフェデレーションの信頼ポリシーで使用できたの条件キーは以下の標準的なクレームに限定されていました。
| AWS STS条件キー | IdPのJWTクレーム | 説明 |
|---|---|---|
aud |
azp(未設定時はaud) |
トークンの対象者 |
sub |
sub |
サブジェクト(主体の識別子) |
amr |
amr |
認証方法リファレンス |
oaud |
aud |
元のオーディエンス |
email |
email |
メールアドレス |
従来のGitHub Actionsの信頼ポリシー例
GitHub Actionsからのアクセスを制御するには、主にsubクレームを使っていました。
{
"Version":"2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:org-name/repo-name:ref:refs/heads/demo"
}
}
}
]
}
この方法でも「特定のリポジトリのmainブランチからのみ」という制御は可能ですが、subクレームは以下のような書式で情報が詰め込まれた文字列です。
repo:{owner}/{repo}:ref:refs/heads/{branch}
repo:{owner}/{repo}:environment:{environment_name}
repo:{owner}/{repo}:pull_request
このようにsubの値はコンテキストによってフォーマットが変わります。
また、IAMポリシーのConditionブロック内では同じ条件キーを複数回指定することはできません。
そのため、「特定のブランチかつ特定のEnvironment」といった複合条件をsubだけで表現することができませんでした。
結果として、以下のようにStringLikeでのワイルドカードマッチに頼らざるを得ない場面がありました。
{
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"
}
}
}
これだとブランチやEnvironmentによる細かい制御が難しく、意図しないワークフローからのアクセスを許可してしまうリスクがありました。
新しい条件キー
今回のアップデートにより、プロバイダー固有のクレームを個別の条件キーとして使えるようになりました。
以下は追加された条件キーの一覧です。
GitHub Actionsで追加された条件キー
| 条件キー | 説明 |
|---|---|
actor |
ワークフローを起動した個人アカウント |
actor_id |
個人アカウントの不変ID |
job_workflow_ref |
再利用可能ワークフローの参照パス |
repository |
ワークフローが実行されているリポジトリ |
repository_id |
リポジトリの不変ID |
workflow |
ワークフロー名 |
ref |
Gitリファレンス(ブランチ/タグ) |
environment |
ジョブのEnvironment名 |
enterprise_id |
リポジトリを含むEnterprise ID |
Googleで追加された条件キー
| 条件キー | 説明 |
|---|---|
organization_number |
Google Cloud/Workspaceの組織番号 |
CircleCIで追加された条件キー
| 条件キー | 説明 |
|---|---|
project_id |
CircleCIプロジェクトのUUID |
OCIで追加された条件キー
| 条件キー | 説明 |
|---|---|
rpst_id |
Resource Principal Session Token ID |
subは引き続き必須
OIDCプロバイダーをPrincipalに指定する信頼ポリシーでは、sub条件キーの評価が引き続き必須のようです。
実際にtoken.actions.githubusercontent.com:subを信頼ポリシーに指定しないでIAMロールを作成しようとすると、以下のエラーで作成ができませんでした。
なのでsubをStringEquals、StringLike、StringEqualsIgnoreCaseのいずれかで評価する必要がありました。
Trust policy with trusted principal arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com must evaluate, using StringEquals, StringLike or StringEqualsIgnoreCase, token.actions.githubusercontent.com:sub which is not scoped to all.
そのため、新しい条件キーはsubを置き換えるものではなく、subに追加してより細かい制御を実現するもののようです。
なお、Conditionブロック内に複数の条件演算子(StringEqualsとStringLikeなど)がある場合、すべてAND条件で評価されます。
つまりsubのワイルドカードマッチに加えて、repositoryやenvironmentなどの条件キーもすべて満たす必要があるので、結果的に従来より細かいアクセス制御が実現できます。
新しいGitHub Actionsの信頼ポリシー例
では、新しい条件キーを使った信頼ポリシーを見てみましょう。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:repository": "my-org/my-repo",
"token.actions.githubusercontent.com:ref": "refs/heads/main",
"token.actions.githubusercontent.com:environment": "production"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"
}
}
}
]
}
従来はsubクレームにまとめて詰め込まれていた情報を、個別の条件キーとして指定できるようになりました。
これにより「特定のリポジトリ」かつ「mainブランチ」かつ「productionのEnvironment」といった複合条件を表現できます。
不変のIDを指定する例
不変のIDを使うことで、リポジトリ名やアカウント名の変更に左右されない、より堅牢なポリシーを書くこともできます。
repository(文字列)ではなくrepository_id(数値ID)を使うことで、リポジトリがリネームされても条件が壊れません。
同様にactor_idを使えば、GitHubユーザー名の変更にも対応できます。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:repository_id": "123456789",
"token.actions.githubusercontent.com:actor_id": "123456789"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"
}
}
}
]
}
特定のワークフローを指定する例
job_workflow_refを使えば、特定の再利用可能ワークフロー経由でのみアクセスを許可するといった制御も可能です。
これにより、組織で管理しているデプロイ用の共通ワークフロー経由でのみAWSへのアクセスを許可するといった運用が実現できます。
例えばセキュリティチームが管理するワークフローを経由しないデプロイを防ぐ、といったことができるのでガバナンスの強化にも役立ちそうですね。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:job_workflow_ref": "my-org/shared-workflows/.github/workflows/deploy.yml@refs/heads/main"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/*"
}
}
}
]
}
Enterprise単位での制御例
GitHub Enterprise Cloudを利用している場合は、enterprise_idを使ってEnterprise全体のリポジトリからのアクセスを許可しつつ、外部からのアクセスを防ぐ設定もできます。
個別のリポジトリ名を列挙しなくても、Enterprise配下のリポジトリからのアクセスをまとめて許可できるので、リポジトリが増えるたびに信頼ポリシーを更新する必要がなくなります。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:enterprise_id": "345"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/*"
}
}
}
]
}
Terraformでの設定例
ここからは、Terraformで実際にOIDCプロバイダーとIAMロールを設定する例を紹介します。
従来の設定
# OIDC Provider
resource "aws_iam_openid_connect_provider" "github_actions" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
}
# IAM Role
resource "aws_iam_role" "github_actions_deploy" {
name = "github-actions-deploy"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github_actions.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
"token.actions.githubusercontent.com:sub" = "repo:my-org/my-repo:ref:refs/heads/main"
}
}
}
]
})
}
アップデート後の設定
前述の通りsubは引き続き必須なので、新しい条件キーをsubに追加する形になります。
各条件が何を制限しているのか一目でわかるようになりましたね。
# OIDC Provider
resource "aws_iam_openid_connect_provider" "github_actions" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
}
# IAM Role
resource "aws_iam_role" "github_actions_deploy" {
name = "github-actions-deploy"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github_actions.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
"token.actions.githubusercontent.com:repository" = "my-org/my-repo"
"token.actions.githubusercontent.com:ref" = "refs/heads/main"
"token.actions.githubusercontent.com:environment" = "production"
}
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:my-org/my-repo:*"
}
}
}
]
})
}
さいごに
以上、AWS STSのOIDCフェデレーションにおけるIdP固有クレーム検証サポートについてまとめました。
従来はsubクレームに全部詰め込まれていた情報を個別の条件キーとして使えるようになったことで、ポリシーの可読性や保守性が向上したと思います。
特にGitHub Actionsを使ったCI/CDパイプラインを運用している組織にとっては、セキュリティ面でも運用面でも嬉しいアップデートですね。
これを機に既存のOIDCフェデレーション設定を見直して、より細かい条件に切り替えるいい機会かもしれません。
皆様のCI/CDパイプラインのセキュリティ向上にお役立ていただければ幸いです。






