인프런 커뮤니티 질문&답변

kingbj0429님의 프로필 이미지
kingbj0429

작성한 질문수

[2024] 실무에서 사용중인 AWS 클라우드 IAM 이해와 보안

IRSA 의 토큰이 최대 24시간인데 만료되면 어떻게 되나요?

해결된 질문

작성

·

244

1

안녕하세요

우선 좋은 강의 감사합니다.

 

강의 시청 중 IRSA 부분을 진행 중인데, 웬만한 것들은 이해가 되는데 한가지 궁금한 것이 있습니다.

결국 파드가 AWS 리소스에 접근할 수 있는 토큰을 특정 마운트 경로에 가지고 있는 것인데, 임시 자격 증명은 영구적인 것이 아니고 최대 24시간까지만 유지가 되는데, 이 유효 시간이 지나면 어떻게 되나요?

 

예를 들어 파드 A 는 S3 에 접근을 해야 해서 IRSA 를 사용하고 있습니다. 최대 만료 시간인 24시간 지난 후에도 여전히 S3 에 접근이 가능한데, 이게 왜 가능한지가 궁금합니다.

 

무언가 토큰을 갱신해주는 쿠버네티스 컨트롤러나 오퍼레이터 같은 것이 있는 걸까요?

(근데 암만 찾아봐도 mutatingwebhookconfigurations 의 pod-identity-webhook 외에는 관련된 것을 못찾겠네요 ㅜㅜ)

답변 1

1

천강민님의 프로필 이미지
천강민
지식공유자

안녕하세요! 좋은 질문 주셔서 감사합니다.

먼저 아셔야 하는건 토큰을 발급하고 주입하는 기능은 k8s의 기능이라는 것 입니다.

k8s 1.12 버전부터 도입된 ProjectedServiceAccountToken 이라는 기능으로, 자세한 내용은 projected volume 이라는 키워드와 함께 찾아보시길 권장 드립니다. (말 그대로 k8s의 기능이기에...)

위의 기능을 AWS의 Identity providers 기능과 연계해서 사용하는게 IRSA라고 보시면 됩니다. (그리고 AWS에 등록된 프로바이더를 통해 검증하기 위해 EKS OIDC 엔드포인트는 무조건 퍼블릭인겁니다.)

결과적으로 토큰의 로테이션 또한 k8s의 kubelet이 수행(요청)합니다. 링크로 가셔서 조금 내리면 아래와 같은 문구가 있습니다.

The kubelet will: request and store the token on behalf of the Pod; make the token available to the Pod at a configurable file path; and refresh the token as it approaches expiration. The kubelet proactively requests rotation for the token if it is older than 80% of its total time-to-live (TTL), or if the token is older than 24 hours.

The application is responsible for reloading the token when it rotates. It's often good enough for the application to load the token on a schedule (for example: once every 5 minutes), without tracking the actual expiry time.

대충 요약하면, "kubelet은 토큰의 수명이 80%보다 오래됐거나 24시간을 넘으면 로테이션을 수행한다." 겠죠?

결국 중요한 일은 k8s가 모두 수행하는거고, aws-eks-pod-identity-webhook은 볼륨마운트와 환경변수 주입을 수행하는거라고 보시면 됩니다! (실제 적용될 yaml 수정)

!꿀팁!을 하나 드리자면, k8s preStop hook 사용 시 토큰 로테이션이 정상적으로 되지 않는 이슈가 있으니 실제 실무에서 사용하실 때에는 안내도 같이 해주시면 같이 일하시는 분들도 좋아하실 것 같습니다! (장애 사전 예방 ㅎㅎ)

-추가- 조금 더 정확한 내용은 링크 참고하시면 좋을 것 같네요!

감사합니다!

천강민님의 프로필 이미지
천강민
지식공유자

OIDC 엔드포인트를 API 서버라고 잘못 표현하여 수정했습니다!

kingbj0429님의 프로필 이미지
kingbj0429
질문자

아하 답변 감사합니다 ㅎㅎ

그럼 한가지 더 드는 의문점이 있습니다!

$ aws sts assume-role-with-web-identity --role-arn --role-session-name --web-identity-token

 

--web-identity-token 부분에는 파드에 마운트된 토큰을 집어넣게 되고, kubelet 이 이 토큰값을 갱신하는 걸로 이해했는데,

이 토큰값이 AWS 가 아닌 k8s 의 기능으로 갱신이 되는데, 어떻게 해당 토큰값으로 AWS 에 접근할 수 있는 액세스 키를 얻을 수 있는 걸까요?

 

애초에 kubelet 이 IRSA 와 관련된 토큰을 갱신할 수 있는 권한?이 있다는게 이해가 잘 안갑니다 ㅜㅜㅜ (일반적인 serviceaccount token 은 k8s 내 영역이 맞으니 납득이 되는데 말이죠...)

