GitHub Actions OIDC 배포 AccessDenied 복구 플레이북: AWS 권한 오류를 30분 안에 좁히는 방법
💡 Key Takeaways
- OIDC 장애는 워크플로 문법보다 IAM 신뢰 정책 조건 불일치에서 시작되는 경우가 많아 토큰 클레임과 조건식을 먼저 대조해야 합니다.
- 긴급 복구는 임시 자격증명 발급보다 영향 범위를 제한한 롤백 배포가 더 안전하며, 권한 수정은 검증 단계와 함께 진행해야 합니다.
- 재발 방지는 브랜치 규칙, 환경별 role 분리, 배포 전 sts 시뮬레이션 체크를 파이프라인에 고정하는 것이 핵심입니다.
GitHub Actions OIDC 배포 AccessDenied 복구 플레이북
야간 배포에서 가장 자주 사람을 멈칫하게 만드는 에러가 AccessDenied입니다. 특히 GitHub Actions OIDC를 쓰는 팀은 "어제까지 되던 배포가 오늘 갑자기 막혔다"는 상황을 꽤 자주 맞습니다. 지난 분기 우리 팀은 태그 릴리스 직전에 이 문제를 겪었습니다. aws-actions/configure-aws-credentials 단계는 통과했는데 실제 aws ecs update-service 호출에서 권한 오류가 터졌고, 운영 채널에는 "롤백도 같은 에러면 어떻게 하냐"는 질문이 쏟아졌습니다. 처음 10분은 액션 버전, 리전 변수, 시크릿 누락을 의심하며 로그만 뒤졌지만 진전이 없었습니다. 전환점은 "토큰 발급은 성공했는데 권한 위임이 실패한다"는 가설로 바꾼 뒤였습니다. OIDC 문제는 대체로 애플리케이션 코드가 아니라 IAM 신뢰 정책의 조건식과 GitHub 토큰 클레임(sub, aud) 불일치에서 시작됩니다. 그래서 저는 이런 장애가 나면 가장 먼저 에러 문구 전체를 복사해 실패한 API 이름과 리소스 ARN을 분리하고, 같은 시점의 워크플로 실행 컨텍스트(브랜치, 태그, 환경명)를 한 줄로 고정합니다. 이 한 줄 기준이 없으면 팀마다 서로 다른 로그를 보고 다른 원인을 말하게 됩니다.
판단 기준은 네 가지면 충분합니다. 첫째, AssumeRoleWithWebIdentity 자체가 실패했는지, 아니면 역할 획득 후 특정 서비스 권한에서 막혔는지 나눕니다. 둘째, IAM role trust policy의 token.actions.githubusercontent.com:sub 조건이 실제 실행 ref(refs/heads/main, refs/tags/v1.2.3)와 정확히 맞는지 확인합니다. 셋째, role policy에서 허용한 리소스 ARN이 현재 배포 대상과 일치하는지 봅니다. 저희는 ECS 클러스터명을 바꾼 뒤 ARN 패턴이 예전 값으로 남아 같은 에러를 재현했습니다. 넷째, 환경 분리 규칙을 확인합니다. production 환경에만 승인 규칙이 있는데 워크플로가 prod로 호출되면 OIDC 인증은 돼도 이후 단계가 예상과 다르게 동작합니다. 여기서 자주 하는 실수는 임시 액세스 키를 시크릿에 넣어 "일단 배포부터"를 선택하는 것입니다. 장애 하나는 넘길 수 있지만, 누가 언제 어떤 권한으로 배포했는지 감사 추적이 흐려지고 다음 장애에서 원인 추적이 더 어려워집니다. 우리 팀은 긴급 상황에서도 "정적 키 우회 금지"를 원칙으로 두고, 서비스 영향이 크면 먼저 직전 아티팩트 롤백을 수행한 뒤 OIDC 권한을 수정합니다.
실행 절차는 30분 기준으로 고정해두면 흔들리지 않습니다. 0~10분에는 사실 수집만 합니다. 실패 액션 스텝, AWS API 이름, role ARN, 실행 브랜치/태그를 표로 적고 재실행은 한 번만 허용합니다. 10~20분에는 신뢰 정책과 권한 정책을 분리 검증합니다. 저는 aws sts get-caller-identity와 대상 서비스의 읽기 API 하나를 먼저 호출해 "역할 획득 성공/실패"를 분기하고, 성공했다면 서비스별 최소 권한 누락을 확인합니다. 20~30분에는 복구 경로를 결정합니다. 고객 영향이 이미 커졌다면 권한 수정 배포보다 롤백 배포를 우선하고, 트래픽 안정 후 정책 변경을 적용합니다. 실제 사고 때도 이 순서로 진행해 배포 중단 시간을 27분으로 제한했습니다. 정책 수정 시에는 조건식을 넓히는 대신 정확히 필요한 ref만 추가하는 방식이 안전합니다. 예를 들어 repo:org/service:ref:refs/heads/main에 릴리스 태그 배포가 필요하면 refs/tags/v*를 명시적으로 추가하고, 모든 브랜치를 허용하는 와일드카드는 금지합니다. 수정 후에는 바로 본 배포를 누르지 말고 "권한 검증 전용 잡"을 먼저 돌려 실패 지점을 줄이는 습관이 필요합니다.
운영 체크리스트는 복구 이후가 핵심입니다. 첫째, role trust policy 변경 이력과 변경 사유를 PR 본문에 남깁니다. 둘째, 워크플로 입력값(environment, ref, aws-region)을 표준화하고 문자열 상수를 재사용해 오타를 줄입니다. 셋째, 배포 전 가드 잡에서 sts get-caller-identity와 핵심 리소스 읽기 권한 점검을 강제합니다. 넷째, 환경별 role을 완전히 분리해 스테이징 정책 완화가 프로덕션으로 번지지 않게 합니다. 다섯째, 온콜 런북에 "AccessDenied 발생 시 30분 의사결정 규칙"을 추가합니다. 마지막 주의사항은, 권한 이슈를 해결했다고 해서 배포 품질까지 보장되는 건 아니라는 점입니다. 권한 복구 직후엔 배포 후 검증(헬스체크, 오류율, 롤백 가능성)을 반드시 별도 단계로 두어야 합니다. 우리 팀은 이 사고 이후 배포 성공 메시지보다 "검증 성공 메시지"를 종료 기준으로 바꿨고, 그 뒤 동일 유형의 장애에서 회의 시간이 절반 가까이 줄었습니다.