결론부터
Gitlab CI, Github Actions, circleci, Jenkins와 같은 CI/CD 도구들을 사용할 때, AWS를 비롯한 클라우드 사용을 위해서는 자격 증명(Identities)을 얻어야 한다. AWS에서 Access Key로 CLI를 사용할 수 있지만, 여러 사람이 사용하는 CI 도구에 키를 등록해 놓으면 키 노출 위험이 발생한다. 이 위험을 없애기 위해 OIDC(OpenID Connect)를 사용해서 인증하는 방법을 찾아서 적용해 봤다.
엑세스키를 사용하는 것보다는 난이도가 높지만, Gitlab CI 및 AWS IAM에서는 OIDC를 사용하기 편하게 제공하고 있다. AWS에서 발급받은 OIDC Provider, Audience, Role를 Gitlab CI에서 매칭해서 사용하기만 하면 된다.
Aceess Key를 사용할 때와, OIDC를 사용할 때 항목별 비교
구분 | AWS Access Key | AWS IAM OIDC |
---|---|---|
키 노출 위험 | 있음 | 없음 |
CI 제어 수준 | 낮음 | 높음 |
개발 난이도 | 비교적 낮음 | 비교적 높음 |
AWS Access Key를 통한 자격 증명 사용
AWS IAM에서 사용자와 권한을 생성하고, 발급된 Access Key(Access Key ID, Secret Access Key)를 Gitlab CI에 등록하면 Gitlab CI 스크립트에서 AWS를 즉시 사용할 수 있다.
Gitlab CI를 통해 러너를 실행할 때, AWS의 자격증명을 얻기 위해 Gitlab CI에서는 AWS 관련 환경변수를 제공한다. CI/CD 환경변수에서 AWS_ACCESS_KEY_ID
, AWS_SECRET_ACCESS_KEY
, AWS_DEFAULT_REGION
를 세팅하면 별다른 CI 스크립트 없이 AWS CLI를 사용할 수 있다.
장점
- 구성하기 쉽고 간단하다. Gitlab에서 관리하는 환경변수에 등록하기 때문에, CI 스크립트에서 추가로 설정할 부분 없이 자동으로 인증된다.
단점
- 엑세스키(access key id + secret access key)의 노출 위험이 있다. 권한만 있다면 Gitlab CI 설정에서 언제든지 등록되어 있는 엑세스키를 다시 볼 수 있기 때문에, 보안에 취약할 수 있다. 엑세스키 관련된 환경변수는 AWS에서 생성하듯이 한번 등록하고 나면 다시 못 보게 하면 더 좋을 것 같은데, 등록된 키를 다시 확인할 수 있다는 점에서 키가 노출될 위험이 있다고 판단된다.
AWS OIDC Provider을 통한 임시 자격 증명 사용
AWS IAM OIDC를 생성한 다음에 OpenID Connect를 신뢰하도록 설정(OIDC fingerprint 등록) 한다. 클라이언트(리파지토리 또는 애플리케이션)에 맞는 Audience(aud)를 생성하고, 클라이언트별로 사용할 역할(Role)을 분리해 놓으면 리파지토리마다 다른 권한을 획득할 수 있다. 리파지토리별로 다른 OIDC 권한으로 접근하면, OIDC 사용자의 신원을 체크할 수 있도록 한다.
AWS IAM에 등록된 OIDC Provider의 ROLE ARN(aud 별 Role)을 리파지토리마다 Gitlab CI에 환경변수로 등록해 놓으면 AWS STS를 통해 쉽게 AWS CLI 접근 권한을 획득할 수 있다.
CI 스크립트에서 IAM Role이 포함된 AWS STS를 사용하거나, Credential Chain에서 Web Identity 인증으로 접근하도록 설정해 준다. 러너가 실행되는 환경에서 임시 자격 증명을 얻고, 그 자격 증명으로 AWS CLI를 실행하게 된다.
이렇게 설정하면 ROLE_ARN은 노출되더라도 AWS에 설정한 ROLE ARN이기 때문에 별문제가 없다.
장점
- 엑세스 키를 사용하지 않기 때문에, 키 노출 위험이 없다.
- OIDC Role에서 Policy의 제어에 따라 임시 자격 증명을 얻을 수 있는 수준을 결정할 수 있다. 예를 들면 특정 리파지토리나, 특정 브랜치 또는 태그만 임시 자격 증명을 획득하는 제어 레벨을 AWS IAM에서 정책을 통해 할 수 있다. 이를 통해 엑세스키를 통해 하지 못하는 제어가 가능하다.
단점
- 개발 난이도가 엑세스키를 사용하는 것에 비해 높다. OIDC로 임시자격증명을 얻는 과정은 AWS CLI를 사용하거나 CI 스크립트 내에서 코드를 작성해야 하는데, OIDC로 임시 자격 증명을 얻는 메커니즘과 설정에 대한 학습이 필요하다.
AWS OIDC 구성과 Gitlab CI에서 임시 자격 증명 검색
참고 링크
- AWS 파트너 네트워크(APN) 블로그 : Setting Up OpenID Connect with GitLab CI/CD to Provide Secure Access to Environments in AWS Accounts
- 깃랩 공식 : Configure OpenID Connect in AWS to retrieve temporary credentials
1. AWS IAM OIDC에서 gitlab.com 공급자 등록
먼저, OIDC를 생성하고 Gitlab같은 CI 서비스의 fingerprint를 등록한다. AWS IAM > 자격 증명 공급자(Identity providers)로 들어가서 프로바이더를 생성한다. 생성한 AWS OIDC Provider는 gitlab.com을 신뢰하게 된다.
2. OIDC audiences(클라이언트 ID 리스트) 추가
OIDC Provider를 https://gitlab.com으로 생성한 후, Audience(aud)는 최소 권한 원칙에 따라 접근할 클라이언트별로 따로 생성해 주는 것이 좋다. 특정 클라이언트만 접근하도록 제어하는 것에도 용이하다.
3. 역할(IAM Role) 생성 및 신뢰 정책 추가
aud에서 사용할 정책이 들어있는 IAM Role을 생성하고, Trust relationships를 등록한다. 이때, OIDC ARN이 ROLE에 Federated 되어야 ROLE에 등록된 OIDC를 찾아서 인증할 수 있게 된다.
사용할 역할(IAM Role)에 따라 AWS 리소스에 접근할 수 있는 권한이 다른데, Role마다 Permission을 지정해서 사용하면 된다. Gitlab CI yaml 스크립트 파일에는 아래와 같이 설정하면 web identity token이 설정돼서, 임시 자격 증명을 얻고 AWS CLI를 사용할 수 있다.
Policy Statement에서 Condition을 설정해주면 리파지토리 단위나 브랜치, 태그 단위까지 제어가 가능하다. 이 부분은 엑세스키를 사용했을 때는 제어하기 힘든 부분이다.
4. Gitlab CI 설정
# .gitlab-ci.yml
stages:
- test
deploy:
stage: test
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
id_tokens:
OIDC_TOKEN:
aud: https://user-audience.test
before_script:
- |
mkdir -p ~/.aws
echo "${OIDC_TOKEN}" > /tmp/web_identity_token
echo -e "[default]\nrole_arn=${ROLE_ARN}\nweb_identity_token_file=/tmp/web_identity_token" > ~/.aws/config
script:
- aws ec2 describe-instances
gitlab.com에서는 Aud에 맞는 OIDC 프로바이더 접근해서, 허락된 AWS Role을 통해 리파지토리, 브랜치, 태그를 검증하고 AWS CLI를 실행하게 된다.
Gitlab CI 스크립트에서 id_tokens
는 Gitlab CI에서 JWT를 CI/CD 작업에 추가할 수 있도록 만든 JWT 환경변수인데, OIDC_TOKEN이라는 변수명으로 AWS에서 생성한 OIDC aud를 비교적 간단하게 사용할 수 있다. web identity token을 ~/.aws/config 파일에 세팅한 뒤, AWS CLI를 사용하면 미리 생성해 둔 역할(AWS ROLE)에 맞는 리소스를 제어할 수 있다.
만약 .gitlab-ci.yml에 등록된 aud가 IAM OIDC Provider의 audience 리스트에 없으면, 다음과 같이 에러가 발생한다.
AWS CDK에서는 에러가 나는데?
위의 코드 예제처럼 ~/.aws/config 파일을 사용해서 프로필을 설정하면, 아래와 같이 에러가 발생한다.
Unable to resolve AWS account to use. It must be either configured when you define your CDK Stack, or through the environment
이때는, AWS STS를 호출해서 임시 자격 증명을 러너에 세팅해서 사용하면 된다.
# .gitlab-ci.yml
stages:
- test
deploy:
stage: test
image: node:20-alpine
id_tokens:
OIDC_TOKEN:
aud: https://user-audience.test
before_script:
- apk add --no-cache aws-cli
- npm install
- npm install -g aws-cdk
- > # 러너에 임시자격증명을 세팅(OIDC)
export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s"
$(aws sts assume-role-with-web-identity
--role-arn ${ROLE_ARN}
--role-session-name "GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}"
--web-identity-token ${OIDC_TOKEN}
--duration-seconds 900
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]'
--output text))
- aws sts get-caller-identity
script:
- cdk diff