aws-eks-pod-identity-webhook 은 환경변수와 볼륨 마운트 부분만 핸들링한다면, 더욱더 어떻게 Kubelet 이 assume-role 할 수 있는 토큰을 갱신할 수 있는 권한이 있는지 궁금합니다!

image.png




천강민님의 프로필 이미지
천강민
지식공유자

안녕하세요! 제가 질문에 대한 이해를 잘못한 것 같아서 먼저 말씀드리면,

24시간이 유지되는건 k8s sa token이지, AWS session token이 아닙니다.(AssumeRoleWithWebIdentity API 문서의 Session Duration을 보시면 아무런 옵션이 없으면 기본 1시간, 옵션을 주더라도 최대 12시간 까지만 사용 가능하다고 나와 있습니다.)

사실 이것 때문에 24시간 얘기하신게 당연히 k8s sa token을 말씀하신걸로 이해 했었습니다.

아무튼 답변을 계속 드리면, 토큰을 두 가지로 구분할 수 있습니다.

  1. k8s service account token

    1. kubelet이 갱신해주는 것

  2. aws session token

    1. 자체 구현하거나 API 사용시마다 세션을 새로 생성해서 갱신

여기서 1/2번 토큰을 갱신하는 주체는 위에서 써놨듯이 서로 다릅니다. 즉, kubelet은 AWS session token을 갱신하지 않습니다.(k8s sa token을 갱신합니다. -> 정확히는 API 서버에 요청하고... 등등이 있지만 편하게 표현하겠습니다.)

또한, IRSA는 k8s 서비스 어카운트 토큰을 통해 AWS 역할을 사용하기 위한 방법일 뿐이지 IRSA 자체는 토큰 발급과는 아무런 관련이 없습니다.(결국 OIDC + AssumeRoleWithWebIdentity API를 활용해서 역할을 사용 가능하도록 지원(마운트, 환경변수 주입)하는 방식일 뿐입니다.)


여기까지를 서론으로 두고, 본론을 말씀드리기 전에 아래의 내용에 대해 제가 추측한 것을 기준으로 말씀드릴건데 틀리다면 댓글 달아주세요.

"애초에 kubelet 이 IRSA 와 관련된 토큰을 갱신할 수 있는 권한?이 있다는게 이해가 잘 안갑니다 ㅜㅜㅜ (일반적인 serviceaccount token 은 k8s 내 영역이 맞으니 납득이 되는데 말이죠...)"

위 내용은 kubelet이 어떻게 AWS role session token을 갱신할 수 있는지를 질문하신 것 같은데요.(IRSA와 관련된 토큰을 AWS 세션 토큰이라고 이해 했습니다.)

당연히 kubelet은 AWS role session token을 갱신하지 않습니다. 그리고 IRSA 방식을 활용하기 위해 k8s sa token을 발급하고 관리하는 것도 말씀하신 "일반적인 serviceaccount token 은 k8s 내 영역" 입니다.

그렇기에 질문주신 내용에서 "어떻게 Kubelet 이 assume-role 할 수 있는 토큰을 갱신할 수 있는 권한" 라는 내용이 잘 이해가 안되는 상황인데요. 위에서 말씀드린 것 처럼 k8s sa token은 모두 k8s 내 영역이기 때문입니다.

그래서 질문 주신 분께서 진짜 궁금한 내용이 뭘까 고민해본 결과,

"이 토큰값이 AWS 가 아닌 k8s 의 기능으로 갱신이 되는데, 어떻게 해당 토큰값으로 AWS 에 접근할 수 있는 액세스 키를 얻을 수 있는 걸까요?"

라고 결론 내렸습니다. 결국 k8s 토큰을 통해 어떻게 AWS 접근 가능한 자격증명을 얻을 수 있는가? 라고 말이죠.


위 부분은 강의에서도 설명드리고 있는 부분이긴 한데 파편화 되어 있어서 이해하기 어려우셨을 수도 있을 거라고 생각 합니다.

image.png

