문제

Actions에서 도커 이미지를 빌드하여 AWS의 ECR에 업로드 하기위해 하던 작업에는

기존에 AWS의 Access key와 secret을 github secrets에 저장해두고 사용하던 방식이 있지요.

해당 액션은 아래처럼 github secret에 IAM에서 생성한 유저의 액세스키를 등록하여 사용하였지요.

...
    steps:
    - name: Checkout
      uses: actions/checkout@v3

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ap-northeast-2

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v2

    - name: Build, tag, and push the image to Amazon ECR
      run: |
        TAG=$(git rev-parse --short HEAD)
        echo "TAG=${TAG}" >> $GITHUB_ENV
...

AWS Console에서는 해당 액세스 키를 위해 별도의 유저를 만들고 해당 유저의 역할에 맞는 권한만 부여해서 사용했지요.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload",
                "ecr:PutImage"
            ],
            "Resource": "*"
        }
    ]
}

기존 방식대로 해도 문제는 되지 않지요, 하지만 관리적인 요소가 필요할뿐

유출되었을때의 문제점, 그리고 revoke이후의 재등록 과정이 귀찮을 수 있지요.

방향

이걸 해결 하기 위한 방법이 OIDC(링크)를 활용한 방법이지요.

자격 증명이 긴 시간의 매체 대신, 그때그때 짧은 시간동안만(설정에따라 기본값 3600초) 유효한 자격을 얻어 AWS에 액세스 하는 방식이지요.

그럼 어떻게 하는지 한번 진행해 봅시다.

진행

해당 내용은 위 링크에도 잘 나와있지만, 익숙하지 않다면, 조금 불편할수도 있기에 아래 이미지를 잘 보시지요.

IAM > Access management > Identity providers로 이동하여 Add provider를 클릭

 

 

그리고 아래와 같이 입력을 후

Get thumbprint 클릭

Add provider 클릭 -> 저장

 

Thumbprint는?

이는 SSL/TLS 인증성의 지문 같은거라고 보면 되지요, AWS에서는 이 값을 사용해서 응답에 대한 신뢰성을 검증 하지요.

이 값이 일치하지 않으면 요청은 거절되지요.

이걸 구하는 방법은 openssl로 해서 aws-cli로 등록하는 방식도 있는데, 복잡해서, 이렇게 클릭 몇번으로 해결하지요.

 

 

IAM > Policies > Create policy 클릭

기존에 액션에서 사용하던 사용자의 권한정책을 그대로 가져오지요.

그리고 Next를 눌러 적당한 정책 이름(github-action-policy)을 넣어주고 생성하지요

 

AWS에서는 마지막으로 IAM > Roles > Create role 클릭

Web Identity를 선택

공급자를 아까 등록했던 공급자로 지정하지요.

조직과 레포, 브랜치는 해당 조직의 모든걸 허용한다는 전제로 아래처럼

조직 or 개인계정을 써주고

이하 *을 작성합니다.

그리고 Next

아까 미리 만들어준 정책을 주입하고 Next

 

Role name을 적당히 작성해주고

신뢰요소를 확인하지요.

자 여기서 신뢰요소는 수정할 필요가 있지요!

조건이 StringEquals가 아니라, StringLike로 해야하지요

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::xxxxxxxxx:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringLike": {
                    "token.actions.githubusercontent.com:sub": [
                        "repo:org or github id/*:*"
                    ]
                },
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": [
                        "sts.amazonaws.com"
                    ]
                }
            }
        }
    ]
}

 

그럼 이제 AWS 에서 할 작업은 끝났지요.

이제 Actions를 수정할 차례 이지요.

 

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        role-to-assume: arn:aws:iam::xxxxxxxx:role/github-action-role
        aws-region: ap-northeast-2

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v2

    # - name: Configure AWS credentials
    #   uses: aws-actions/configure-aws-credentials@v2
    #   with:
    #     aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    #     aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    #     aws-region: ap-northeast-2
        
    # - name: Login to Amazon ECR
    #   id: login-ecr
    #   uses: aws-actions/amazon-ecr-login@v2

 

그리고 액션을 실행하면

끝! 이지요

 

 

주의할점

fork 된 레포에서는 작동하지 않지요.

그리고 이글(링크)에서 보면 나오듯이 포크에 대한 악용 문제도 나오니 참고하세요.

role-to-assume에 할당하는 arn을 시크릿에 보관하면 해결가능!

@WebMvcTest를 진행시 Header에 Authorization을 할당 할 경우 아래 처럼 SecurityRequirements는 다양한 형태로 생길 수 있다.

