[K8s] AWS Access Key 대신 AWS IRSA 적용하기 (+EKS Pod Identity)

우노·2024년 12월 18일
1

Practice & Trouble Shooting

목록 보기
21/21

애플리케이션에서는 AWS 서비스에 접근하기 위한 자격 증명을 통해 AWS API 요청에 서명해야한다. 현재 개발 중인 백엔드 애플리케이션에서는 Parameter Store 연결을 위해 SSM 권한을 가진 IAM 사용자의 Access Key를 발급받아 Secret으로 등록하고 Volume으로 주입했다.

env에 계정 정보를 주입해서 사용하는 게 과연 최선의 방법인가? 를 고민하다가 IRSA를 알게되어 찾아본 결과를 간단하게 적어보려고 한다.

🚧 아래의 모든 글은 AWS와 EKS 환경을 전제로 작성하였습니다.

IRSA란?

IRSA는 IAM Roles for ServiceAccounts 로써 IAM Role을 Kubernetes ServiceAccount에 연관시키는 것을 의미한다. 이를 통해서 클라우드 제공업체의 서비스, 즉 쿠버네티스 클러스터 외적인 권한을 부여할 수 있다.

클러스터에서 Pod 형태로 실행되는 애플리케이션에 IAM Role을 통해 클라우드 서비스 권한을 부여하면 애플리케이션은 파드에 할당된 ServiceAccount와 매핑된 IAM 역할의 권한을 통해 클라우드 서비스에 접근할 수 있다.

IRSA의 필요성

IRSA 없이 IAM 사용자에 특정 권한을 부여하고 Credential을 발급받아 Access_Key, Secret_Key를 등록하여 서비스에 접근할 수 있다. 쿠버네티스라면 Secret에 등록하고 env로 가져올 수 있을 것이다.

env:
  - name: AWS_ACCESS_KEY
    valueFrom:
    secretKeyRef:
      name: secret
      key: awsAccessKey
  - name: AWS_SECRET_ACCESS_KEY
    valueFrom:
    secretKeyRef:
      name: secret
      key: awsSecretKey

하지만 쿠버네티스의 Secret은 base64 encode이므로 키값 노출의 가능성이 있고 해당 키값은 별도의 만료가 없다면 영구적이기 때문에 AWS Credential이 그대로 노출된다면 보안에 매우 취약할 수 있다. 외부에서 환경변수를 관리한다고 하더라도 이 환경변수에 접근하는 크레덴셜도 비슷한 문제에 부딪힐 수 있다.

그래서 IRSA를 사용하면 다음과 같은 장점을 가질 수 있다.

  • 최소 권한 원칙
    • 역할에 최소 권한 정책 설정 가능
    • ServiceAccount로 특정 Pod에만 권한 부여 가능
  • 자격 증명 격리
    • ServiceAccount에 연결된 자격 증명만 검색 가능
  • 감사 가능성 -- AWS CloudTrail을 통해 액세스/이벤트 로깅 가능

위의 장점을 살려서 권한을 부여한 사용자의 크레덴셜을 쓰는 대신 AWS IRSA을 활용한 ServiceAccount로 애플리케이션과 클라우드 서비스를 연결해보려고 한다.

IRSA 동작 원리

사용자 Credential보다 IRSA가 안전한 이유에서는 키값이 노출되지 않는 점도 있지만 사용자의 자격 증명이 Key와 달리 임시로 부여된다는 점이 중요하다. 이를 가능하게 하는 것은 AWS Security Token Service이다.

그럼 동작 원리를 간단하게나마 알아보자.

먼저 OIDC에 대한 이해가 필요하다. OIDC는 OAuth 2.0를 기반으로 간편하게 인증을 처리하며 액세스 토큰 ➡️ 사용자 데이터의 과정을 거쳐 사용자 정보를 가져와서 처리하는 OAuth 2.0보다 편하게 사용자 인증 정보가 포함된 JWT 형태의 ID 토큰을 바로 발급 받아 사용자 인증을 수행할 수 있다. 목적은 서드파티 애플리케이션이 OIDC 제공업체의 인증 서비스를 사용하여 사용자 인증을 처리하도록 지원하는 것이다.

