1. IRSA란?
IRSA(IAM Roles for Service Accounts)는 쿠버네티스 service account에 IAM role을 설정하는 기능입니다. pod는 service account에 설정된 IAM role을 사용하여 AWS리소스 작업을 합니다. pod가 IAM role을 사용할 때, AssumeRoleWithWebIdentity API가 호출되어 assume role이 수행됩니다.
2. 사용방법
사용방법은 매우 간단합니다. service account annotation에 IAM role arn을 설정하면 됩니다. 그리고 2가지 설정이 필요한데 다음 챕터인 [IRSA를 사용하기 위한 설정]에서 설명합니다.
3. IRSA를 사용하기 위한 설정
pod가 쿠버네티스 service account에 설정한 IAM Role을 사용하려면 2가지 설정이 필요합니다.
- EKS OIDC idenity provider 생성
- AWS IAM Role TrustRelationship 설정
3.1. EKS OIDC identity provider 등록
EKS OIDC identity provider는 EKS생성시에 자동으로 생성됩니다.
자동으로 생성된 EKS OIDC identity provider은 AWS에 등록해줘야 사용할 수 있습니다. AWS IAM의 Identity provider메뉴에서 등록합니다.
생성입력 폼에는 아래 그림과 같이 설정합니다
- Provider type: OpenID Connect
- Provider URL: EKS 클러스터에서 확인한 OpenID Connect provider URL
- Audience: sts.amazonaws.com
3.2. AWS IAM Role TrustRelationship 설정
pod에서 Assume role을 할 수 있도록 IAM role에서 TrustRelationship을 설정합니다. 예제 코드는 저의 github에 공개되어 있습니다.
- Principal: EKS OIDC idenity provider arn
- Action: sts:AssumeRoleWithWebIdentity
- Condition:
- aud: sts.amazonaws.com
- sub: system:{namespace}:{service account이름}
4. 동작원리
EKS IRSA동작원리는 EKS OIDC identity provider를 왜 사용했는지 이해하면 됩니다.
4.1. IRSA에서 EKS OIDC idenity provider를 왜 사용할까?
EKS IRSA 동작 원리의 시작은 pod인증에서 시작합니다. AWS는 pod 인증을 위해 OIDC프로토콜을 적용했습니다. 그래서 IRSA에서 EKS OIDC identity provider를 사용했습니다.
IRSA가 등장하기 이전 Assume role을 직접 구현한 경험을 회상해봅시다. IAM user로 Assume role을 했을 때는 Access key, Secret key를 사용하여 IAM user가 맞는지 인증을 했습니다.
그렇다면 pod가 IRSA를 사용하여 Assume role을 한다면, AWS에서는 어떻게 내가 관리하는 pod인지 인증을 어떻게 할까요? Access key 등 인증수단이 없기 때문에 pod인증을 못합니다.
만약 IRSA에서 pod 인증과정이 없다면 어떻게 될까요? 모든 pod가 Assume role을 성공하여 AWS권한을 얻습니다. Assume role에 권한이 Administrator가 있다면 피해가 커지겠죠.
IRSA에서 pod인증을 안하면 보안문제가 발생하기 때문에, AWS는 IRSA pod인증 메커니즘으로 OIDC프로토콜을 적용했습니다. AWS에서는 pod마다 JWT를 발급하고, pod가 Assume role을 요청할 때 JWT로 pod 인증을 수행합니다. pod는 Assume role을 할 때, 인증수단을 JWT을 사용하는 AssumeRoleWithWebIdentity API를 호출합니다.
4.2 JWT는 pod 어디에 있을까?
JWT는 pod의 /var/run/secrets/eks.amazonaws.com/serviceaccount 디렉터리에 있습니다. 예제코드는 저의 github을 참고해주세요.
$ ls -l /var/run/secrets/eks.amazonaws.com/serviceaccount/
lrwxrwxrwx 1 root root 12 Apr 13 04:55 token -> ..data/token
JWT는 모든 pod에 있지 않습니다. IAM role을 설정한 serviceaccount를 사용하는 pod에만 JWT가 있습니다.
4.3 JWT 내용 확인 하는 방법
JWT 내용는 cat명령어로 확인할 수있습니다.
$ cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token
JWT내용은 해석하려면 디코딩이 필요한데 jwt.io에서 쉽게 디코딩할 수 있습니다.
4.4. JWT가 어떻게 생성되는 걸까?
쿠버네티스 manifest에 JWT설정이 없지만 pod에 JWT가 있었습니다. 어떻게 JWT가 pod에 존재한걸까요?
JWT는 3가지 기술을 사용하여 pod에 JWT를 생성합니다.
- EKS OIDC identity provider
- kubernetes amdmision controller
- kubernetes projected service account
JWT는 EKS OIDC identity provider가 생성합니다. 그래서 JWT issuer(발급자)가 EKS OIDC identity provider URL입니다.
JWT생성 시기는 pod가 생성되거나 수정될 때입니다. mutatingwebhook을 사용해서 pod가 생성 또는 수정될 때 EKS OIDC identity provider가 생성한 JWT를 volume으로 mount합니다.
kubectl get mutatingwebhookconfigurations 1 ↵
NAME WEBHOOKS AGE
pod-identity-webhook 1 135m
4.4.1. EKS OIDC identity provider을 연동
projected serivce account를 사용하는 가장 중요한 이유는 EKS OIDC identity provider를 쿠버네티스 serviceaccount에 연동하기 위해서입니다.
기본 쿠버네티스 serviceaccount 토큰 audience(대상자)는 쿠버네티스로 되어 있습니다. JWT audience가 https://kubernetes.default.svc입니다.
그리고 volume 마운트 경로도 IRSA마운트 경로와 다릅니다.
ls -l /var/run/secrets/kubernetes.io/serviceaccount/token
lrwxrwxrwx 1 root root 12 Apr 13 04:55 /var/run/secrets/kubernetes.io/serviceaccount/token -> ..data/token
JWT audience를 수정하려면 project service account를 사용할 수 밖에 없습니다.
volumes:
- name: aws-iam-token
projected:
sources:
- serviceAccountToken:
audience: sts.amazonaws.com
expirationSeconds: 86400
path: token
4.4.2 JWT 만료시간 설정과 자동갱신
projected serivce account는 expirationSeconds필드를 사용하여 JWT 만료시간을 설정합니다. JWT 데이터를 보면 exp필드가 만료시간입니다.
그리고 JWT는 최소 7일에 1번 자동 갱신을 해야합니다. JWT를 생성할 때 만드는 키가 7일에 한번 갱신되기 때문입니다. projected serivce account는 JWT가 만료되면 자동갱신 하기 때문에 관리입장에 매우 좋습니다.
4.5. JWT 검증은 어떻게 할까?
JWT검증은 AWS IAM이 서명검증 방법으로 진행합니다. JWT를 만들 때 private key가 필요하기 때문에 private key에 대응하는 public key로 검증할 수 있습니다. 검증이 성공하면 에러가 발생하지 지만 검증이 실패하면 오류가 발생합니다. 검증에 대한 코드는 제 github을 참고해주세요.
public_keys = get_aws_public_keys()
# 토큰에서 'kid' 헤더 추출
headers = jwt.get_unverified_header(token)
key_id = headers["kid"]
# 적합한 공개 키로 서명 검증
public_key = public_keys[key_id]
jwt.decode(token, public_key, algorithms=["RS256"], audience="sts.amazonaws.com")
검증 단계에서 필요한 public key는 “EKS OIDC identity provider URL” + “/keys” API로 조회할 수 있습니다.
pod는 AWS IAM이 JWT를 검증할 수 있도록 AssumeRole API대신 AssumeRoleWithWebIdentity API를 호출합니다.
AssumeRoleWithWebIdentity API를 호출하려면 약속된 환경변수 “AWS_WEB_IDENTITY_TOKEN_FILE”을 설정해야 합니다. 환경변수 값은 JWT경로입니다.
5. 마치며
사실 EKS IRSA글을 쓰기 위해 약 30시간 이상 고민하고 글을 작성했습니다. 글을 썼다가 지웠다가 여러번 반복했습니다.
많이 고민했던 이유는 OIDC 프로토콜을 왜 사용하는지 스스로 납득하지 못했습니다. 우연히 지쳐서 멍 때리고 있다가, 머리속에 갑자기 쿠버네티스 service account는 어떻게 AWS가 인증할 건데?라는 생각이 스쳐지나갔습니다.
그래서 저는 이 글의 주제를 AWS가 OIDC 프로토콜을 사용하여 쿠버네티스 servicea account를 인증하는 방법으로 방향을 결정했습니다. 이 생각이 틀릴 수 있지만 저처럼 IRSA가 왜 OIDC를 사용하는지 고민하시는 분에게 많은 도움이 됐으면 좋겠습니다.
6. 참고자료
- EKS IRSA 공식문서: https://docs.aws.amazon.com/ko_kr/ko_kr/eks/latest/userguide/iam-roles-for-service-accounts.html
- IAM TrustRelationship 설정 공식문서: https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html
- AWS identity provider과 EKS OpenID Connect provider 연결 원리: https://aws.amazon.com/ko/blogs/containers/introducing-oidc-identity-provider-authentication-amazon-eks/
- 쿠버네티스 service account issuer discovery: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-issuer-discovery
- go AWS SDK AssumeRoleWithWebIdentity패치 PR: https://github.com/aws/aws-sdk-go/commit/2e1d76a9c838cb2bf0e03de2ced6666dc2f3eaa4
- EKS OpenID Connect identity provider: https://docs.aws.amazon.com/eks/latest/userguide/authenticate-oidc-identity-provider.html
- OIDC 프로토콜 설명: https://www.samsungsds.com/kr/insights/oidc.html
- 데브시스터즈 테크블로그: https://tech.devsisters.com/posts/pod-iam-role/
- AWS 블로그 - diving IRSA: https://aws.amazon.com/ko/blogs/containers/diving-into-iam-roles-for-service-accounts/
'전공영역 공부 기록' 카테고리의 다른 글
github action을 로컬에서 실행해보자 (0) | 2024.04.28 |
---|---|
github action workflow로 cron을 실행할 때 주의사항 (0) | 2024.04.24 |
IRSA가 등장하기 전, 설정했던 방법들 (0) | 2024.04.11 |
EKS IPv6에서 IPv4통신 원리 (0) | 2024.03.31 |
EKS IPv6 pod간 통신 (0) | 2024.03.31 |