🧩 개요
Kubernetes를 클라우드 환경에서 구성할 때, 일반적으로 AWS EKS, Google GKE, Azure AKS와 같은 Cloud Service Provider(CSP)의 관리형 Kubernetes를 사용할 것이다. 우리는 이러한 서비스를 통해 Kubernetes Cluster의 Control Plane 및 Worker Node에 대한 고민 없이 관리를 자동화할 수 있게 된다.
만약, Cluster를 업그레이드하거나, 네트워크 설정을 온프레미스의 환경에서 작업하게 된다면, 엄청나게 머리 아픈일이 아닐 수 없다.
클라우드 환경에서 Kubernetes로 구성한 서비스들은 각 어플리케이션에 필요한 네트워크 설정, 권한 관리 필수적인 것이다. 특히, 권한 관리에서는 최소 권한 원칙(Zero Trust)을 지키는 것이 가장 중요한데. 예를 들어, 파일 업로드를 담당하는 서버가 S3 Bucket에 접근할 수 있도록 구성해야 한다면, 모든 어플리케이션에 권한을 부여하기 보다는 필요한 어플리케이션에만 제한적으로 권한을 부여해야할 것이다.
이를 위해, AWS에서는 IRSA (AWS IAM Roles for Service Accounts) 기능을 제공하고 있다. IRSA를 사용한다면 원하는 어플리케이션이 필요한 AWS 리소스에 접근할 수 있는 권한을 할당할 수 있으며, EKS Cluster 내에서 어플리케이션 별로 필요한 최소 권한만 부여할 수 있게 된다.
또한, 인프라 관리자의 입장에서도 어느 어플리케이션이 어떤 Resource에 접근했는지 모니터링할 수 있어 더욱 보안을 강화할 수 있다.
이번 게시글에서는 IRSA와 관련된 개념을 하나씩 살펴보면서, Terraform을 통해 EKS와 IRSA를 생성하고, 어플리케이션을 구성하여 S3 Bucket에 접근할 수 있도록 하나의 작은 프로젝트를 시작해보도록 하자.
🔗 IRSA (AWS IAM Roles for Service Accounts)
IRSA (AWS IAM Roles for Service Accounts)는 AWS IAM Role을 Kubernetes의 Service Account와 연결하여 AWS STS(Security Token Service)를 통해 일시적인 권한을 발급받을 수 있게 한다.
만약, EKS Cluster 내의 특정 Pod가 필요한 AWS Resource에 접근해야 한다면, IRSA를 통해 원하는 권한을 획득할 수 있게 될 것이다.
🔑 AWS IAM Role
AWS IAM Role(역할)은 특정 AWS Resource의 작업을 수행할 수 있는 Policy(정책)을 지정하여 권한을 할당하는 서비스이다.
AWS IAM Role은 일반 사용자나 어플리케이션에 직접 연결할 수도 있지만, 필요 시 특정 리소스에 임시로 할당하여 권한을 부여할 수도 있다. 예를 들어, EC2 인스턴스가 S3 Bucket에 접근할 수 있도록 권한을 부여하려면 필요한 정책을 정의하고 EC2 인스턴스에 IAM Role을 연결하여 사용하거나, EC2 인스턴스가 필요 시 원하는 권한을 요청(Assume Role)하여 일시적인 권한을 획득하는 방식으로 사용할 수 있다.
🔓 AWS STS
AWS STS(Security Token Service)는 특정 AWS 리소스를 사용할 수 있는 '임시 보안 자격 증명'을 생성하는 서비스이다.
서비스의 이름과 같이 생성된 '임시 자격 증명'은 단기적으로만 사용할 수 있으며, 자격 증명이 만료 되면 그 어떠한 종류의 권한 요청도 실패하도록 구성되어 있다.
IAM User 또는 AWS Resource는 AWS STS를 통해 '임시 자격 증명'을 요청할 수 있으며, 해당 자격 증명에 특정 Role(역할)에 설정된 권한을 부여받아 사용할 수 있게 된다. 이를 통해 '단기적으로 사용할 수 있는 권한을 소유한 임시 자격 증명'을 생성할 수 있게 되며, 이 과정 자체를 Assume Role 이라고 부르게 된다.
☁ Assume Role
Assume Role은 특정 Role(역할)을 일시적으로 부여받아 AWS 리소스에 접근할 수 있도록 도와준다.
Assume Role은 IAM User에 영구적인 Role을 부여하는 것과 달리, Assume Role은 일시적인 권한을 부여하여 지정된 시간 이후에는 해당 권한이 제거되므로 보안을 강화할 수 있다.
예를 들어, EC2 인스턴스가 S3 Bucket에 접근하기 위해 Assume Role을 사용하면, EC2는 일시적으로만 S3 Bucket을 사용할 수 있게 된다. 이는 IAM User뿐만 아니라, 다양한 AWS 서비스에서도 사용 가능하다.
👤 Kubernetes Service Account
Kubernetes의 Service Account는 Cluster 내 특정 Pod가 Kubernetes API Server(kube-apiserver)와 통신할 수 있는 권한을 부여하기 위해 사용한다.
Service Account는 네임스페이스마다 생성되며, 특정 Pod에 권한을 부여하기 위해 동일한 네임스페이스 내에서 Service Account를 생성하여 연결할 수 있다. IRSA는 이 Kubernetes Service Account와 AWS IAM Role을 연결하여 특정 Pod에 AWS Resourcce에 대한 접근 권한을 제공할 수 있게 된다.
🕛 제로부터 IRSA 시작하기
이번 예시는 Terraform을 이용해 EKS와 IRSA를 구성하고, Application을 수동으로 배포하여 서비스가 IRSA를 이용해 권한을 획득하고 사용하는 것에 대해 확인해보고자 한다.
해덩 예시와 관련된 모든 코드는 해당 Github Repository를 참조하길 바란다.
1️⃣ AWS Resource 구성하기
지금부터 구성하는 모든 인프라는 서울 리전(ap-northeast-2)를 기준으로 $135.05 비용이 발생하므로 작업을 완료한다면 삭제하는 것을 꼭! 꼭! 잊지 말자.
꼭!
🌱 EKS 생성하기
EKS Cluster를 구성하기 위해 Terraform으로 AWS Resource를 한땀 한땀 작성할 수 있지만, 간단한 예시를 구성하기 위해 이미 구성된 Module을 이용하여 아주 손쉽게 생성할 예정이며, terraform-aws-modules/eks/aws를 이용해 VPC와 EKS를 함께 구성할 예정이다.
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "20.29.0"
cluster_name = "eks-s3-irsa"
cluster_version = "1.31"
vpc_id = module.vpc.vpc_id
...
}
🪴 S3 생성하기
이번 IRSA 예시에서는 EKS에서 생성된 어플리케이션이 S3 Bucket의 파일 목록을 조회하는 작업을 진행할 것이다.
resource "aws_s3_bucket" "this" {
bucket = "test-irsa-s3"
}
여기서, S3 Bucket은 Terraform을 통해 생성한 이후 AWS Console에서 직접 샘플 파일을 업로드해 Bucket에 테스트 데이터를 추가하도록 하자!
2️⃣ IRSA 구성하기
IRSA를 구성하기 위해서는 AWS와 같은 Cloud Service Provider(CSP)의 공식 Terraform Provider와 더불어, Kubernetes의 Service Account를 생성하기 위한 별도의 Terraform Provider(kubernetes)가 필요하게 된다. 개인적으로 CSP의 공식 Provider 외에는 사용을 지양하고 있는데, 이는 완성도의 차이도 있겠지만, 내부 로직 이슈로 인해 Terraform State 관리에서 문제가 발생할 수 있기 때문이다.
하나의 예시를 들어보자, kubernetes Provider를 통해 Service Account를 생성한 후, 수동으로 삭제하는 작업을 진행한다면, 이후 terraform apply
명령어를 실행할 때 Terraform State가 리소스의 실제 상태를 반영하지 못해 오류가 발생할 수도 있다. 이러한 문제를 방지하기 위해서 필자는 최대한 CSP의 공식 Provider를 사용하는 것을 지향하고 있다.
다만, 하나의 예외가 있는데, 이는 배포 후 유지 관리를 하지 않는 경우를 한정하여 사용하고 있다.
이번 예시는 단순히 예시를 보여주기 위한 것이므로, kubernetes Terraform Provider를 이용해 Service Account를 생성하는 작업을 진행할 예정이다.
🌱 AWS IAM Role 생성하기
먼저, test-irsa-s3
라는 이름의 S3 Bucket의 파일을 조회할 수 있는 Policy를 생성하도록 하자. 이후, 생성한 Policy를 이용해 IAM Role을 구성하고, Kubernetes Service Account가 해당 Role을 Assume할 수 있도록 신뢰 관계(Trust Relationships)를 설정해야 한다. 해당 설정을 바탕으로 Kubernetes 어플리케이션이 특정 AWS 리소스에 접근할 수 있도록 필요한 권한을 요청하고, 부여해줄 수 있게 된다.
여기서, 신뢰 관계는 AWS 리소스에 접근을 허용할 주체를 지정하는 아주 중요한 설정이므로, 이를 통해 지정된 Kubernetes Service Account만이 지정된 S3 Bucket에 접근할 수 있는 권한을 획득할 수 있도록 구성할 것이다.
resource "aws_iam_role" "get_s3_bucket" {
name = "getS3BucketIRSARole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = "arn:aws:iam::<ACCOUNT_ID>>:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/<OIDC_ID>"
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"oidc.eks.ap-northeast-2.amazonaws.com/id/<OIDC_ID>:aud" = "sts.amazonaws.com"
"oidc.eks.ap-northeast-2.amazonaws.com/id/<OIDC_ID>:sub" = "system:serviceaccount:default:get-s3-bucket-sa",
}
}
}
]
})
}
여기서, 아주 중요하게 여겨야하는 것은 StringEquals
부분이다. 해당 부분은 EKS에서 구성될 Service Account의 정보를 작성하게 되는 것인데, 해당 예제에서는 default
네임스페이스에 존재하는 get-s3-bucket-sa
Service Account를 지정한 것이다.
다음에 생성할 Service Account와 명확하게 일치해야만, Service Account가 getS3BucketIRSARole
을 Assume할 수 있게 된다.
🪴 AWS Service Account 생성하기
Service Account는 kubernetes Terraform Provider를 사용하여 default
네임스페이스 내에 get-s3-bucket-sa
라는 이름으로 생성할 것이다. 여기서 중요시 여겨야할 것은 annotations
부분에서 어떤 AWS IAM Role과 연결하여 IRSA를 구성할지 지정하는 것이다.
resource "kubernetes_service_account_v1" "get_s3_bucket" {
metadata {
name = "get-s3-bucket-sa"
namespace = "default"
annotations = {
"eks.amazonaws.com/role-arn" : "arn:aws:iam::<ACCOUNT_ID>:role/getS3BucketIRSARole"
}
}
}
3️⃣ IRSA 사용하기
🌱 어플리케이션 배포하기
이번에 배포할 어플리케이션은 Express.js로 구성된 S3 Bucket의 파일 목록을 조회하는 서버이다. 해당 서버는 3000
번 Port에서 GET /files
요청을 통해 S3 Bucket의 파일 목록을 출력한다. 해당 서버는 Docker Hub에 Public으로 업로드되어 있으며, 상세한 서버 구성은 Github Repository를 참조하길 바란다.
해당 어플리케이션은 Kubernetes Deployments를 통해 구성하였다. 이전에 생성한 Service Account를 ServiceAccountName
부분에 할당하였고, 이를 통해 Pod는 Service Account를 통해 AWS IAM Role을 Assume하여 최종적으로 test-irsa-s3
라는 S3 Bucket의 파일 목록을 조회할 수 있게 될 것이다.
다음으로 직접 EKS에 Deployment를 배포하는 작업을 진행해보자. 아래의 명령어를 순서대로 입력한다면 kubectl이 생성된 AWS EKS Cluster를 등록할 것이고, 예시 Deployment를 배포하는 작업을 진행할 것이다.
# aws-cli를 이용해 EKS Cluster 정보를 `.kube/config`에 등록한다.
$ aws eks update-kubeconfig --region ap-northeast-2 --name eks-s3-irsa
# kubectl을 이용해 EKS Cluster에 구성된 Node 목록을 조회할 수 있다.
$ kubectl get nodes
# kubectl을 이용해 예시 Deployment를 배포한다.
$ kubectl apply -f get-s3-aws-sdk.deployment.yaml
# kubectl을 이용해 배포되는 Pod를 조회한다.
$ kubectl get pods -o wide --watch -A
🪴 IRSA 테스트 하기
마지막으로, 배포된 Deployment에 접근하여 IRSA 설정이 제대로 작동하는지 확인해보도록 하자. 현재 EKS Cluster는 서비스 및 인그레스가 설정되지 않아 외부에서 접근할 수 없는 상태인데, kubectl
을 이용한다면 직접 Pod의 Bash shell에 접근하여 어플리케이션에 요청을 수행할 수 있다. 그렇다면, 직접 S3 Bucket의 파일 목록을 조회할 수 있는지 확인해보도록 하자.
# kubectl을 이용해 배포된 Pod의 bash shell에 접근한다.
kubectl exec -it get-s3-aws-sdk-ccb8d4654-bwzw8 /bin/sh
# curl 명령어를 이용해, S3 Bucket에 파일이 존재하는지 검증하는 작업을 수행한다.
curl localhost:3000/files
Pod 내에서 curl localhost:3000/files
명령을 실행하여 S3 Bucket의 파일 목록을 정상적으로 조회할 수 있었다. 이는 IRSA를 통해 Service Account가 IAM Role을 Assume 하여 필요한 권한을 획득하고, 이를 이용하여 S3 Bucket에 접근할 수 있었기 때문이다.
🥕 마지막으로
처음 Kubernetes를 공부하면서 새로운 개념들이 많아 적응하기가 너무 어려웠다. 특히, 빠르게 기술을 적용해야 하는 상황에서 IRSA는 가장 난해한 부분 중 하나였는데, 처음에는 Service Account 개념도, Kubernetes 내부 동작도 제대로 이해하지 못했기에 IRSA를 학습하는 데 많은 시간이 소요되었다. 하지만, 하나씩 직접 만들어보고 노력하다보니, 이제는 IRSA를 사용하는 것이 어느정도 익숙해진 것 같다.
이 게시글이 과거의 나와 같이 Kubernetes와 IRSA를 처음 접하는 사람들에게 도움이 되길 바라며, 이번 게시글을 마치도록 하겠다.
'Infrastructure' 카테고리의 다른 글
ALB를 이용해 EKS 단일 장애점 개선하기 (이론) (0) | 2024.04.14 |
---|---|
ISMS 인증을 받기 위한 인프라 개선 요구사항 (0) | 2024.03.17 |
GitOps 뉴비의 Argo CD 여행기 (2) | 2024.02.04 |