AWS EKS - Service Account 와 IAM Role 연결하기

2020. 2. 8. 18:01Kubernetes

이번 글에서는 간단하게 사용법만 설명하고 다음 글에서 이론을 자세히 다루도록 하겠습니다.

 

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. 요구조건

3. 적용하기

eksctl을 이용하면 보다 간편하게 적용할 수 있으나 GUI가 보다 직관적이기에 AWS 콘솔에서 진행하겠습니다.

3.1. IAM <-> EKS Cluster 연결

  1. EKS에서 원하는 Cluster 클릭시 아래와 같은 화면이 나오면 "OpenID Connect provider URL" 값을 복사한다
  2. IAM > Identity providers > "Create Provider" 버튼 클릭
  3. 다음과 같이 설정 값 입력
    • Provider Type: OpenID Connect
    • Provider URL: 1번에서 복사한 값
    • Audience: sts.amazonaws.com

EKS 대시보드
IAM > Identity providers 화면

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

IAM Role 생성 화면

Role 생성 후 해당 Role의 'Trust relationshps' 탭 > 'Edit trust relationship' 버튼 클릭 후 다음과 같이 수정합니다.

여기서 바꿔야 할것은 총 2개입니다.

IAM Edit Trust Relationsips 버튼

  1. Statement > Condition > StringEquals 에 있는 클러스터 OIDC URL뒤에 ":aud"를 ":sub"로 변경
    • 예) oidc.eks.ap-northeast-1.amazonaws.com/id/AAAAAAAAAAAAAAAAAAAAAAAAAAAA:sub
  2. 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 프로젝트를 만들어 보도록 하겠습니다.