
클라우드 인프라를 구축하고 CI/CD 파이프라인을 다루다 보면 반드시 마주치게 되는 기술이 바로 OIDC(OpenID Connect)이다.
"GitHub Actions에서 AWS 배포할 때 Access Key 쓰지 말고 OIDC 쓰세요"라는 말은 많이 들어봤지만, 막상 코드를 작성하다 보면 여러 가지 의문이 생긴다.
"OIDC는 누가 발급하나?"
"AWS에 접속도 못 했는데, OIDC 신분증은 어떻게 받아오는 거지?"
"GitHub에서 OIDC 발급받는 로직도 테라폼 코드로 짜야 하나?"
최근 프로젝트 인프라를 구축하며 직접 겪었던 트러블슈팅 경험과 함께, OIDC의 핵심 동작 원리와 적용 팁을 알기 쉽게 정리해 보았다.
과거에는 GitHub Actions에서 AWS로 배포하기 위해 다음과 같은 방식을 사용했다.
IAM User를 생성한다.Access Key ID와 Secret Access Key를 발급받는다.Settings > Secrets에 이 키들을 복사해 넣는다.이 모든 문제를 깔끔하게 해결해 주는 최신 보안 표준이 바로 OIDC(OpenID Connect)를 통한 Keyless 인증입니다.
OIDC(OpenID Connect)는 쉽게 말해 외부 시스템(GitHub)이 AWS에게 신원을 증명하는 1회용 모바일 전자 신분증이다.
나는 OIDC는 AWS에서 발급해주는줄 알았다.
그래서 "AWS에 접속을 못하는데 OIDC 신분증은 어떻게 발급받지?"
라고 생각했다.
하지만 아니었다.
"OIDC 신분증은 AWS가 아니라 'GitHub'이 직접 발급한다"
신분증을 발급받는 순간에는 AWS에 접속할 필요가 전혀 없었다.
[1. GitHub 내부 서버] ──(1회용 신분증 발급)──▶ [2. Actions 러너] ──(신분증 제시)──▶ [3. AWS STS]
(구청) (여행객) (입국심사대)
Fundit-Infra 저장소의 main 브랜치에서 실행 중인 프로세스임"이라는 내용과 함께 GitHub의 전자 직인(디지털 서명)이 찍혀 있다.AWS와 GitHub 사이에는 비대칭 암호화(공개키/개인키) 기반의 신뢰 관계가 설정되어 있다.
결론부터 말하면 아니다. OIDC 발급기는 GitHub 플랫폼에 이미 탑재(Built-in)되어 있다.
GitHub Actions 워크플로우 YAML 파일에서 딱 두 가지만 적어주면 끝난다.
# 1. GitHub에게 OIDC 신분증 발급 권한 요청
permissions:
id-token: write
contents: read
steps:
# 2. 공식 액션이 GitHub 내부에서 토큰을 받아와 AWS STS로 교환
- name: Configure AWS Credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/my-terraform-ci-role
aws-region: ap-northeast-2
테라폼 코드는 GitHub을 건드리는 게 아니라, AWS 쪽에 수신 창구를 만드는 역할을 한다.
# 1. AWS에 GitHub을 공인 인증기관으로 등록
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = [data.tls_certificate.github.certificates[0].sha1_fingerprint]
}
# 2. 심사 기준 설정: 어떤 신분증을 가진 프로세스에 권한을 줄 것인가?
resource "aws_iam_role" "terraform_ci" {
name = "my-terraform-ci-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
# 특정 저장소, 특정 브랜치/환경만 허용 (최소 권한 원칙)
"token.actions.githubusercontent.com:sub" = [
"repo:my-org/my-repo:pull_request",
"repo:my-org/my-repo:ref:refs/heads/main"
]
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
}
}]
})
}
dev-apply 승인 게이트와 OIDC sub 클레임 에러프로젝트에서 CD 파이프라인을 구축할 때, 코드가 main에 머지되었다고 무조건 terraform apply가 실행되면 위험하다고 생각하여 관리자의 수동 승인을 거치도록 GitHub Environment Protection Rule(dev-apply)을 적용했다.
그런데 승인 버튼을 누르고 배포가 돌자마자 Access Denied (AssumeRoleWithWebIdentity failed) 에러가 발생했다.
GitHub Actions는 워크플로우에 environment:가 지정되면, 신분증의 sub 클레임 내용을 브랜치 이름이 아니라 환경 이름으로 바꿔서 발행한다.
sub: repo:my-org/my-repo:ref:refs/heads/mainenvironment: dev-apply 실행 시 sub: repo:my-org/my-repo:environment:dev-applyAWS IAM Role의 신뢰 정책에는 dev-apply가 등록되어 있지 않았기 때문에, AWS 수신 창구에서는 등록되지 않은 신분증으로 간주하고 거부했던 것이였다.
AWS IAM Role의 신뢰 정책 sub 조건에 environment:dev-apply를 명시적으로 추가하여 해결했다.
보안 강화를 위해 와일드카드(*) 대신 GitHub 고유 식별 번호가 포함된 불변 식별자를 사용하여 탈취 위험을 완전히 차단했다.
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:sub" = [
"repo:my-org@ID/my-repo@ID:pull_request",
"repo:my-org@ID/my-repo@ID:ref:refs/heads/main",
"repo:my-org@ID/my-repo@ID:environment:dev-apply" # ◀ 추가 완료!
]
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
}
정리하자면:
permissions: id-token: write로 신분증을 발급받아 AWS에 전달environment)를 쓸 때는 OIDC의 sub 클레임이 environment:<환경명>으로 변경되므로 IAM 신뢰 정책에 반드시 반영하기이상.