근데 똑같이 헤더 이름을 지정하고, 테스트용 토큰 문자열을 할당하는데, 어떻게 아래처럼 다양한 결과를 맞을 수 있을까?

 

일단 스웨거 UI에서 우측 상단 아래에 있는 버튼을 활성화하려면 JWT_BEARER이어야 한다.

그 외 OAUTH2 관련 스펙을 정의하는건 별도 설정을 통해서 가능한데,

우선 나는 활성화를 하고 싶은데 안되어서,

securityRequirements의 type을 결정하는건 대체 누가 해주나 싶어서 찾아 보았다.

 

결과는 아래와 같다.

 

토큰의 페이로드에 오는 데이터의 형태에 따라 결정된다고 보면 된다.

scope가 포함된 토큰을 사용시에는 OAUTH2로 형태가 지정되고,

그게 아닌 경우 JWT_BEARER,

토큰은 있으나, 형태가 불분명하면, null로 처리되더라.

jwt 토큰 검증 관련 테스트는 별개로 하고,

mvcTest의 테스트 코드에 jwt처리하는 부분을 모킹하고 아무 토큰이나 주워다 넣었는데,

하필 그게 OAUTH2 토큰이었고,

별거 아닌곳에서 헤매게 되었지만, 나름 기능상 중요한 사실을 알게된 시간.

https://school.programmers.co.kr/learn/courses/30/lessons/131534

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

-- 코드를 입력하세요 

select a.YEAR
     , a.MONTH * 1 as MONTH
     , count(a.USER_ID) as PUCHASED_USERS
     , round(count(a.USER_ID)/(select count(u.USER_ID) from USER_INFO u where date_format(u.JOINED, '%Y') = '2021'), 1) as PUCHASED_RATIO
  from (
        SELECT distinct b.USER_ID
             , date_format(b.SALES_DATE, '%Y') YEAR
             , date_format(b.SALES_DATE, '%m') MONTH
             , date_format(b.SALES_DATE, '%Y-%m') YM
          from ONLINE_SALE b
         inner join USER_INFO u
            on b.USER_ID = u.USER_ID
           and date_format(u.JOINED, '%Y') = '2021'
       ) a
 group by a.YM
 order by YEAR, MONTH

# select a.YEAR
#      , a.MONTH * 1 as MONTH
#      , count(distinct a.USER_ID) as PUCHASED_USERS
#      , round(count(distinct a.USER_ID)/count(distinct b.USER_ID), 1) as PUCHASED_RATIO
#   from (
#         SELECT distinct b.USER_ID
#              , date_format(b.SALES_DATE, '%Y') YEAR
#              , date_format(b.SALES_DATE, '%m') MONTH
#              , date_format(b.SALES_DATE, '%Y-%m') YM
#           from ONLINE_SALE b
#        ) a
#  inner join USER_INFO b
#     on date_format(b.JOINED, '%Y-%m') <= a.YM
#    and b.GENDER is not null
#  group by a.YM
#  order by YEAR, MONTH

 

문제 제대로 안읽음;;

이게 레벨 5던데, 문제 자체가 그냥 헷갈림...내가 대충 읽은건가;;

첨에 아래 쿼리 만들었는데,,

아래 쿼리는 (매월 구매회원 / 매월 회원수)을 구하는거임...아 이정도 되니까 레벨5 구나 싶었음.

근데 아무리 돌려도 정답이 아니라길래;;

뭐 2021년 회원 중...이라는;; 이걸 읽지도 않고, 반올림 하는것도 안읽고;;

 

위 쿼리에선 스칼라서브쿼리를 사용하였다, 2021년 이라는 데이터에 대해서 반복적으로 가져올테니, 매 행마다 실행하는게 아니라,

한번 실행하고 메모리에 올려두고 재사용할테니 요게 더 빠를 거라 판단,

 

아래 쿼리의 경우 매년 매월 이라는 그나마 좀 다양한 경우의 수가 있기에, 범위가 커질수록 스칼라서브쿼리가 불리할거라 판단했고,

조인을 통해 가져오는 영역은 count(distinct b.USER_ID) 뿐일거라, 범위의 데이터를 통으로 가져오는게 아니라, 저걸 위한 데이터를 알아서 옵티마이저가 잘 연산해 주겠지 하는 막연한 믿음으로 작성한 쿼리,

동등 조인이 아니기에 일반적인 상황에선 쓰일수 없는 쿼리;

+ Recent posts