강의에서 설명드렸던 자료들을 조금 편집해서 첨부 했습니다.

  1. k8s 토큰(JWT)을 통해 AWS 역할을 사용하기 위해 AWS IAM에서 Identity Provider 설정 합니다.

  2. 1번 과정이 완료되면 [그림의 1번 과정]을 통해 k8s sa token(JWT)와 함께 ARN 등을 함께 전송합니다.(이때 호출하는게 AssumeRoleWithWebIdentity API 입니다.)

  3. AWS STS는 전달 받은 토큰이 유효한지 IAM에게 물어보고, IAM은 퍼블릭하게 오픈되어 있는 EKS OIDC ENDPOINT의 특정 경로(/.well-known./~)를 통해 토큰을 검증합니다.(해당 과정은 강의에서 보여드리고 있습니다.)

  4. 토큰의 유효성이 검증되면 IAM은 STS에게 맞다고 전해줍니다.

  5. STS는 전달 받은 역할의 ARN과 일치하는 역할의 세션 토큰을 생성해서 사용자에게 전달합니다.

  6. 사용자는 전달 받은 토큰을 활용하고, 갱신이 필요한 시점이 되면(이건 코딩해야함) 다시 토큰을 읽어 AssumeRoleWithWebIdentity API를 통해 세션 토큰 갱신을 요청합니다.

  7. 1번 과정은 최초 1회만 수행하면 되며, 2 ~ 6 과정을 반복하면서 필요한 API를 호출합니다,

그림을 보면 아시겠지만 각 토큰 갱신주체는 다르고, 중간에서 aws-eks-pod-identity-webhook이라는 애가 마운트/변수 주입을 통해 중계만 한다고 보시면 됩니다. 그리고 이러한 과정을 통틀어 IRSA라고 표현합니다.

감사합니다.

천강민님의 프로필 이미지
천강민
지식공유자

혹시라도 계속 이해가 안되시면 말씀 부탁드리겠습니다. 방법을 강구해 보겠습니다!

kingbj0429님의 프로필 이미지
kingbj0429
질문자

정말 이해하기 어렵게 질문을 했는데도.. 기가막히게 질문의도를 캐치하신거 같습니다 ㅜㅜ 최고십니다!!

 

가장 궁금했던건

"6. 사용자는 전달 받은 토큰을 활용하고, 갱신이 필요한 시점이 되면(이건 코딩해야함) 다시 토큰을 읽어 AssumeRoleWithWebIdentity API를 통해 세션 토큰 갱신을 요청합니다."

이 부분이였습니다. 즉 aws session token 이 만료되면 그걸 어떻게 다시 갱신하지가 가장 궁금했던 포인트였습니다!

코딩이 필요하다고 하셨는데, 만약 S3 에 접근이 필요한 파드(spring 애플리케이션을 가지는)가 있는데 AWS session token 이 만료 되었을 경우 애플리케이션 내에서 마운트된 k8s sa token(JWT) 와 AssumeRoleWithWebIdentity API 를 이용해 코딩으로 다시 갱신할 수 있도록 해야 한다는 말씀이신가요?

스크린샷 2024-08-12 오전 12.23.35.png

 

그리고

"1번 과정이 완료되면 [그림의 1번 과정]을 통해 k8s sa token(JWT)와 함께 ARN 등을 함께 전송합니다.(이때 호출하는게 AssumeRoleWithWebIdentity API 입니다.)"

라고 하셨는데, ARN 과 함께 보내더라도 뭘 믿고 AWS 입장에선 AssumeRoleWithWebIdentity API 를 통해 AWS Session Token 을 발급해주는건가요? 있는 정보라고는 OIDC, ARN 등 퍼블릭?스러운 값밖에 없는데 말이죠.

 

그럼 누군가 OIDC URL 과 ARN 등 필요한 정보를 알아내서 JWT 를 만들게 된다면 AWS Session Token 을 똑같이 발급 받을 수 있는 상황이 생길 수 있는걸까요?

 

ps)

"이 토큰값이 AWS 가 아닌 k8s 의 기능으로 갱신이 되는데, 어떻게 해당 토큰값으로 AWS 에 접근할 수 있는 액세스 키를 얻을 수 있는 걸까요?"

정확합니다!!

+ JWT Token 이랑 AWS Session Token 이 같다고 생각하고 있었습니다 ㅜㅜ 그래서 aws-eks-pod-identity-webhook 과정이 AWS Session Token 를 생성해주는 과정이라고 생각했습니다..

천강민님의 프로필 이미지
천강민
지식공유자

안녕하세요. 질문 순서와 반대로 답변을 드리겠습니다.

일단, 생성되는 토큰을 볼륨마운트 하는 기능 자체가 원래 k8s의 기능이라는걸 아셔야 합니다.

aws-ekd-pod-identity-webhook은 pod가 생성되는 과정에서 웹훅을 받아서 검사하고 볼륨마운트와 환변경수 주입을 자동으로 해주는 것 뿐입니다.(이 말인 즉, 자동으로 만들어지는 projected volume과 환경변수들을 손으로 넣어줘도 된다는 거겠죠? 정말 궁금하시면 한 번 시도해보셔도 됩니다.)