AWS에서는 OIDC를 통해 사용자를 중앙에서 관리할 수 있고 여러 애플리케이션에 대한 액세스 제어 시스템을 구축했다. 그리고 OIDC를 통해 서드파티 ID 인증을 지원한다.
그래서 서드파티의 ID로 AWS API 호출을 인증하고 OIDC JWT를 발급받을 수 있다.
이 토큰을 AWS STS AssumeRoleWithWebIdentityAPI API 작업에 전달하고 이를 통해 IAM 역할에 대한 임시 자격 증명을 발급받을 수 있다. AssumeRoleWithWebIdentityAPI의 세부적인 역할은 인증된 사용자에게 IAM 역할을 가정하도록 허용하고 만료가 있는 AccessKeyID와 SecretAccessKey로 서명 JWT 토큰을 서명하는 것이다. 이렇게 임시 자격 증명을 발급하여 영구적인 Credential 대신 이 자격 증명을 이용해서 AWS 서비스에 접근할 수 있는 것이다.

+ Amazon EKS Pod Identity

EC2에 인스턴스 프로필로 자격 증명을 제공하는 것과 유사하게 EKS에서 애플리케이션에 대한 자격 증명을 관리하는 기능을 제공한다. Amazon EKS Pod Identity는 추가 EKS Auth API와 각 노드에서 EKS Add-on으로 제공되고 Daemonset으로 실행되는 Agent Pod로 보안 인증 정보를 제공한다.

OIDC를 사용하지 않아 IRSA보다 구성이 간단한 방법이며 IRSA의 장점을 포함한 개선 사항을 가지고 있다.

  • IRSA와 동일한 장점
    • 최소 권한 -- Pod로만 권한 지정
    • 자격증명 격리
    • 감사
  • IRSA보다 개선된 사항
    • 독립적 운영 -- 연결 및 구성은 EKS에서, 권한 구성은 IAM에서
    • 재사용 가능성 -- 단일 IAM 역할을 여러 클러스터, ServiceAccount에 사용
    • 확장성 -- 부하가 노드당 한 번, Pod마다 중복 X

기본 개념은 IAM Role과 ServiceAccount를 연관시킨다는 점에서 IRSA와 유사하나 내부 동작 원리가 다르다. 간단히 말하자면
애플리케이션 Pod가 EKS Pod Identity 연결이 있는 ServiceAccount를 사용하는 경우 컨테이너에 환경변수를 설정하여 AWS SDK를 구성하며 EKS Pod Identity의 보안 인증 정보를 사용할 수 있다.
Pod에 ServiceAccount를 등록해두면 AWS 리소스에 접근할 때 Pod Spec을 수정하여 SDK가 적절한 Credential을 가지게 설정한다.

개인적으로는

IRSA: 클러스터에서 ServiceAccount에 역할을 연결해서 AWS에 확인 ➡️ AWS에서 확인
EKS Pod Identity: AWS에서 역할에 맞는 ServiceAccount를 생성해서 클러스터에 전달 ➡️ 클러스터에서 확인

이라는 느낌이 큰 것 같다.

그리고 IRSA에서는 클러스터마다 OIDC가 필요하지만 EKS Pod Identity는 EKS Auth API로 외부까지 나가지 않고 인가를 해결하고 여러 클러스터에서 사용한다고 해도 정책 수정이 필요없어 구성이 더 쉽게 느껴진다. 다만 EKS Pod Identity는 EKS에서만 사용할 수 있는 제한 사항이 있기 때문에 IRSA보다 클러스터 구성 외적으로 IAM 측면에서 고려해야할 사항이 늘어날 것 같다.

IRSA 적용 과정

그럼 이제부터는 Access Key를 env에 두고 사용하는 방법에 어떻게 IRSA를 적용할 수 있는지 따라가보겠다.
AWS 공식 문서의 Kubernetes 서비스 계정에 IAM 역할 할당을 참고했다.

1. OIDC 공급자 유무 확인

가장 먼저 클러스터에 대한 IAM OIDC 공급자 생성이 필요하다.

cluster_name=<cluster_name>
oidc_id=$(aws eks describe-cluster --name $cluster_name --query "cluster.identity.oidc.issuer" --output text | cut -d '/' -f 5)
aws iam list-open-id-connect-providers | grep $oidc_id | cut -d "/" -f4

위 명령어로 OIDC 제공업체 유무를 확인하고 출력값이 있다면 다음으로 넘어가고, 출력값이 없다면 위의 AWS 링크를 통해 OIDC 공급자를 생성한다.

2. IAM 정책 생성

원하는 권한을 정의한 정책을 생성한다. 현 상황에서는 SSM Parameter Store에서 파라미터를 가져오는 권한이 필요하기 때문에 아래와 같이 정의했다.

