AWS EKS 에서 AWS 의 Loadbalancer를 이용하기 위해서는 aws loadbalacner controller를 설치해줘야한다.
이 때 loadbalancer controller는 aws resource인 loadbalancer 를 새로 생성하고 지우기 위해서 권한이 필요하게 되는데 이 때 사용되는 것이 IRAS ( IAM Roles for Service Account ) 개념이다. 이렇듯 EKS 상에서 AWS Service 나 Resource 를 사용하게 될 때 접근권한을 얻을 때는 항상 위 문제에 직면하게 된다. AWS Resource를 사용하면서 해당 내용을 제대로 정립하지 못했다는 생각이 들어서 한 번 정리하는 시간을 갖어봤다.
AWS 는 IAM ( Identity And Access Management ) 를 이용해서 RBAC를 구현하였는데 이를 통해서 역할을 통해서 접근을 제한할 수 있게 된다. EKS 에서는 Service Account에 annotation을 통해서 IAM의 Role을 부여하고 이를 통해서 해당 Service Account를 사용하는 Service는 해당 권한을 갖을 수 있게 되는 것이다. 이개념이 IRSA이다.
Annotation을 통해서 role을 부여할 때 OIDC ( OpenID Connect) issuer URL 이 사용되게 된다. 따라서 EKS 에서 IRSA 라는 개념을 적용하여 사용하기 위한 도구로 OIDC identity provider를 만들어 주어야한다.
OIDC 는 간단한 인증 레이어로 OAuth2.0 프로토콜을 기반으로 하여 만들어졌다. Oauth2.0을 기반으로 만들어 졌다보니 동작하는 원리는 Oauth2.0의 인증과정과 흡사하다.
그렇다면 OAuth2.0 에서 더 나아가 OIDC를 사용하는 이유는 무엇일까?
OAuth2.0은 단순 인가(Authorization) 이 목적이었다면 OIDC는 인가와 인증(Authentication)을 같이 하기 위한 목적이 있다.
OAuth2.0을 통해서 받은 AccessToken에는 사용자의 신원정보는 없고 이를 통한 접근권한만 갖게된다. 따라서 AccessToken만으로는 누구인지 식별을 할 수가 없는 것이다. 따라서 이 AccessToken을 가지고 Resource Server에 요청을 하여서 얻어와야 하는데 그러면 2번의 작업을 해야하는 불리함이 생기게 된다.
OIDC는 AccessToken과 ID Token이 발행되는데 이 ID Token이 신원정보를 갖고 있는 토큰이 된다.
ID Token 또한 JWT구조이며 해당 Token의 Payload를 구성하는 Required 필드는 아래와 같다.
{
"iss": "https://server.example.com",
"sub": "24400320",
"aud": "s6BhdRkqt3",
"nonce": "n-0S6_WzA2Mj",
"exp": 1311281970,
"iat": 1311280970,
"auth_time": 1311280969,
"acr": "urn:mace:incommon:iap:silver"
}
AWS EKS 에서는 위와 같은 흐름으로 인증&인가를 진행하게 된다. 현재 내 EKS Cluster에 OIDC Provider가 설치가 돼있는지는 콘솔이나 cli로 간단하게 확인이 가능하다.
_aws console 에서의 OIDC Provider URL 확인_OIDC Provider가 설치돼있지 않다면 eksctl을 활용하거나 Console을 통해서 만들어 줄 수 있다.
우리가 Pod를 만들게 되면 자동으로 ServiceAccount( 이하 SA )의 Token 값을 projected volume 의 형태로 갖게 되고 이를 사용하게 된다. 만약 따로 ServiceAccount를 지정하지 않았다면 defualt SA의 Token을 발급받게 된다.
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: eks-iam-test
spec:
containers:
- name: my-aws-cli
image: amazon/aws-cli:latest
command: ['sleep', '36000']
restartPolicy: Never
EOF
다음과 같이 실행하고 sleep을 걸어 컨테이너가 죽지 않게 만들고 default로 projected 된 token을 확인해볼 수 있다.
SA_TOKEN=$(kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
jwt decode $SA_TOKEN --json --iso8601
{
"aud": [
"https://kubernetes.default.svc"
],
"exp": 1714828380,
"iat": 1683292380,
"iss": "https://oidc.eks.*************",
"kubernetes.io": {
"namespace": "default",
"pod": {
"name": "eks-iam-test",
"uid": "689dab37-3908-461f-8c76-9a257ec1ebd7"
},
"serviceaccount": {
"name": "default",
"uid": "7132de9a-c17e-48a7-a3b9-3265a03d7d44"
},
"warnafter": 1683295987
},
"nbf": 1683292380,
"sub": "system:serviceaccount:default:default"
}
값을 살펴보면 aud 로 kubernetes.default.svc 로 요청이 가는 것을 확인할 수 있다. 해당 위치가 바로 kubernetes api server 의 주소이다.
이렇게 kubernetes 안의 service들도 OIDC를 이용한 자격 인증,인가를 사용하고 있다.
이제 aws loadbalancer controller를 한 번 살펴보자.
눈여겨 봐야할 부분은 loadbalacner 가 사용할 IAM 을 만들고 해당 IAM 을 IdP 와 연동시키는 부분이다. IAM에 정책은 우리가 적용할 Access Scope를 할당하게 되고 Trust Relationship 항목에 OIDC Provider URL을 설정하고 sub에 설정할 ServiceAccount를 설정하는 부분이다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::********:oidc-provider/oidc.eks.**********"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.***************:sub": "system:serviceaccount:kube-system:ebs-csi-controller-sa"
}
}
}
]
}
aws loadbalancer controller 의 SA 를 이용하는 Pod를 임시로 만들어 보면 다음과 같이 2개의 projected volume serviceaccount가 생성된다.
해당 token을 decode 해보면 아래와 같은 정보를 담고있다.
{
"aud": [
"sts.amazonaws.com"
],
"exp": 1683381966,
"iat": 1683295566,
"iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/E78A7816E4004CFC408FAD44D5DFCF6B",
"kubernetes.io": {
"namespace": "kube-system",
"pod": {
"name": "eks-iam-test",
"uid": "d241c835-9c2f-4596-b4d9-3bbbf322396e"
},
"serviceaccount": {
"name": "aws-load-balancer-controller",
"uid": "060cd287-c276-4d8e-9825-6aa69f348229"
}
},
"nbf": 1683295566,
"sub": "system:serviceaccount:kube-system:aws-load-balancer-controller"
}
우리가 알 수 있는 가장 큰 차이점은 aud이다. ( loadbalancer SA를 사용해서 바뀐 차이도 있지만 핵심은 service account 에 적용된 annotation을 통한 새로운 OIDC Token 에 주목하자! )
"sts.amazonaws.com" 으로 대상이 변경됐다.
해당 Pod는 바로 이렇게 발급된 Token을 이용하여서 aws resource에 대한 접근이 가능한 것이다.
https://aws.amazon.com/ko/blogs/containers/diving-into-iam-roles-for-service-accounts/
https://openid.net/connect/
IRAS가 아닌, IRSA (Identity Role as a Service Account) 입니다