개요
CD(Continuous Deployment)를 구축하기 위해 Github Action은 많이 사용되는 배포 자동화 도구 중 하나이다.
더불어 애플리케이션을 빌드한 이미지를 보관하기 위해 이미지 보관소 (Dockerhub or ECR 등등)을 활용한다.
Github Action을 통하여 AWS ECR에 접근하기 위해서는 ECR의 접근 권한이 있는 계정 정보에 대해서 알아야한다.
하지만 직접적으로 계정 정보를 명시하는 것은 보안상 좋지 않은 방법이다.
따라서 Github Action에서는 OIDC를 이용한 인증 방식을 제공한다.
이 포스트에서는 OIDC를 설정, 계정 정보 대신 AWS에 인증하는 방법을 작성할 예정이다.
AWS 역할 생성
IAM 역할은 IAM 사용자와 다르게 짧은 인증 시간을 가지고 있다.
따라서 임시로 애플리케이션이나 외부 서비스에 권한을 부여하는데 유용하다.
먼저 Github Action에서 사용할 역할을 생성해야 한다.
Github Action workflow가 실행될 때 이 계정을 가지고 aws 리소스에 접근할 것이다.
AWS에서 제공하는 ID제공 업체 중 githubusercontent.com을 설정한다.
연결할 레포지토리가 GitHub 조직에 포함된 것이 아닌 개인용 레포일 때는 username을 작성하면 된다.
여기 작성한 증명 내용은 나중에 수정 가능하다.
다음 버튼을 누르고, 이 역할에 부여할 권한을 추가한다.
ECR에 이미지를 push 하는 용도로 사용할 것이기 때문에, 관련 push 정책만 주면 되지만, 임의로 full access 권한을 주었다.
다음 버튼을 누르면 아래와 같이 신뢰정책 검토 및 편집이 가능하다.
깃허브 조직 내 레포가 여러 개인 경우 sub에 대해서 레포 이름을 배열로 지정할 수 있다.
아래 예시로 설명하면, test organization의 server1, server2, server3 레포에 대해서 GitHub Action workflow에서 이 역할을 사용할 수 있는 것이다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::12345678: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:test/server1:*",
"repo:test/server2:*",
"repo:test/server3r:*"
]
}
}
}
]
}
GitHub Secret 지정
GitHub Workflow에서 AWS에 인증받을 수 있는 토큰을 받기 위해서 역할의 arn 정보와, ECR이 생성된 지역 정보가 필요하다.
따라서 Secrets에 다음과 같이 저장한다.
arn 정보는 IAM 역할 화면에서 볼 수 있다.
region은 ecr 레포지토리가 생성된 서울(ap-northeast-2)로 지정하였다.
workflow 작성
아래는 java 애플리케이션에 대한 workflow이다.
configure-aws-credintials@v4를 이용하여 AWS 인증을 받도록 하였는데, 이 과정에서 권한 부여가 필요하다.
permissions:
id-token: write
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Source Code
uses: actions/checkout@v4
with:
ref: '${{ github.head_ref }}'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Setup JDK 21
uses: actions/setup-java@v4
with:
java-version: 21
distribution: 'temurin'
- name: Build with gradlew
run: ./gradlew clean test bootJar
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{secrets.AWS_ROLE}}
aws-region: ${{secrets.AWS_REGION}}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag, and push docker image to Amazon ECR
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: medeasy
IMAGE_TAG: alarm
run: |
docker build --platform linux/amd64 -t $REGISTRY/$REPOSITORY:$IMAGE_TAG -f ./Dockerfile .
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
- name: deploy to Server
uses: appleboy/ssh-action@v0.1.8
with:
host: ${{secrets.MEDEASY_K3S_HOST}}
username: ${{ secrets.MEDEASY_USERNAME }}
key: ${{ secrets.MEDEASY_PEM }}
script: |
sh deploy-alarm.sh
verbose: true
아래는 위 워크플로우 중 권한 부여에 대한 내용이다.
permissions:
id-token: write
contents: read
id-token 설정을 통해 Github Action이 Github OIDC Provider에 토큰 생성을 요청할 수 있다.
이러한 설정들을 통해 AWS 인증에 필요한 토큰을 받고 Github Action이 역할을 가정할 수 있다.
OIDC 원리
OIDC 발급
그렇다면 어떤 구조로 안전하게 토큰을 발급받고 AWS로부터 인증받는 것일까? 라는 궁금증이 생기게 되었다.
따라서 과정을 자세하게 정리해보았다.
먼저 workflow를 실행하는 GitHub Hosting Runner에서 OIDC Provider에 토큰을 요청할 것이다.
OIDC Provider는 이벤트 트리거가 발생한 레포지토리 정보를 가지고 JWT 토큰의 형태와 유사한 OIDC Token을 발급해준다.
토큰은 발급자, 사용자, 인증처에 대한 클레임을 비밀키와 함께 해시화 시켜 만들어진다.
OIDC 검증
OIDC 토큰을 받게 되면 그 토큰을 가지고 AWS에 인증 요청하게 된다.
AWS에서는 GitHub의 공개키를 특정 주기로 가져오는데, 이 공개키를 통해 서명 검증을 한다.
검증에 통과하면 OIDC 토큰에 있는 클레임 값을 이전에 등록한 IAM정책과 비교하여 권한을 부여한다.
마무리
OIDC의 원리와 Github Action workflow에서의 OIDC 토큰을 통한 ECR접근 권한 부여에 대해서 정리해보았다.
기존 aws 사용자 계정을 사용하여 AWS리소스에 접근하는 방식보다, 보안적으로 이점이 많은 방식이라고 생각이 들었다.