- Published on
GitHub Actions OIDC assume-role 실패 7분 해결법
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
GitHub Actions에서 aws-actions/configure-aws-credentials로 OIDC 기반 AssumeRoleWithWebIdentity를 쓰면, 장기 Access Key 없이도 안전하게 AWS 권한을 위임할 수 있습니다. 하지만 실제로는 AccessDenied, No OpenIDConnect provider found, InvalidIdentityToken, Not authorized to perform sts:AssumeRoleWithWebIdentity 같은 오류로 파이프라인이 멈추는 일이 흔합니다.
이 글은 “원인 분석을 길게” 하기보다, 실패한 assume-role을 7분 안에 복구하는 데 초점을 맞춥니다. 특히 아래 4가지만 빠르게 점검하면 대부분 해결됩니다.
- 워크플로우
permissions에id-token: write가 있는가 - AWS IAM Role Trust Policy의
Principal/Federated,aud,sub조건이 맞는가 - AWS 계정에 GitHub OIDC Provider가 올바른 URL/Thumbprint/Audience로 등록되어 있는가
- 실제로 어떤
sub클레임으로 토큰이 발급되는지(브랜치/태그/환경)와 Trust 조건이 일치하는가
추가로, OIDC가 403/권한거부로 보일 때의 케이스는 별도 글로 더 깊게 정리해두었습니다: GitHub Actions OIDC 403·권한거부 원인 7가지
0. 증상별로 먼저 분류하기(30초)
실패 메시지에 따라 “어디를 먼저 볼지”가 달라집니다.
Error: Not authorized to perform sts:AssumeRoleWithWebIdentity- 99%: Role trust policy 조건 불일치(sub/aud) 또는 OIDC provider ARN 불일치
No OpenIDConnect provider found in your account for https://token.actions.githubusercontent.com- AWS IAM OIDC Provider 미등록/오등록
InvalidIdentityToken또는audience is invalid- aud 클레임 불일치(대개
sts.amazonaws.com아닌 값으로 조건을 걸어둠)
- aud 클레임 불일치(대개
Could not load credentials from any providers- OIDC 이전 단계에서 실패(permissions, action 버전, job 조건) 가능
이제부터는 실제로 7분 안에 끝내는 순서로 진행합니다.
1. 1분: workflow permissions에 id-token이 있는지 확인
OIDC는 GitHub가 워크플로우에 JWT를 발급해주는 기능이고, 이를 받으려면 Job 또는 Workflow 레벨에 다음 권한이 필요합니다.
name: deploy
on:
push:
branches: [ main ]
permissions:
id-token: write # OIDC 토큰 발급에 필수
contents: read # checkout에 보통 필요
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/gha-deploy
aws-region: ap-northeast-2
체크 포인트
- 레포지토리 Settings → Actions → General에서 “Workflow permissions”가
Read repository contents permission으로 제한되어 있어도, 위처럼 명시하면 대개 해결됩니다. pull_request이벤트에서 포크 PR은 보안상id-token이 제한될 수 있습니다. 이 경우pull_request_target사용 여부, 환경 보호 규칙, 별도 배포 전략을 검토해야 합니다.
2. 2분: AWS OIDC Provider 등록 상태를 CLI로 검증
AWS 계정에 GitHub OIDC Provider가 있어야 Principal.Federated로 신뢰를 맺을 수 있습니다.
2.1 Provider 존재 여부
aws iam list-open-id-connect-providers \
--query 'OpenIDConnectProviderList[*].Arn' \
--output text
출력에 아래 형태가 있어야 합니다.
arn:aws:iam::<ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com
없다면 생성합니다.
2.2 Provider 생성(없을 때)
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1
주의
- Thumbprint는 문서/예시를 그대로 쓰기보다, 조직 보안 기준에 맞춰 최신 값을 확인하는 게 좋습니다. 다만 실제 장애 상황에서는 “Provider 자체가 없어서” 실패하는 경우가 많고, 위 명령으로 즉시 복구되는 케이스가 흔합니다.
3. 2분: IAM Role Trust Policy의 핵심 3요소(Principal/aud/sub)
assume-role 실패의 대부분은 Trust Policy 조건이 실제 토큰 클레임과 다르기 때문입니다.
3.1 올바른 Trust Policy 예시(가장 무난한 형태)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:OWNER/REPO:ref:refs/heads/main"
}
}
}
]
}
여기서 자주 틀리는 지점은 다음입니다.
Principal.FederatedARN이 다른 계정/다른 provider를 가리킴aud를sts.amazonaws.com이 아닌 값으로 제한sub를 브랜치가 아닌 태그/환경/PR 형태로 제한해놓고 실제 실행 컨텍스트가 다름
3.2 sub 패턴을 “너무 타이트하게” 잡아 생기는 장애
예를 들어 릴리즈 태그에서 배포하는데 Trust Policy가 main 브랜치만 허용하면 실패합니다.
- 브랜치 실행:
repo:OWNER/REPO:ref:refs/heads/main - 태그 실행:
repo:OWNER/REPO:ref:refs/tags/v1.2.3 - 환경(environment) 보호 사용 시:
repo:OWNER/REPO:environment:prod
운영에서는 보안을 위해 타이트하게 잡는 게 맞지만, 장애 상황에서 원인 파악을 위해 일시적으로 sub 조건을 넓혀 확인하는 방법이 유효합니다.
예시(임시 진단용: 레포 단위로만 제한)
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:OWNER/REPO:*"
}
원인 확인 후에는 다시 브랜치/태그/환경 단위로 좁히는 것을 권장합니다.
4. 1분: 실제 OIDC 토큰 클레임을 출력해 sub/aud를 확정
가장 빠른 해결법은 “추측”이 아니라 “실제 토큰의 sub/aud를 보고 Trust Policy를 맞추는 것”입니다.
GitHub Actions에서 OIDC 토큰은 ACTIONS_ID_TOKEN_REQUEST_URL로 받아볼 수 있습니다. 다음은 진단용으로 페이로드를 출력하는 예시입니다.
- name: Dump OIDC token claims (diagnostic)
shell: bash
run: |
set -euo pipefail
echo "Requesting OIDC token..."
raw=$(curl -sS -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com")
token=$(python - <<'PY'
import json,sys
print(json.load(sys.stdin)["value"])
PY
<<< "$raw")
echo "$token" | awk -F'.' '{print $2}' | tr '_-' '/+' | base64 -d 2>/dev/null | python -m json.tool
출력된 JSON에서 최소한 아래를 확인합니다.
aud값이 무엇인지 (sts.amazonaws.com인지)sub가 어떤 형태인지 (브랜치/태그/환경)repository,ref,job_workflow_ref등 추가 힌트
이 값을 기준으로 Trust Policy의 StringEquals/StringLike를 정확히 수정하면 재발 확률이 크게 줄어듭니다.
참고로, 위 스크립트처럼 set -euo pipefail을 쓰면 진단 단계에서 변수 미정의로 스텝이 바로 죽을 수 있습니다. 파이프라인 안정성 관점에서의 함정은 이 글이 도움이 됩니다: bash set -euo pipefail 함정과 안전한 예외처리
5. 자주 터지는 ‘의외의’ 원인 5가지(현장 체크리스트)
5.1 액션 버전/입력값 문제
aws-actions/configure-aws-credentials@v4 사용을 권장합니다. 구버전은 OIDC 처리/로그가 부족해 원인 파악이 더 어렵습니다.
또한 role-to-assume ARN 오타(계정 ID, role 이름, path 포함 여부)가 생각보다 많습니다.
5.2 role session name 충돌/추적성 부족
실패 자체의 원인은 아니지만, CloudTrail에서 추적이 어려워 원인 분석이 길어집니다.
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/gha-deploy
role-session-name: gha-${{ github.run_id }}-${{ github.job }}
aws-region: ap-northeast-2
5.3 조직/레포 이름 변경 후 sub 불일치
레포를 이관하거나 이름을 바꾸면 sub의 repo:OWNER/REPO가 바뀌어 즉시 실패합니다. 특히 모노레포/조직 이관 직후 장애가 잦습니다.
5.4 environment 보호 규칙 도입 후 sub 형태 변경
environment: prod 같은 보호 규칙을 붙이면, Trust Policy가 ref:refs/heads/main만 허용할 때 실패할 수 있습니다. 배포 Job이 실제로 환경 컨텍스트로 토큰을 받는지 확인하고, 필요하면 sub 조건을 environment 형태로 맞추세요.
5.5 캐시/빌드 산출물 문제로 OIDC처럼 보이는 실패
간혹 assume-role 실패가 아니라, 앞 단계 빌드가 꼬여서 “자격 증명 설정 단계가 실행되지 않거나” 잘못된 바이너리를 참조해 엉뚱한 오류로 보이기도 합니다. 캐시 충돌로 워크플로우가 비정상 상태일 때는 이 전략이 유용합니다: GitHub Actions 캐시 충돌 시 빌드 완전 초기화 전략
6. 최종 점검: 7분 복구용 ‘정답 템플릿’
마지막으로, 가장 재현이 잘 되는 구성(레포 main 브랜치만 허용)을 한 번에 정리합니다.
6.1 GitHub Actions workflow
name: aws-oidc-test
on:
push:
branches: [ main ]
permissions:
id-token: write
contents: read
jobs:
sts:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/gha-deploy
aws-region: ap-northeast-2
- name: Verify caller identity
run: aws sts get-caller-identity
6.2 IAM Role Trust Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:OWNER/REPO:ref:refs/heads/main"
}
}
}
]
}
여기까지 맞춰도 실패한다면, 남는 가능성은 크게 두 가지입니다.
- 실행 이벤트가 생각과 다르다(태그/PR/환경 컨텍스트)
- provider/role이 다른 AWS 계정/리전에 섞여 있다(특히 멀티계정에서 ARN 혼동)
이 경우에는 4절의 “토큰 클레임 덤프”로 sub/aud를 확정하고, CloudTrail에서 AssumeRoleWithWebIdentity 이벤트를 찾아 거부 사유(조건 불일치)를 교차 확인하면 됩니다.
결론
GitHub Actions OIDC assume-role 실패는 겉보기엔 복잡하지만, 실제로는 permissions → provider → trust policy(aud/sub) 순서로 확인하면 대부분 7분 안에 복구됩니다. 특히 sub는 브랜치/태그/환경에 따라 바뀌므로, 장애 상황에서는 “실제 토큰 클레임을 출력해서” 신뢰 정책을 맞추는 것이 가장 빠르고 확실합니다.
다음 단계로는, 운영 배포에서는 repo:*처럼 넓은 조건을 피하고, 브랜치/태그/환경을 명시적으로 제한하며, CloudTrail 추적을 위해 role-session-name을 규칙적으로 부여하는 것을 권장합니다.