권한 설정을 위해 필요한 지식과 우리가 사용한 방법
Tanto • Backend / DevOps Engineer
안녕하세요 👋. 저는 채널톡 데브옵스팀 탄토라고 합니다.
채널톡 개발팀에서는 내부적으로 여러 가지 개발환경 및 운영환경을 가지고 있는데요. 각 환경은 용도에 따라 여러 개의 AWS 계정에 걸쳐 운영하고 있습니다.
데브옵스팀에서는 여러 AWS 계정에 배포된 서비스들을 효율적으로 관리하기 위해 별도의 내부 서비스(이하 Dyrgo)를 개발하고 있습니다. 이 Dyrgo는 채널톡의 다른 내부 서비스들 대부분을 모니터링해야 하기 때문에 서로 다른 AWS 계정들의 리소스 및 EKS 클러스터에 대한 접근 권한이 필요합니다.
이러한 요구사항을 만족하기 위해서는 AWS IAM, K8s ServiceAccount 및 IRSA(IAM Roles for Service Accounts)을 잘 이해하고 설정해 주어야 하는데요. 권한 설정을 위해 필요한 지식들 및 저희가 사용한 방법을 이 글에서 공유해 보려고 합니다.
시작하기 전에 저희가 구성하려는 Dyrgo의 대략적인 구조도를 그려보면 아래와 같습니다. 기존 서비스들의 경우 두 개의 서로 다른 AWS 계정에 각각 ECS, EKS 형태로 서버들을 배포해 운영하고 있습니다. Dyrgo는 EKS 클러스터에서 운영하고, 해당 서비스는 각 AWS 계정에 있는 여러 AWS 리소스들 및 EKS 클러스터에 접근해야 하는 구조입니다.
Dyrgo에 적절한 권한을 부여하기 위해서는 아래와 같은 경우들을 고려해야 합니다.
EKS 내부의 다른 리소스들에 접근할 수 있는 권한 (EKS → EKS)
EKS 에서 동일한 AWS 계정의 리소스들에 접근할 수 있는 권한 (EKS → AWS)
EKS 에서 다른 AWS 계정의 리소스들에 접근할 수 있는 권한 (EKS → Another account’s AWS)
AWS 에서 EKS 내부의 리소스들에 접근할 수 있는 권한 (AWS → EKS)
(3+4) EKS 에서 다른 AWS 계정에 있는 EKS 내부의 리소스들에 접근할 수 있는 권한
(EKS → Another account’s AWS → Another account’s EKS)
첫 번째로, Dyrgo는 자기 자신이 배포되어 있는 클러스터 내부의 리소스들에 접근할 수 있어야 합니다. EKS 클러스터 내부에 있는 Pod가 다른 리소스들에 대한 권한을 얻는 것은 K8s의 자체 기능이며, 관련된 리소스로는 Role, RoleBinding, ClusterRole, ClusterRoleBinding과 ServiceAccount가 있습니다.
Role(Binding)과 ClusterRole(Binding)은 K8s 내부에서 RBAC(Role based access control)을 구성할 수 있도록 제공해 주는 기능입니다. 자세한 내용은 쿠버네티스 공식 문서를 참고해 주시면 자세한 내용이 나와있습니다.
ServiceAccount는 K8s 내부에서 Pod가 identity를 가지기위한 방법입니다. Pod가 K8s api와 상호작용할 때 자기자신의 identity를 나타내야할 때 ServiceAccount를 Pod에 할당하는 방식으로 사용할 수 있습니다.
Dyrgo는 동일 클러스터 내의 모든 리소스에 대한 읽기 권한이 필요했고, 아래와 같이 ClusterRole 및 ClusterRoleBinding, ServiceAccount를 설정하여 권한을 부여했습니다. 아래와 같이 설정할 경우 해당 ClusterRole의 권한을 부여받은 Dyrgo Server Pod는 동일한 클러스터 내의 모든 리소스에 대한 읽기 권한을 부여받게 됩니다.
# ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: dyrgo-cluster-role
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- get
- list
---
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: dyrgo-service-account
namespace: devops
---
# ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: dyrgo-cluster-role-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: dyrgo-cluster-role
subjects:
- kind: ServiceAccount
name: dyrgo-service-account
namespace: devops
---
# Pod Example
apiVersion: v1
kind: Pod
metadata:
name: dyrgo-pod
namespace: devops
spec:
serviceAccountName: dyrgo-service-account
containers:
image: dyrgo-image:tag
name: dyrgo-container
두 번째로 필요한 권한은 EKS 클러스터에 배포된 Dyrgo가 클러스터 외부의 AWS 리소스에 접근할 수 있도록 하는 것입니다.
AWS에서는 IRSA(IAM Roles for Service Accounts)라는 기능을 소개하고 있는데요. Pod가 가진 ServiceAccount와 AWS IAM의 Role을 연결해서, 특정 ServiceAccount가 특정 Role을 사용할 수 있도록 권한을 부여하는 기능입니다. 전체적인 구조를 간단히 나타내면 아래와 같습니다.
위 구조를 참고해서 AWS IAM의 작동방식을 간단히 이해하고 넘어가면 좋을 것 같은데요. 특정 서비스나 유저가 AWS API호출에 사용할 토큰을 요청(4)하게되고,
STS(Security Token Service)라는 토큰 서비스는 IAM Role에서 권한을 확인(5)합니다. 요청한 서비스가 해당 Role을 부여받을 권한이 있는지 확인 후(Trust, 6), STS는 토큰을 발급(7)해주고, 발급한 토큰을 AWS API를 호출할 때 사용(8)하게 됩니다.
이 때 IAM Role입장에서는 요청을 한 사용자나 서비스가 자신의 권한을 사용할 수 있는지 없는지를 구분하기 위해 Role의 Trust Relationship(이하 Trust)을 확인하게 됩니다. 위의 예시에서는 특정 ServiceAccount를 Identity로 하는 요청이 들어왔을 경우 Role을 사용할 수 있도록 허용해야 하는데, Role의 Trust에 아래와 같이 설정할 수 있습니다.
대충 해석하자면, Federated 필드에 있는 ARN이 가리키는 AWS Identity Provider에게 Identity 확인을 위임한다는 뜻이고, 확인된 Identity가 system:serviceaccount:devops:dyrgo-service-account
일 경우 Role을 사용할 수 있다는 의미입니다. 이 방법을 사용해서, 특정 ServiceAccount를 사용하는 Pod가 IAM Role을 사용할 수 있도록 권한을 부여할 수 있습니다.
IRSA를 사용하는 경우, Role의 Trust외에도 ServiceAccount를 Identity로 잘 넘겨주기 위한 간단한 추가설정이 필요합니다. 아래와 같이 ServiceAccount에 AWS IAM Role을 위한 annotation을 적절히 달아주게 되면, Pod가 STS에게 토큰을 요청할 때 어떤 Role을 사용할 것이고 자신의 Identity가 무엇인지 STS에 전달할 수 있게 됩니다.
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT_ID:role/dyrgo-iam-role
name: dyrgo-service-account
namespace: devops
그렇다면 Dyrgo에서 다른 AWS Account에 있는 IAM Role을 사용하기 위해서는 어떻게 해야 할까요? 이 경우에는 크게 두 가지 방법이 있습니다.
첫 번째 방법은 다른 Role의 권한을 사용할 수 있는 Role을 만들어 권한을 부여하는 방법입니다. 예를 들어, 서버가 Role A를 할당받은 상태이고 Role B가 Role A를 Trust하고 있다면, 해당 서버는 Role A를 이용해 Role B를 사용할 수 있습니다. 글로 쓰면 어렵지만, 간단히 도식으로 나타내면 아래와 같습니다.
이 경우 AWS 리소스를 사용하기 위한 최종 권한을 얻는 단계는 ServiceAccount를 Identity로 해서 토큰을 얻는 과정(4~7)과 Role A를 Identity로 해서 토큰을 얻는 과정(8~11)으로 크게 두 단계로 이루어져 있습니다.
두 번째 방법은 Role을 두 개 사용하지 않고 하나의 Role만 사용하되, Identity provider를 각 Account마다 생성해 주는 방법입니다. 사실 Identity를 제공하는 것은 EKS 클러스터가 하는 일이고, AWS Identity provider가 해주는 일은 Role이 OIDC 표준을 이용해서 Identity를 얻어올 수 있도록 URL을 연결해 주는 일입니다. 따라서 하나의 EKS 클러스터를 위한 여러 개의 Identity provider를 만들어도 문제가 없습니다. 이 역시 도식으로 나타내면 아래와 같습니다.
Production AWS Account와 Dev AWS Account에 있는 두 개의 Identity provider는 ARN만 다를 뿐 동일한 기능을 하고 있습니다. Identity provider 생성 메뉴를 보면 더 명확하게 알 수 있는 것이, 설정할 수 있는 것은 OIDC 연결을 위한 URL과 audience가 전부입니다. 따라서, 각 account에 동일한 EKS 클러스터의 OIDC URL을 갖는 OIDC Provider를 생성해 주면 되는 것이죠. Dyrgo에서는 편의를 위해 Role을 두 개 사용하는 방법이 아닌, Identity provider를 각 Account마다 생성해 주는 방법을 선택했습니다.
지금까지 다룬 내용을 토대로 Dyrgo는 동일한 EKS 클러스터 내부, 동일한 AWS 계정의 리소스들, 다른 AWS 계정의 리소스들에 대한 접근 권한을 얻게 되었습니다. 이제 남은 것은 다른 AWS 계정의 EKS 클러스터 내부의 리소스들에 접근하는 방법입니다.
이 권한을 얻기 위해서는 하나의 중간 단계에 대한 설명이 필요한데요. 다른 계정에 있는 EKS 클러스터는 AWS 리소스와는 달리 해당 AWS 계정의 Role을 얻었다고 해서 EKS 클러스터 내부의 권한까지 얻은 것이 아니기 때문입니다.
여태까지 설명한 것을 토대로, 권한이라는 것은 요청하는 쪽의 Identity와 요청받는 쪽의 Trust 두 가지 요소를 이용해서, 새로운 Identity를 얻는 과정으로도 볼 수 있습니다. 따라서 EKS 외부에서 EKS 클러스터 내부의 권한을 얻는 방법을 알면, 하나의 EKS 클러스터에서 다른 EKS 클러스터의 권한을 얻는 것도 자연스럽게 할 수 있게 됩니다.
아래 그림과 같이 AWS에서는 EKS 클러스터 내부의 권한을 AWS 리소스에게 부여하는, 기존과 반대 방향으로의 권한을 부여하는 방법도 제공하고 있습니다. 방법 자체는 한 가지는 아니지만, 채널톡에서는 aws-auth configmap을 이용하는 방식으로 권한을 부여하고 있습니다.
aws-auth에 대한 자세한 내용은 AWS 공식문서에 자세히 나와있습니다. 간단히 요약만 하면, aws-auth라는 configmap을 EKS 클러스터 내부에 생성하게 되면 IAM의 Role, User, 또는 Group을 EKS 클러스터 내부의 RBAC에서 사용할 수 있는 Group으로 mapping 해주는 역할을 합니다.
설정한 aws-auth의 예시는 아래와 같습니다.
아래와 같이 설정할 경우 dyrgo-iam-role
이라는 role을 가진 서비스는 EKS클러스터 내에서 dyrgo-group
이라는 그룹에 속하게 됩니다. 해당 그룹에 대한 권한은 EKS 내부에서 별도로 RBAC 설정을 해주면 됩니다.
apiVersion: v1
kind: ConfigMap
data:
mapRoles: |
- rolearn: arn:aws:iam::ACCOUNT_ID:role/dyrgo-iam-role
username: dyrgo-username
groups:
- dyrgo-group
다른 AWS Account에 존재하는 EKS클러스터에 접근하는 권한을 부여하려면 3번 방법과 4번 방법을 이용할 수 있습니다. 전체적인 구조를 그려보면 아래와 같아집니다. 1~4번까지의 방법을 잘 따라오신 분이라면 여태까지 했던 설정의 반복이라는 것을 금방 알아채실 수 있습니다.
Dyrgo라는 서비스를 만들고 EKS에서 운영하면서 하나의 서버가 여러 가지 환경에서의 권한을 가질 수 있는 방법이 필요했고, 앞서 소개해 드린 방법들을 이용해서 설정했습니다. 그리고 Dyrgo이라는 서비스의 내부에서 자신이 사용할 Role의 ARN을 환경에 맞게 코드에서 갈아끼울 수 있도록 구현하였고 정상적으로 잘 동작하는 것을 확인할 수 있었습니다.
데브옵스라는 업무를 하면서 여러 서비스나 환경을 모니터링하거나 변경을 하기 위해서, 또는 다른 이유로 내부 도구를 개발하는 경우가 자주 생기게 되는데요. 그럴 때마다 도구가 가져야 하는 권한을 필요한 것만 부여하도록 하는 것이 고민 포인트가 되는 경우도 많고, 중요하다고 생각합니다. 저희와 비슷한 상황에서 서비스에 권한을 부여하는 방법을 찾고 계신 분들이 계시다면 이 글이 도움이 되면 좋겠습니다.
https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html
https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html
https://docs.aws.amazon.com/eks/latest/userguide/cross-account-access.html
https://aws.amazon.com/blogs/containers/cross-account-iam-roles-for-kubernetes-service-accounts/
https://docs.aws.amazon.com/eks/latest/userguide/security-iam-service-with-iam.html
[이런 글도 추천드려요]
We Make a Future Classic Product
채널팀과 함께 성장하고 싶은 분을 기다립니다