Work/개발 노트

[Kubernetes] Cross Account에 IRSA를 적용하기

★용호★ 2023. 10. 22. 01:04

이 게시글은 Database Operator In Kubernetes study (=DOIK) 2기에서 스터디한 내용 중 ExternalDNS에 대한 내용을 공부하며 기록한 내용입니다.

 

External DNS란?

ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.

쿠버네티스의 External DNS는 Service와 Ingress 생성 시 도메인 연동을 할 수 있도록 하는 애드온 입니다. 따라서 기본 내장되어 있지 않기 때문에 별도의 설치가 필요합니다. 기존에는 생성된 로드밸런서의 DNS를 수작업으로 Route53과 같은 도메인 서비스에 연동을 해줘야 했다면 External DNS로 쿠버네티스의 리소스를 통해 DNS 레코드를 동적으로 제어할 수 있습니다.

 

EKS 클러스터가 존재하는 Account와 DNS 레코드를 관리하는 Account가 다르다면?

저는 현재 EKS 클러스터를 운영하는 Account와 Route53으로 DNS를 관리하는 Account가 분리되어 있습니다. 따라서 External DNS로 IRSA를 적용하여 Cross-Account 간 리소스 접근을 위해서는 권한 설정이 필요합니다.

아래와 같이 EKS 클러스터가 존재하는 Source Account와 Route53 관리를 할 Target Account가 있다고 가정합니다.

AWS CLI의 Credential은 default Profile은 Source Account로 지정하고, Target Account는 별도의 Profile을 사용합니다.

Command 명령을 실행할 Shell 환경은 이미 필요한 권한이 할당되어 있다고 가정하고 진행합니다.

 

1. 터미널로 접속하여 초기 셋팅을 진행합니다.

# developer Account의 리소스들에 대한 변수 설정
SOURCE_ACCOUNT_ID=1111111111
AWS_REGION=ap-northeast-1
EKS_CLUSTER_NAME=eks-cluster
SA_NAME=external-dns
NAMESPACE_NAME=external-dns

# shared_content Account의 리소스들에 대한 변수 설정
TARGET_ACCOUNT_ID=9999999999
TARGET_AWS_PROFILE=target
TARGET_IAM_ROLE_NAME=Route53RoleForEKS
TARGET_IAM_POLICY_NAME=Route53PolicyForEKS

kubectl create ns $NAMESPACE_NAME

2. Target Account는 EKS 클러스터의 Pod로부터 AWS 사용 권한을 수신 받게 되는데 이때 OIDC Provider가 사용됩니다. Source Account의 EKS 클러스터에 OIDC Connector Provider를 연결하겠습니다.

# OIDC Provider 연결
eksctl utils associate-iam-oidc-provider --cluster $EKS_CLUSTER_NAME --approve

# OIDC Provider URL 확인
aws eks describe-cluster --name $EKS_CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text | rev | cut -d'/' -f 1 | rev

# 변수 설정
SOURCE_OIDC_ID=$(aws eks describe-cluster --name $EKS_CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text | rev | cut -d'/' -f 1 | rev)
SOURCE_OIDC_PROVIDER=$(aws eks describe-cluster --name $EKS_CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text | sed -E 's/^\s*.*:\/\///g')

 

3. Target Account에 2번에서 생성한 OIDC Provider를 사용하여 자격 증명 공급자를 생성하도록 하겠습니다.

[IAM 콘솔] → [자격 증명 공급자] → [공급자 추가]

 

4. [공급자 추가] 페이지의 공급자 URL에 2번에서 생성된 OIDC Provider URL을 입력하고, 대상에는 sts.amazonaws.com을 입력한 후 [지문 가져오기]를 선택합니다.

 

5. 공급자 생성 후 아래의 빨간색 상자에 해당하는 OIDC ID를 복사합니다.

6. Target Account에 IAM Role을 생성 합니다. 여기서 만들어진 IAM Role ARN으로 EKS의 Service Account에 annotation으로 설정할 것입니다.

TARGET_OIDC_ID=<위에서 복사한 OIDC ID 입력>
cat << EOF > target_role.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::$TARGET_ACCOUNT_ID:oidc-provider/oidc.eks.$AWS_REGION.amazonaws.com/id/$TARGET_OIDC_ID"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringLike": {
          "oidc.eks.$AWS_REGION.amazonaws.com/id/$TARGET_OIDC_ID:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}
EOF

aws iam create-role --profile $TARGET_AWS_PROFILE --role-name $TARGET_IAM_ROLE_NAME --assume-role-policy-document file://target_role.json

# Route53 접근 권한 할당을 위한 Policy 추가
cat << EOF > target_policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid" : "AllowPublicHostedZonePermissions",
            "Effect": "Allow",
            "Action": [
                "route53:CreateHostedZone",
                "route53:UpdateHostedZoneComment",
                "route53:GetHostedZone",
                "route53:ListHostedZones",
                "route53:DeleteHostedZone",
                "route53:ChangeResourceRecordSets",
                "route53:ListResourceRecordSets",
                "route53:GetHostedZoneCount",
                "route53:ListHostedZonesByName"
            ],
            "Resource": "*"
        },
        {
         "Sid" : "AllowHealthCheckPermissions",
            "Effect": "Allow",
            "Action": [
                "route53:CreateHealthCheck",
                "route53:UpdateHealthCheck",
                "route53:GetHealthCheck",
                "route53:ListHealthChecks",
                "route53:DeleteHealthCheck",
                "route53:GetCheckerIpRanges",
                "route53:GetHealthCheckCount",
                "route53:GetHealthCheckStatus",
                "route53:GetHealthCheckLastFailureReason"
            ],
            "Resource": "*"
        }
    ]
}
EOF

aws iam put-role-policy --profile $TARGET_AWS_PROFILE --role-name $TARGET_IAM_ROLE_NAME --policy-name $TARGET_IAM_POLICY_NAME --policy-document file://target_policy.json

 

7. Source Account의 Amazon EKS 클러스터 내에서 Service Account 생성합니다. 이 때 Role ARN은 Target Account에서 생성한 IAM Role 입니다.

eksctl create iamserviceaccount \
  --name $SA_NAME \
  --namespace $NAMESPACE_NAME \
  --cluster $EKS_CLUSTER_NAME \
  --attach-role-arn arn:aws:iam::$TARGET_ACCOUNT_ID:role/$TARGET_IAM_ROLE_NAME \
  --approve

 

8. 대상 pod에서 권한이 제대로 적용 되었는지 확인합니다.

cat << EOF > aws-cli.yaml
apiVersion: v1
kind: Pod
metadata:
  name: aws-cli
  namespace: $NAMESPACE_NAME 
spec:
  serviceAccountName: $SA_NAME
  containers:
  - name: aws-cli
    image: amazon/aws-cli:latest
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always
EOF

kubectl apply -f aws-cli.yaml
kubectl exec -it aws-cli -n $NAMESPACE_NAME -- aws sts get-caller-identity
kubectl exec -it aws-cli -n $NAMESPACE_NAME -- aws route53 list-hosted-zones

9. 여기까지 진행 됐다면 ExternalDNS로 Route53을 제어하기 위한 권한에 대한 부분은 모두 설정된 것입니다. 이 후 절차는 아래 링크에서 설치 가이드 및 설정 방법을 참고하시기 바랍니다.