# IRSAforSSMParams
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ssm:GetParameters",
                "ssm:GetParameter",
              	"ssm:GetParametersByPath"
            ],
            "Resource": "arn:aws:ssm:*:<account_id>:parameter/*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "ssm:DescribeParameters",
            "Resource": "*"
        }
    ]
}

3. 서비스 계정 생성 및 연결

아래 명령어를 통해 원하는 정책 권한을 연결한 serviceaccount를 원하는 클러스터의 namespace에 생성할 수 있다.

eksctl create iamserviceaccount --name <service_account_name> --namespace <namespace> --cluster <eks_cluster --role-name <role_name> \
    --attach-policy-arn arn:aws:iam::054037113048:policy/<policy_name> --approve

주의해야할 것은 위에서 말하는 role은 Cluster 내부의 role이 아니라 AWS IAM의 역할이다. 실제로 클러스터에서 role과 rolebinding을 확인해도 아무 내용이 나오지 않는다.

그래서 생성된 serviceaccount를 들여다보면 AWS IAM 역할이 annotation이 걸려있다.

AWS에서도 원하는 정책으로 연결된 IAM Role이 생성된 것을 확인할 수 있다.

이미 serviceaccount가 있는 상태에서 IRSA를 원한다면 eksctl이 아니라 aws cli를 통해 직접 역할을 생성하고 annotate할 수 있다. 2단계: IAM 역할 생성 및 연결; 역할 생성 및 연결(AWS CLI)을 참고하면 될 것이다.

3. 구성 확인

aws iam get-role --role-name <role_name> --query Role.AssumeRolePolicyDocument

위 명령어를 통해 IAM 신뢰 정책을 확인한다.

이외에는 정책에 선언된 권한이 적절한지, serviceaccount에 적절한 role의 annotation이 걸렸는지, role의 생성과 권한이 적절한지 등을 확인하면 된다.

+ 애플리케이션 종속성

spring 등의 애플리케이션에서는 sts 종속성과 기타 설정이 필요할 수 있다. 하지만 본 글을 IRSA 자체에 초점을 맞추었기 때문에 생략하도록 하겠다.

IRSA 적용 결과

이제 권한을 가진 serviceaccount가 생겼으니 이를 연결해주면 된다.

기존 - AWS Credential Mount

기존에는 앞서 언급한대로 SSMFullAccess를 가진 사용자의 Credential을 발급받아 Secret으로 등록하고 volume으로 mount하여 사용했다.

apiVersion: apps/v1
kind: Deployment
metadata: ...
spec:
  ...
  template: 
      ...
      containers:
      	  ...
          volumeMounts:
            - name: aws-config
              mountPath: /root/.aws/config
              subPath: config
            - name: aws-credentials
              mountPath: /root/.aws/credentials
              subPath: credentials
      volumes:
        - name: aws-config
          secret:
            secretName: aws-config
        - name: aws-credentials
          secret:
            secretName: aws-credentials

IRSA 적용

Credential을 가진 volume을 없애고 serviceAccountName으로 serviceaccount를 적용해주었다. 이를 통해 IAM 역할과 매핑된 ServiceAccount가 허용된 범위 안에서 AWS 서비스에 접근할 권한을 가지게 된다.

apiVersion: v1
kind: Pod
metadata: ...
spec:
  serviceAccountName: ssm-param-sa
  affinity:
  	...
  containers:
  	...

마무리

사실 아직 내부적으로 어떤 토큰이 오가는지, 어떻게 통신이 구성되어있는지 자세하게 뜯어봐야할 부분이 있어서 이 글은 필요한대로 계속 업데이트 될 것 같습니다.
그래도 가볍게 IRSA를 이해하고 싶은 분들께 도움이 되었으면 좋겠습니다.
틀린 부분, 애매한 부분에 대해서는 가감없이 편하게 지적 부탁드리며 읽어주셔서 감사합니다!

참고 자료

[AWS] IAM roles for service accounts
[AWS] Kubernetes 서비스 계정에 IAM 역할 할당
[AWS] Diving into IAM Roles for Service Accounts
[AWS] EKS Pod Identity의 작동 방식 이해
[AWS] EKS Pod Identity가 포드에 AWS 서비스에 대한 액세스 권한을 부여하는 방법 알아보기
EKS Pod Identity vs IRSA | Securely Connect Kubernetes Pods to AWS Services
[LG U+] EKS Pod Identity Addon PoC
[클클콘] AWS IRSA Deep-Dive - 안지완

profile
기록하는 감자

0개의 댓글