[AWS] EKS 운영시 IRSA 와 OIDC 에 대한 이해

김지환·2023년 5월 5일
0

TL;DR

AWS EKS 에서 AWS 의 Loadbalancer를 이용하기 위해서는 aws loadbalacner controller를 설치해줘야한다.
이 때 loadbalancer controller는 aws resource인 loadbalancer 를 새로 생성하고 지우기 위해서 권한이 필요하게 되는데 이 때 사용되는 것이 IRAS ( IAM Roles for Service Account ) 개념이다. 이렇듯 EKS 상에서 AWS Service 나 Resource 를 사용하게 될 때 접근권한을 얻을 때는 항상 위 문제에 직면하게 된다. AWS Resource를 사용하면서 해당 내용을 제대로 정립하지 못했다는 생각이 들어서 한 번 정리하는 시간을 갖어봤다.

IRSA ( Identity Role as a Servic account ) 란?

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 ( OpenID Connect ) 란?

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

ID Token 또한 JWT구조이며 해당 Token의 Payload를 구성하는 Required 필드는 아래와 같다.

  • iss: 발행자의 식별자로 https url 형태
  • sub: 사용 주체자에 대한 식별자
  • aud: ID Token을 사용하게 될 대상에 대한 식별자
  • exp: 만료기한 Timestamp 형태
  • iat: 발행 시각 Timestamp 형태
  {
   "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 Resource 인증 과정

AWS OIDC

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에 대한 접근이 가능한 것이다.

Reference

https://aws.amazon.com/ko/blogs/containers/diving-into-iam-roles-for-service-accounts/
https://openid.net/connect/

profile
Developer

1개의 댓글

comment-user-thumbnail
2023년 10월 26일

IRAS가 아닌, IRSA (Identity Role as a Service Account) 입니다

답글 달기