-> aws-eks-pod-identity-webhook은 IRSA 과정에서 그냥 귀찮은 일 대신해주는 역할이다. 손으로 직접 토큰을 볼륨마운트하고, 환경변수를 넣어서 pod을 만들어도 동일한 효과를 볼 수 있다.

JWT가 유효한지 확인은 PUBLIC OIDC ENDPOINT를 통해서 진행하는 거구요.(AWS IAM에서요.)

STS는 JWT가 유효한지 확인하지 않습니다. 다만, 신뢰정책(Trust Policy)를 통해서 aud, sub 값을 추가로 검사할 수 있는겁니다.

kubelet -> sa token(JWT)
 : 갱신 등 관리 수행
POD(AWS sdk) -> AssumeRoleWithWebIdentity API -> STS ENDPOINT
 : STS 엔드포인트는 JWT가 유효한지 모름
STS -> IAM
 : STS가 IAM에게 JWT가 유효한지 확인해달라고 함
IAM -> EKS OIDC ENDPOINT
 : /.well-known/~ 접근하여 키 획득 후, JWT 토큰 유효한지 확인
IAM -> STS
 : OK
STS -> POD
 : 토큰 전달

두 번째로, 보통 개발을 수행할 때는 SDK의 자격증명 프로바이더 클래스를 사용하실 겁니다.(직접 AssumeRoleWithWebIdentity 호출 안하실거예요. 보통은.)

이렇게 사용하면 장점이 있는데요. 자격증명 공급자 체인이라고 해서, 위 링크의 순서대로 탐색을 하면서 사용 가능한 자격증명 획득 방식을 선택합니다.

IRSA를 통해 사용하시면 아마 3번째 방식(Web identity token from AWS Security Token Service)으로 선택이 될겁니다. 이 방식으로 토큰을 얻는 방법은 (제 생각엔) 충분히 설명 드린 것 같아서 AWS 세션 토큰 갱신과 관련해 말씀드리겠습니다.

제가 Java와 친하지 않아서 파이썬 코드로 보여드릴게요.

import boto3
import time
from datetime import datetime, timedelta, timezone

def refresh_session_token():
    # AssumeRoleWithWebIdentity 라고 봐주세요.
    # 어차피 세션 토큰 반환하고, 유지 시간 지정하는건 동일합니다.
    res = sts.get_session_token(DurationSeconds=900) 
    cred = res["Credentials"]
    ec2 = boto3.client(
        "ec2",
        region_name="ap-northeast-2",
        aws_access_key_id=cred["AccessKeyId"],
        aws_secret_access_key=cred["SecretAccessKey"],
        aws_session_token=cred["SessionToken"],
    )
    exp_time = cred["Expiration"].astimezone(timezone.utc)
    return ec2, exp_time

sts = boto3.client("sts", region_name="ap-northeast-2")
ec2, exp_time = refresh_session_token()

while True:
    # 현재 시간 얻기
    now = datetime.now(timezone.utc)
    
    # 만료 예정 시간 - 현재 시간으로 세션이 14분 이하로 남았는지 확인
    if exp_time - now < timedelta(minutes=14):
        print("Session is about to expire, refreshing session token...")
        ec2, exp_time = refresh_session_token()
    
    # EC2 인스턴스 정보 조회
    print(ec2.describe_instances()["Reservations"])
    
    time.sleep(10)

"애플리케이션 내에서 마운트된 k8s sa token(JWT) 와 AssumeRoleWithWebIdentity API 를 이용해 코딩으로 다시 갱신할 수 있도록 해야 한다"

라고 해주셨는데, 맞습니다. 다만, 앞서 말씀드렸듯이 직접 호출은 안하실겁니다.

보통은 위 코드와 비슷하게 시간이 XX% 정도 남았을 때 갱신하도록 구현하게 됩니다.(계속 Assume 계열 API를 호출하면 CloudTrail 로그도 빨리 쌓이고, 자격증명 얻기 위한 레이턴시가 추가 되겠죠?)

이걸 자동으로 수행하도록 하는 방법도 있는데 Java에서는 어떻게 구현할 수 있는지 검색해보시면 좋을 것 같네요.(스택오버플로우에 있는 답변 공유드립니다.)

kingbj0429님의 프로필 이미지
kingbj0429
질문자

와... 궁금했던 부분이 전부 해결되었습니다 ㅜㅜㅜㅜㅜㅜ

엄청나게 상세하고 친절한 답변에 진심으로 감사드립니다!!

 

kingbj0429님의 프로필 이미지
kingbj0429
질문자

추가로 자바 같은 경우 공식 문서에 토큰 새로 고침 프로세스를 자동으로 수행해주는 것 같다고 하네요!

image.png

 

image.pngimage.png


kingbj0429님의 프로필 이미지
kingbj0429

작성한 질문수

질문하기