Kubernates에 Pod 기반의 IAM Role 접근 제어 통합하기
Hyojun Kim — 김효준
@therne
EKS는 클러스터 단위의 IAM Role 부여는 지원하지만, 불행하게도 파드 단위로의 세부적인 Role 부여는 아직 지원하지 않는다. 덕분에 WAS는 Beanstalk만 접근할 수 있게, 워커는 Redshift만 접근할 수 있게... 이런 식의 세밀한 접근제어가 불가능하다. 아니, ECS보다 좋다고 해서 EKS로 이사왔더니 이런 봉변이...
왜 Kiam인가?
파드 단위의 IAM Role 지원은 AWS 팀에서 열심히 공식 지원을 위해 노력하고 있긴 하지만 2019년 4월 현재 아직도 감감무소식이다... 빨리 좀 나와!! 그래서 대안으로 많이 쓰이는게 쿠버네티스의 플러그인인 kube2iam이다.
Kiam의 구조
kiam은 마스터 노드에 붙는 서버와 각 노드에 붙는 에이전트로 나뉘어진다.
오직 소수의 Kiam 서버만 IAM 서비스에 접근할 수 있고 에이전트들은 단순히 gRPC로 서버와 통신하는 역할밖에 하지 않기 때문에 훨씬 더 보안이 뛰어나다. 특히 이 구조는 대규모의 클러스터로 갈수록 보안 통제 및 리소스 활용 측면에서 훨씬 용이해지게 되며, TLS 통신 지원 및 IAM 세션 캐싱 등의 다양한 기능을 제공할 수 있게 된다.
파드에서 어떤 IAM Role을 쓰고 싶을땐 어떻게 할까? 파드의 메타데이터에 태그 하나만 달아주면 끝이다. Kiam Agent가 해당 태그를 자동으로 스캔하면서 Role에 알맞은 크리덴셜을 자동으로 부여하므로, 설정 단 한줄만으로 파드 안에선 AWS 접근 및 권한 제어가 가능하다.
kind: Deployment
...
annotations:
iam.amazonaws.com/role: MyS3ReadRole
YAML
복사
import boto3
s3 = boto3.resource('s3')
s3.Bucket('my-secret-bucket')
.download_file('hello_iam.jpg')
Python
복사
한가지 더 특이한 점이라면, Kiam은 EC2 메타데이터 API를 후킹한다. 일반적인 EC2 인스턴스에선 169.254.169.254 IP로 접근하면 해당 인스턴스 정보 (메타데이터) API를 제공해주는 숨겨진? 기능이 있는데, Kiam은 이 접근을 기본적으로 차단하기 때문에 파드가 원치 않는 노드 정보에 접근하는걸 통제할 수 있다.
kube2iam 대비 장점
일반적으로 많이 쓰이는 kube2iam보다 어떤 점이 더 좋을까? 자세한 건 다음 글을 참조하면 좋을 것 같다.
1.
kube2iam에 있는 고질적인 문제들이 없음
2.
Server-Agent모델로 인해 훨씬 보안이 뛰어나며 (+ TLS 지원!) 리소스 효율적임
3.
IAM 요청을 서버가 캐싱해주기 때문에 성능 UP
성능에 관한 주의사항: 최근 Kiam을 사용 시 AWS 리소스 접근 속도가 2배 ~ 3배정도 느려지는 버그가 발견되었다고 한다. AWS Java SDK의 버그로 인해서 생기는 문제라는데, --session-duration 플래그를 15분 이상으로 설정하면 문제가 해결된다고 한다.
kiam 설치하기
이렇게 좋은 Kiam을 EKS에 설치해보려고 했으나... 공식 가이드대로는 절대 설치가 되지 않는다. Kiam의 버전업과 EKS의 발전으로 인해 가이드의 상당수가 업데이트가 되지 않고 불친절하게 나와 있어서, 한참을 해매다가 여러 이슈 코멘트와 글을 조합해서 겨우 설치에 성공했다.
1. IAM 설정
Kiam은 서버 - 에이전트 구조이다. 서버는 sts:AssumeRole을 이용해 특정 Role에 대한 임시 세션을 발급받아놓고, 에이전트가 요청할때마다 업데이트해주는 식이다. 그래서 Kiam Server가 STS를 통해 Role을 요청할 수 있도록 Kiam Server에 권한을 부여해줘야 한다.
resource "aws_iam_role" "server_role" {
name = "kiam-server"
description = "Role the Kiam Server process assumes"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "<<클러스터에 부여된 Role ARN>>"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_policy" "server_policy" {
name = "kiam_server_policy"
description = "Policy for the Kiam Server process"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Resource": "*"
}
]
}
EOF
}
resource "aws_iam_policy_attachment" "server_policy_attach" {
name = "kiam-server-attachment"
roles = ["${aws_iam_role.server_role.name}"]
policy_arn = "${aws_iam_policy.server_policy.arn}"
}
YAML
복사
여기서 <<클러스터에 부여된 Role ARN>> 부분을 현재 EKS 클러스터에 부여된 Role의 ARN으로 바꿔줘야 한다. 만약 잘 모르겠으면 kubectl describe configmap -n kube-system aws-auth를 쳐보면 무슨 Role이 붙었는지 알 수 있다.
해당 HCL 파일을 terraform apply로 적용해주면 AWS에서 IAM 설정 완료!
2. TLS 인증서 발급하기
Kiam 구조 특성상, 서버와 에이전트간에 암호화된 통신을 구축해야 IAM 권한 요청에 대한 중간 감청 및 MITM 공격 등을 방지할 수 있다. 이를 위해 TLS 인증서를 생성해야 한다.
이미 Let's Encrypt 등에서 발급한 TLS 인증서가 있다면 kiam-agent-tls와 kiam-server-tls라는 이름의 Secret으로 쿠버네티스에 올려놓으면 된다. 하지만 없다면 수동으로 생성하거나, cert-manager 익스텐션을 통해 발급하고 자동 관리하는 방법이 있다.
Case 1. 인증서 수동 생성하기
$ go get -u github.com/cloudflare/cfssl/cmd/...
Plain Text
복사
1.
인증서 정의 파일을 만든다.
ca.json
{
"CN": "Kiam CA",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "UK",
"L": "London",
"O": "uSwitch",
"OU": "WWW",
"ST": "London"
}
]
}
JSON
복사
server.json
{
"hosts": [
"kiam-server",
"localhost",
"kiam-server:443",
"127.0.0.1",
"::1"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "UK",
"L": "London",
"O": "uSwitch",
"OU": "WWW",
"ST": "London"
}
]
}
JSON
복사
agent.json
{
"CN": "Kiam Agent",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "UK",
"L": "London",
"O": "uSwitch",
"OU": "WWW",
"ST": "London"
}
]
}
JSON
복사
2. 인증서 페어를 생성한다.
$ cfssl gencert -initca ca.json | cfssljson -bare ca
$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem server.json | cfssljson -bare server
$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem agent.json | cfssljson -bare agent
Plain Text
복사
3. 다 만든 인증서 페어를 쿠버네티스에 Secret으로 등록해주자.
$ kubectl create secret generic kiam-server-tls -n kube-system \
--from-file=ca.pem \
--from-file=server.pem \
--from-file=server-key.pem
Plain Text
복사
$ kubectl create secret generic kiam-agent-tls -n kube-system \
--from-file=ca.pem \
--from-file=agent.pem \
--from-file=agent-key.pem
Plain Text
복사
이렇게 하면 수동으로 TLS 인증서를 준비할 수 있다.
Case 2. cert-manager를 이용해 인증서 자동 생성 및 관리
cert-manager는 다양한 발급자로부터 인증서의 프로비져닝과 갱신을 자동으로 수행해주는 에이전트다. 수동으로 인증서를 관리하는 번거로움 없이, 쿠버네티스 위에서 자체적으로 인증서를 발급하고 관리할 수 있다.
매우 간편하지만, 프로덕션에서 충분히 테스트된 코드는 아니므로 실제 사용에는 신중한 보안 검토를 요한다.
설치 방법
3. 본격 설치하기
kiam 공식 문서에는 직접 설치방법이 설명되어 있지만, EKS에서는 왜인지 모르겠지만 자잘한 문제들이 많이 생겨서 돌아가지 않는다. 따라서 편리하게 Helm으로 설치하는게 정신건강에 좋은 것 같다ㅎㅎ
일단, 컨픽을 채우기 위해 values.yaml 파일을 작성한다. 가장 중요한 건 server.assumeRoleArn의 <<YOUR_CREATED_ROLE_ARN_HERE>> 부분에 앞서 생성했던 kiam-server의 ARN 명을 넣어줘야 한다!
agent:
tlsSecret: kiam-agent-tls
log:
level: debug
gatewayTimeoutCreation: 1s
extraEnv:
GRPC_GO_LOG_SEVERITY_LEVEL: info
GRPC_GO_LOG_VERBOSITY_LEVEL: 8
host:
interface: "!eth0"
iptables: true
extraHostPathMounts:
- name: ssl-certs
hostPath: /etc/pki/ca-trust/extracted/pem
mountPath: /etc/ssl/certs
readOnly: true
tlsCerts:
certFileName: agent.pem
keyFileName: agent-key.pem
caFileName: ca.pem
server:
assumeRoleArn: "<<YOUR_CREATED_ROLE_ARN_HERE>>"
tlsSecret: kiam-server-tls
extraEnv:
GRPC_GO_LOG_SEVERITY_LEVEL: info
GRPC_GO_LOG_VERBOSITY_LEVEL: 8
tlsCerts:
certFileName: server.pem
keyFileName: server-key.pem
caFileName: ca.pem
log:
level: debug
gatewayTimeoutCreation: 1s
extraHostPathMounts:
- name: ssl-certs
hostPath: /etc/pki/ca-trust/extracted/pem
mountPath: /etc/ssl/certs
readOnly: true
useHostNetwork: true
YAML
복사
•
위에서 cert-manager를 사용했다면 server와 agent 안의 tlsCerts를 전부 다음과 같이 변경해줘야 한다.
tlsCerts:
certFileName: tls.crt
keyFileName: tls.key
caFileName: ca.crt
YAML
복사
설정이 끝났으면 이제 Helm으로 설치만 하면 끝이다.
$ helm install stable/kiam --name kiam -f values.yaml --namespace kube-system
JSON
복사
마지막으로, 반드시 사용하려는 네임스페이스에 사용하려는 Role 범위에 대한 어노테이션을 걸어줘야 Kiam을 쓸 수 있다. 예를 들어 default 네임스페이스 안에서 모든 Role을 사용할 수 있게 하려면 이렇게 지정해준다.
$ kubectl annotate namespace default iam.amazonaws.com/permitted='.*'
namespace "default" annotated
Plain Text
복사
4. 테스트하기
Role이 제대로 잘 붙는지 볼까? AWS CLI가 담긴 파드를 생성해서 한번 테스트해보자. AWS_DEFAULT_REGION만 알맞게 설정해준 후, kubectl apply!
apiVersion: apps/v1
kind: Deployment
metadata:
name: aws-iam-tester
labels:
app: aws-iam-tester
spec:
selector:
matchLabels:
app: aws-iam-tester
template:
metadata:
labels:
app: aws-iam-tester
annotations:
iam.amazonaws.com/role: KiamTesterRole
spec:
containers:
- name: aws-iam-tester
image: garland/aws-cli-docker:latest
command:
- /bin/sleep
args:
- "3600"
env:
- name: AWS_DEFAULT_REGION
value: ap-northeast-1
YAML
복사
유심히 봐야될 부분은, 이제 파드에다가 Role을 지정할 수 있다는 점이다!
annotations:
iam.amazonaws.com/role: KiamTesterRole
YAML
복사
kubectl apply를 때려주고, 쿠버네티스 대시보드에 들어가서 쉘을 실행시킨 후 EC2 Metadata API와 AWS CLI를 통해 Role을 점검해주자.
어노테이션을 떼면 바로 해당 Role이 사라지며, 해당 파드는 즉시 크리덴셜을 잃게 된다.
$ kubectl annotate pods aws-iam-tester-9bcbf4f5b-dsm5h iam.amazonaws.com/role-
pod "aws-iam-tester-9bcbf4f5b-dsm5h" annotated
root@mypod:/data $ aws s3 ls s3://my-secret-bucket
Unable to locate credentials. You can configure credentials by running "aws configure".
Plain Text
복사
마무리
kiam도 매우 좋은 구조를 가지고 있으며 프로덕션에서도 잘 사용되고 있지만, AWS 공식 지원에는 비할 바가 못될 수 있다. EKS에선 현재 쿠버네티스 위의 베타 기능인 ServiceAccountToken Projection을 통해 AWS IAM을 쿠버네티스의 RBAC 시스템 안에 심리스하게 통합하려는 계획을 세우고 공식 지원을 위해 개발 중이다.
언젠가 공식 지원이 되면 kiam은 자연스럽게 필요없어질 수 있겠지만, 그럼에도 현재 AWS 위에서 쿠버네티스를 사용할 때 더할 나위 없이 가장 좋은 솔루션임에는 분명하다. 문서화가 더 잘 되었다면 훨씬 좋았겠지만 그래도 다른 사람들이 더 쉽게 구축할 수 있도록 부족하게나마 가이드를 남겨본다.
ᴡʀɪᴛᴇʀ
Hyojun Kim @therne
Data R&D Engineer