2020. 2. 8. 18:01ㆍKubernetes

이번 글에서는 간단하게 사용법만 설명하고 다음 글에서 이론을 자세히 다루도록 하겠습니다.
1. 왜 필요한가요?
일반적으로 AWS SDK사용 시 환경변수, EC2 IAM Role 등을 통해 인증을 합니다. 하지만, K8S환경에서는 EC2 IAM Role은 적합하지 않습니다. 예로 DynamoDB 권한이 필요한 주문처리 앱, SQS 권한이 필요한 메세징 앱이 같은 클러스터에서 동작 시 모든 워커노드가 공유하는 IAM Role에 두 권한을 모두 부여해야 합니다. 이는 Least Privilege Principle에 맞지 않기에 권장되지 않습니다.
그렇다고 환경변수가 답은 아닙니다. AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY 이 두 환경변수를 K8S Secret 을 통해 마운팅 하면 작동은 잘 됩니다. 하지만, 해당 키들의 수명주기는 매우 깁니다. 유출 시 피해기간이 길어지며 키 관리또한 어렵습니다.
이런 문제에 대한 기존 해결책은 kube2iam 같은 프로젝트 였으나 AWS Metadata call 프록시 등 다소 설치하기 까다롭습니다.
이렇기에, EKS 공식 기능인 "IAM roles for service accounts" 을 추천 합니다. 이를 통해 IAM Role을 ServiceAccount와 연관시키고 그 ServiceAccount를 Pod과 연결시켜 세분화되고 안전한 권한 부여를 하는 것 입니다.
- Pod 마다 별도 Role 부여 가능
- STS Token 활용하여 키 관리 필요 X
- 1~12시간 길이의 STS Token 사용하여 키 노출 시에도 노출 범위/기간 최소화
2. 요구조건
- EKS Cluster v1.13 이상
- AssumeRoleWithWebIdentity 를 지원하는 AWS SDK (공식 가이드 페이지)
3. 적용하기
eksctl을 이용하면 보다 간편하게 적용할 수 있으나 GUI가 보다 직관적이기에 AWS 콘솔에서 진행하겠습니다.
3.1. IAM <-> EKS Cluster 연결
- EKS에서 원하는 Cluster 클릭시 아래와 같은 화면이 나오면 "OpenID Connect provider URL" 값을 복사한다
- IAM > Identity providers > "Create Provider" 버튼 클릭
- 다음과 같이 설정 값 입력
- Provider Type: OpenID Connect
- Provider URL: 1번에서 복사한 값
- Audience: sts.amazonaws.com


