はじめに
GitHub Actions から Terraform で AWS を操作する CD を、長期キーを置かず OIDC で組みました。このとき1つやりたかったのが、
承認された“本番向けの実行”だけが、本番を変更できるロールを引き受けられる
という形です。これを、GitHub 側の承認と AWS 側のロール引き受け条件を、environment という共通の境界で連動させて実現できたので、その設計をまとめます。
先に結論:OIDC トークンの sub(subject)クレームは、ジョブに environment: を付けると形が変わります。その sub を AWS の IAM 信頼ポリシーで要求しておき、さらに environment 側に承認者を置くと、「承認を通った prod 実行だけが apply ロールを assume できる」という流れに寄せられます。
正直に書いておくと、私は最初から
environment: prodを付けて運用しており、「外したら認証が拒否される」エラーを実際に踏んだわけではありません。以下は、公式仕様と実際の信頼ポリシーから導いた設計上の帰結として理解している内容です。
環境
- GitHub Actions(OIDC /
id-token: write) - Terraform(AWS プロバイダ)で
applyを実行 - AWS IAM OIDC プロバイダ + apply 用 IAM ロール
- リージョン: ap-northeast-1(東京)
狙い:認証条件と承認ゲートを environment で結び付ける
CD で本番を触るロールは、できるだけ「誰でも・いつでも」引き受けられないようにしたい。そこで2つの制御を、environment という接点でつなぎました(別々の仕組みであることは意識しつつ)。
- AWS 側(認証・認可境界):OIDC トークンと IAM 信頼ポリシーで「この実行を信用してよいか」を判定
- GitHub 側(承認ゲート):Environment の Required reviewers で「ジョブを開始してよいか」を制御
この2つの“接点”になるのが environment: prod です。
仕組み①:sub は environment 指定で変わる
GitHub の OIDC トークンの sub は、ジョブの実行文脈で形が変わります(GitHub 公式の例より)。
| ジョブの指定 |
sub の形 |
|---|---|
| ブランチ実行 | repo:<owner>/<repo>:ref:refs/heads/main |
| プルリクエスト | repo:<owner>/<repo>:pull_request |
environment: prod |
repo:<owner>/<repo>:environment:prod |
つまり、ジョブに environment: prod を付けて初めて、sub が ...:environment:prod になります。
ワークフロー側はこうです(抜粋)。
jobs:
provision-and-install:
runs-on: ubuntu-latest
environment: prod # ★これで sub が ...:environment:prod になる
permissions:
id-token: write
contents: read
補足:
subの形式は、組織やリポジトリで subject claim をカスタマイズしていると標準形と変わることがあります。カスタマイズしている場合は、実際のsubの値を確認して信頼ポリシーを合わせる必要があります。上の表はカスタマイズしていない、現行の標準形です。
仕組み②:信頼ポリシーでその sub だけを許す
AWS 側、apply ロールの信頼ポリシーは、sub が ...:environment:prod のときだけ assume を許すようにしてあります(抜粋)。
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:aud"
values = ["sts.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:sub"
values = ["repo:<owner>/<repo>:environment:prod"]
}
ここがポイントで、もしジョブから environment: prod を外すと、sub は ...:ref:refs/heads/main のような別の形になります。すると信頼ポリシーの StringEquals に一致しなくなるので、仕様上は sts:AssumeRoleWithWebIdentity が通りません。**「環境を通っていない実行は、本番ロールを引き受けられない」**が、AWS の信頼ポリシー側で機械的に担保される、という構図です。
(読み取り用の plan ロールは別に用意し、sub を pull_request と ref:refs/heads/main に限定して権限は ReadOnly にしています。「PR では読むだけ・本番変更は prod 環境から」という住み分けです。)
承認ゲートも environment に乗せる
environment: prod には、もう1つ乗せられるものがあります。GitHub の Environment には Required reviewers(承認者)を設定でき、設定した場合、ジョブはその承認が下りるまで開始されません。
- Required reviewers の保護ルールを満たすまで、その Environment を参照するジョブは開始されない
- そのため、通常の workflow 構成では OIDC トークン取得や AWS ロール引き受けの step も実行されない
- 承認後に初めて、apply ロールを assume して
terraform apply
ここは条件付きで、prod Environment に Required reviewers を設定している場合に承認ゲートが効きます(Environment は承認者なしでも使えて、その場合 sub は ...:environment:prod になりますが、人間の承認は挟まりません)。Required reviewers を設定した prod Environment を経由させることで、承認待ちのジョブと、apply ロールを取れるジョブを、実質的に同じ境界へ寄せられました。
補足:environment だけではブランチ制限にならない
注意点として、environment:prod の sub は標準形ではブランチ名を含みません。つまり信頼ポリシーで Environment を見ていても、それだけでは「main からの apply だけ」にはなりません。apply を main に限定したいなら、
- GitHub Environment の deployment branch rules で main を許可対象にする
- または workflow の
on/ ジョブのifで main を制限する
のどちらか(または両方)を別途明示します。ブランチ制御は Environment 側か workflow 側で行う、と切り分けて考えるのが安全でした。
学び
- OIDC の
subは ジョブの実行文脈で形が変わる。environment:を付けると...:environment:<name>になる(標準形・GitHub 公式の例で確認)。組織/リポジトリでカスタマイズしていると変わる。 - AWS 側の信頼ポリシーで
subを...:environment:prodに限定すると、その環境を参照した実行だけがそのロールを assume できる。 - GitHub Environment に Required reviewers を設定すると、承認ゲートを同じ
environmentに乗せられる(設定した場合に限る)。 -
environment:prodだけではブランチ制限にならない。main 限定は deployment branch rules か workflow 側で別途。 - 権限は用途で分ける:plan は ReadOnly(PR / main)、apply は変更可(environment:prod)。
おわりに
「鍵を置かない」だけでなく、「承認された実行しか本番を触れない」までを、environment という1つの境界に寄せられたのが、設計していて一番すっきりした部分でした😊 OIDC の sub が文脈で変わる、という小さな事実が効いています。同じく CD の権限設計で迷っている人の参考になれば嬉しいです🙌
参考
- GitHub公式: About security hardening with OpenID Connect(example subject claims・
subの形)
https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect - GitHub公式: OpenID Connect reference(subject claim の構成・カスタマイズ)
https://docs.github.com/actions/reference/openid-connect-reference - GitHub公式: Managing environments for deployment(Required reviewers / deployment branches)
https://docs.github.com/en/actions/deployment/targeting-different-environments/managing-environments-for-deployment - AWS公式: OIDC ID プロバイダと IAM ロール(
sts:AssumeRoleWithWebIdentity)
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html