왜 장기 자격증명이 문제인가
대부분의 CI 파이프라인은 클라우드에 접근하기 위해 액세스 키를 시크릿으로 저장한다. 하지만 정적 키는 한 번 만들면 만료되지 않고, 저장소·로그·포크된 PR을 통해 유출될 위험이 상존한다. 유출을 탐지하기 어렵고, 회수하려면 수동 로테이션이 필요하다. 키가 유효한 동안에는 어디서 호출됐는지 구분할 수도 없다. 결국 "장기 시크릿을 CI에 넣는 것" 자체가 근본 원인이다.
OIDC 방식의 핵심 아이디어
OIDC(OpenID Connect) 연동은 CI 러너가 발급받은 단기 ID 토큰을 클라우드에 제시하면, 클라우드가 이를 검증하고 수 분짜리 임시 자격증명을 내주는 구조다. 저장해 둘 시크릿이 아예 없다. GitHub Actions·GitLab CI 등은 워크플로 실행마다 서명된 JWT를 발급하며, 여기에는 저장소·브랜치·환경 같은 클레임이 담긴다. 클라우드는 이 클레임으로 "누가 무엇을 요청하는지"를 신뢰 조건으로 검증한다.
신뢰 관계 설정 (AWS 예시)
먼저 클라우드 쪽에서 CI의 OIDC 공급자를 등록하고, 특정 저장소·브랜치만 허용하는 IAM 역할을 만든다. 신뢰 정책의 조건절이 보안의 핵심이다.
{
"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:my-org/my-repo:ref:refs/heads/main"
}
}
}]
}
워크플로에서 인증하기
GitHub Actions에서는 id-token: write 권한만 부여하면 러너가 토큰을 발급받는다. 공식 액션이 토큰을 STS로 교환해 임시 자격증명을 환경변수로 주입한다.
name: deploy
on:
push:
branches: [main]
permissions:
id-token: write # OIDC 토큰 발급에 필수
contents: read
jobs:
deploy:
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/ci-deploy
aws-region: ap-northeast-2
- run: aws sts get-caller-identity # 임시 자격증명 확인
sub 클레임 조건을 정확히 좁혀라
가장 흔한 사고는 sub 조건을 느슨하게 두는 것이다. repo:my-org/my-repo:* 처럼 와일드카드를 남발하면 포크의 PR이나 임의 브랜치에서도 역할을 가로챌 수 있다. 브랜치·태그·환경 단위로 최소한만 허용하자.
| sub 패턴 | 허용 범위 | 권장 |
|---|---|---|
| repo:org/repo:ref:refs/heads/main | main 브랜치 push만 | 권장 |
| repo:org/repo:environment:prod | prod 환경 배포만 | 권장 |
| repo:org/repo:* | 모든 이벤트·브랜치·PR | 지양 |
다른 클라우드에서의 대응
개념은 동일하고 교환 대상만 다르다. 어느 쪽이든 "저장할 시크릿이 없다"는 점은 같다.
# GCP: Workload Identity Federation
gcloud iam workload-identity-pools create-cred-config \
projects/PID/locations/global/workloadIdentityPools/POOL/providers/PROVIDER \
[email protected] \
--output-file=creds.json
# Azure: azure/login 액션에서 client-id, tenant-id, subscription-id 지정
# (client-secret 없이 federated credential 사용)
운영 시 주의점
몇 가지를 반드시 점검한다. 첫째, aud(audience)를 클라우드가 기대하는 값으로 고정해 토큰 재사용을 막는다. 둘째, 역할 권한은 배포에 필요한 최소 범위로 제한하고 세션 지속시간을 짧게 둔다. 셋째, CloudTrail 등 감사 로그로 AssumeRoleWithWebIdentity 호출의 sub를 주기적으로 확인해 예상치 못한 저장소·브랜치가 역할을 사용했는지 살핀다. 넷째, 셀프호스티드 러너를 쓴다면 러너 자체가 신뢰 경계가 되므로 격리를 강화한다. OIDC는 시크릿 자체를 제거하지만, 신뢰 조건을 잘못 좁히면 오히려 무제한 열쇠를 배포한 셈이 된다. 조건절 검증이 곧 보안이다.