3.2 K8S Service Account 생성
나중에 Pod에 붙일 Service Account 를 생성합니다
apiVersion: v1 | |
kind: ServiceAccount | |
metadata: | |
name: sample-svc-ac | |
namespace: default |
3.3 IAM Role 생성
IAM 에서 Role 생성 시 다음 설정을 가져갑니다
- Trusted entity: Web identity
- Identity provider: 3.1 에서 추가한 EKS 클러스터 (같은 리전에 있는 클러스터들을 IAM에 등록 한 경우 같은 이름으로 보이니 마지막 'Review' step에서 확인이 필요합니다.
- Audience: sts.amazonaws.com

Role 생성 후 해당 Role의 'Trust relationshps' 탭 > 'Edit trust relationship' 버튼 클릭 후 다음과 같이 수정합니다.
여기서 바꿔야 할것은 총 2개입니다.

- Statement > Condition > StringEquals 에 있는 클러스터 OIDC URL뒤에 ":aud"를 ":sub"로 변경
- 예) oidc.eks.ap-northeast-1.amazonaws.com/id/AAAAAAAAAAAAAAAAAAAAAAAAAAAA:sub
- 1번에서 바꾼 Key의 값이 "system:serviceaccount:{Service Account 네임스페이스}:{ServiceAccount 이름}"
- 예) system:serviceaccount:default:sample-svc-ac
{ | |
"Version": "2012-10-17", | |
"Statement": [ | |
{ | |
"Effect": "Allow", | |
"Principal": { | |
"Federated": "여기는 이미 자동으로 완성" | |
}, | |
"Action": "sts:AssumeRoleWithWebIdentity", | |
"Condition": { | |
"StringEquals": { | |
"oidc.eks.ap-northeast-1.amazonaws.com/id/AAAAAAAAAAAAAAAAAAAAAAAAAAAAA:sub": "system:serviceaccount:default:sample-svc-ac" | |
} | |
} | |
} | |
] | |
} |
3.4 Pod에 Service Account 연결
3.4.1 Service Account에 IAM Role 정보 주입
Service Account가 어떤 IAM Role과 연결되기 알려주기 위해 다음과 같이 Service Account에 annotation을 추가해줍니다. Annotation에 들어갈 role-arn은 3.3에서 생성한 IAM Role의 ARN을 넣어줍니다.
apiVersion: v1 | |
kind: ServiceAccount | |
metadata: | |
name: sample-svc-ac | |
namespace: default | |
annotations: | |
eks.amazonaws.com/role-arn: arn:aws:iam::{AWS계정ID}:role/{ROLE이름} |
3.4.2 Pod에 Service Account 정의
다음 YAML은 AWS ALB Controller Deployment를 우리가 만든 Service Account를 사용하도록 바꾼 것입니다. 이와같이 Pod에 Service Account를 정의해주면 됩니다.
* 주의사항 - Container Image 안 AWS 서비스를 사용할 주체가 최신 AWS SDK/CLI를 사용하는지 꼭 확인하세요.
apiVersion: apps/v1 | |
kind: Deployment | |
metadata: | |
name: node-sample | |
namespace: default # Service Account와 같은 namespace 필수 | |
spec: | |
selector: | |
matchLabels: | |
app: node-sample | |
template: | |
metadata: | |
labels: | |
app: node-sample | |
spec: | |
containers: | |
- name: sample-node-container | |
image: node:latest | |
serviceAccountName: sample-svc-ac # 3.2에서 생성한 Service Account |
4. 확인하기
4.1 YAML get Pod
Service Account를 연결한 Pod를 yaml get을 이용해서 들여다 보면 아래와 같이 되어있습니다.
새로운 readonly volume이 하나 마운트 되서 STS Token 을 제공합니다. 이런 볼륨이 마운트 되어있다면 거의 모든게 끝난겁니다.
apiVersion: apps/v1 | |
kind: Deployment | |
metadata: | |
name: node-sample | |
namespace: default # Service Account와 같은 namespace 필수 | |
spec: | |
selector: | |
matchLabels: | |
app: node-sample | |
template: | |
metadata: | |
labels: | |
app: node-sample | |
spec: | |
containers: | |
- name: sample-node-container | |
image: node:latest | |
volumeMounts: | |
# 바로 여기 새로운 Volume이 하나 Mount됬습니다! | |
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount | |
name: sample-svc-ac-token-xxxxx | |
readOnly: true | |
serviceAccountName: sample-svc-ac # 3.2에서 생성한 Service Account |
4.2 환경변수 확인
kubectl -n {네임스페이스} exec {Pod 이름} printenv | grep AWS 명령어로 AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN 환경변수가 주입되어있는지 확인이 가능합니다.
- AWS_WEB_IDENTITY_TOKEN_FILE - STS Token의 위치
- AWS_ROLE_ARN - Assume 하고자 하는 IAM Role의 ARN

여기까지 확인하였으면 인프라 입장에서는 끝났습니다. 여기서부터는 SDK/CLI가 Web identity token을 받아 사용하기만 하면 됩니다.
다음 포스팅에서는 이 것이 어떻게 가능한것인지 내부 구조를 살펴보고 샘플 Spring Boot 프로젝트를 만들어 보도록 하겠습니다.