[AEWS] #6์ฃผ์ฐจ IRSA ์‹ค์Šต (4)

25๋…„๋„ AWS EKS Hands-on Study ์Šคํ„ฐ๋”” ์ •๋ฆฌ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

 

IRSA

IRSA๋Š” ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์˜ ์„œ๋น„์Šค ์–ด์นด์šดํŠธ(Service Account, SA)์™€ AWS IAM ์—ญํ• (Role)์„ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
๊ธฐ๋ณธ์ ์œผ๋กœ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์˜ ํŒŒ๋“œ๋Š” AWS ๋ฆฌ์†Œ์Šค(S3, DynamoDB ๋“ฑ)์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, AWS์˜ ๋ณด์•ˆ ์ •์ฑ…์— ๋”ฐ๋ผ IAM ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•ด์•ผ ํ•œ๋‹ค.

 

๊ธฐ์กด์—๋Š” EC2 ์ธ์Šคํ„ด์Šค์— IAM Role์„ ๋ถ™์ด๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ์ด๋Š” ์„ธ๋ถ„ํ™”๋œ ๊ถŒํ•œ ๊ด€๋ฆฌ๋ฅผ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ ๋‹ค.

 

IRSA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŒŒ๋“œ ๋‹จ์œ„๋กœ IAM ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ์–ด ๋ณด์•ˆ์„ฑ๊ณผ ์œ ์—ฐ์„ฑ์ด ์ฆ๊ฐ€ํ•œ๋‹ค.

  • ๋ถˆํ•„์š”ํ•œ ๊ถŒํ•œ์„ ์ค„์ผ ์ˆ˜ ์žˆ์Œ โ†’ ํŠน์ • Pod์—๋งŒ ํ•„์š”ํ•œ AWS ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋ถ€์—ฌ ๊ฐ€๋Šฅ
  • ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ ํ™˜๊ฒฝ์—์„œ ๋ณด์•ˆ ๊ฐ•ํ™” โ†’ ๋™์ผํ•œ ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด์—์„œ๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(ํŒŒ๋“œ)๋ณ„๋กœ ๋‹ค๋ฅธ IAM ์ •์ฑ…์„ ์ ์šฉ ๊ฐ€๋Šฅ
  • IAM ์ •์ฑ… ์ ์šฉ์ด ํŒŒ๋“œ ์ˆ˜์ค€์œผ๋กœ ๋‚ด๋ ค๊ฐ โ†’ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๊ด€๋ฆฌ ๊ฐ€๋Šฅ

 

sa ๊ณ„์ • ์ƒ์„ฑ

eksctl create iamserviceaccount \
  --name my-sa \
  --namespace default \
  --cluster $CLUSTER_NAME \
  --approve \
  --attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`AmazonS3ReadOnlyAccess`].Arn' --output text)

kubectl describe sa my-sa
Name:                my-sa
Namespace:           default
Labels:              app.kubernetes.io/managed-by=eksctl
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::~:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-3OtbVkhc7PHP
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>

 

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: eks-iam-test3
spec:
  serviceAccountName: my-sa
  containers:
    - name: my-aws-cli
      image: amazon/aws-cli:latest
      command: ['sleep', '36000']
  restartPolicy: Never
  terminationGracePeriodSeconds: 0
EOF
kubectl get pod eks-iam-test3 -o yaml
...
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-dccvr
      readOnly: true
    - mountPath: /var/run/secrets/eks.amazonaws.com/serviceaccount
      name: aws-iam-token
      readOnly: true
...
   volumes:
  - name: aws-iam-token
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          audience: sts.amazonaws.com
          expirationSeconds: 86400
          path: token
 kubectl describe pod eks-iam-test3
 ...
    Environment:
      AWS_STS_REGIONAL_ENDPOINTS:   regional
      AWS_DEFAULT_REGION:           ap-northeast-2
      AWS_REGION:                   ap-northeast-2
      AWS_ROLE_ARN:                 arn:aws:iam::~:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-3OtbVkhc7PHP
      AWS_WEB_IDENTITY_TOKEN_FILE:  /var/run/secrets/eks.amazonaws.com/serviceaccount/token
    Mounts:
      /var/run/secrets/eks.amazonaws.com/serviceaccount from aws-iam-token (ro)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-dccvr (ro)

 

kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn
"arn:aws:sts::~~:assumed-role/eksctl-myeks-addon-iamserviceaccount-default--Role1-3OtbVkhc7PHP/botocore-session-1742074654"

 

eks-iam-test3 ํŒŒ๋“œ๋Š” my-service-account๋ผ๋Š” ์„œ๋น„์Šค ์–ด์นด์šดํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ณ  AWS IAM Role๊ณผ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค.

ํŒŒ๋“œ๊ฐ€ ์‹คํ–‰๋˜๋ฉด AWS IAM STS(Security Token Service)์—์„œ ํ† ํฐ์„ ๋ฐ›์•„์„œ IAM Role์„ Assumeํ•  ์ˆ˜ ์žˆ๊ณ  ์ด ๊ณผ์ •์„ ํ†ตํ•ด ํŒŒ๋“œ๋Š” IAM Role์˜ ๊ถŒํ•œ์„ ์‚ฌ์šฉํ•˜์—ฌ AWS ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

 

IAM ์ธ์ฆ ํ† ํฐ ์„ค์ •๊ณผ Mutating Webhook ํ™•์ธ

kubectl get pod eks-iam-test3 -o json | jq -r '.spec.containers | .[].volumeMounts'
[
  {
    "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
    "name": "kube-api-access-dccvr",
    "readOnly": true
  },
  {
    "mountPath": "/var/run/secrets/eks.amazonaws.com/serviceaccount",
    "name": "aws-iam-token",
    "readOnly": true
  }
]

 

 

kube-api-access-dccvr:

  • ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๊ธฐ๋ณธ ์„œ๋น„์Šค ์–ด์นด์šดํŠธ(SA) ํ† ํฐ์„ ์ €์žฅํ•˜๋Š” ๋””๋ ‰ํ† ๋ฆฌ(/var/run/secrets/kubernetes.io/serviceaccount)
  • ํŒŒ๋“œ๊ฐ€ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค API ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.

aws-iam-token:

  • IRSA๋ฅผ ํ†ตํ•ด AWS STS ํ† ํฐ์„ ์ €์žฅํ•˜๋Š” ๋””๋ ‰ํ† ๋ฆฌ(/var/run/secrets/eks.amazonaws.com/serviceaccount)
  • ์ด ํ† ํฐ์„ ์‚ฌ์šฉํ•˜์—ฌ IAM Role์„ Assumeํ•˜์—ฌ AWS ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋‹ค.
kubectl get pod eks-iam-test3 -o json | jq -r '.spec.volumes[] | select(.name=="aws-iam-token")'
{
  "name": "aws-iam-token",
  "projected": {
    "defaultMode": 420,
    "sources": [
      {
        "serviceAccountToken": {
          "audience": "sts.amazonaws.com",
          "expirationSeconds": 86400,
          "path": "token"
        }
      }
    ]
  }
}

 

IAM STS ํ† ํฐ ์„ค์ •

  • audience: "sts.amazonaws.com" โ†’ AWS STS(Security Token Service)์—์„œ ์‚ฌ์šฉํ•  IAM ์ธ์ฆ ํ† ํฐ
  • expirationSeconds: 86400 โ†’ ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ๊ฐ„(24์‹œ๊ฐ„)
  • path: "token" โ†’ Pod ๋‚ด๋ถ€์—์„œ /var/run/secrets/eks.amazonaws.com/serviceaccount/token ๊ฒฝ๋กœ์— ํ† ํฐ์ด ์ €์žฅ๋œ๋‹ค.
kubectl get MutatingWebhookConfiguration
NAME                            WEBHOOKS   AGE
pod-identity-webhook            1          6h37m
vpc-resource-mutating-webhook   1          6h37m

 

 

์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์—์„œ ํŒŒ๋“œ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ์ž๋™์œผ๋กœ ํŠน์ • ์„ค์ •์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์›นํ›…์ด๋‹ค.

๋งŒ์•ฝ ํŒŒ๋“œ ์ƒ์„ฑ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด Mutating Webhook์ด ์ด๋ฅผ ๊ฐ€๋กœ์ฑ„์„œ ๋ณผ๋ฅจ ๋งˆ์šดํŠธ, ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ถ”๊ฐ€์™€ ๊ฐ™์€ ์ถ”๊ฐ€ ์„ค์ •์„ ์ ์šฉํ•œ๋‹ค.

 

ํŒŒ๋“œ์— IAM ๋กค ์„ค์ • ํ™•์ธ

kubectl get pod eks-iam-test3 -o json | jq -r '.spec.containers | .[].env'

[
  {
    "name": "AWS_STS_REGIONAL_ENDPOINTS",
    "value": "regional"
  },
  {
    "name": "AWS_DEFAULT_REGION",
    "value": "ap-northeast-2"
  },
  {
    "name": "AWS_REGION",
    "value": "ap-northeast-2"
  },
  {
    "name": "AWS_ROLE_ARN",
    "value": "arn:aws:iam::~~:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-3OtbVkhc7PHP"
  },
  {
    "name": "AWS_WEB_IDENTITY_TOKEN_FILE",
    "value": "/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
  }
]

 

  • IAM Role (AWS_ROLE_ARN)์ด ํ• ๋‹น๋˜์–ด ์žˆ๋‹ค.
  • STS ํ† ํฐ ํŒŒ์ผ (AWS_WEB_IDENTITY_TOKEN_FILE)์ด /var/run/secrets/eks.amazonaws.com/serviceaccount/token์— ์กด์žฌํ•œ๋‹ค.
  • ํŒŒ๋“œ ๋‚ด๋ถ€์—์„œ STS ํ† ํฐ์„ ์ด์šฉํ•˜์—ฌ AWS IAM Role์„ Assumeํ•  ์ˆ˜ ์žˆ์Œ

ํŒŒ๋“œ ๋‚ด๋ถ€ STS ์ธ์ฆ ํ† ํฐ ํ™•์ธ

IAM_TOKEN=$(kubectl exec -it eks-iam-test3 -- cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token)
echo $IAM_TOKEN

 

ํŒŒ๋“œ ๋‚ด๋ถ€์—์„œ ์ด ํ† ํฐ์„ AWS STS์— ์ œ์ถœํ•˜์—ฌ ํ•ด๋‹น IAM Role๋กœ AWS API๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

 

OIDC ์„ค์ • ์ •๋ณด ํ™•์ธ

curl -s $IDP/.well-known/openid-configuration | jq -r '.'
{
  "issuer": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/XXXXXXXXX",
  "jwks_uri": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/XXXXXXXXX/keys",
  "authorization_endpoint": "urn:kubernetes:programmatic_authorization",
  "response_types_supported": [
    "id_token"
  ],
  "subject_types_supported": [
    "public"
  ],
  "claims_supported": [
    "sub",
    "iss"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ]
}

 

 

EKS๋Š” OIDC๋ฅผ ์ง€์›ํ•˜๋ฉฐ OIDC ๊ธฐ๋ฐ˜ ์ธ์ฆ์„ ํ†ตํ•ด AWS IAM ์—ญํ• ์„ ํŒŒ๋“œ์— ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

JWKS URL์„ ํ†ตํ•ด ๊ณต๊ฐœ ํ‚ค๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ AWS STS๋Š” ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ OIDC ํ† ํฐ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•˜๋Š”๋ฐ, ์ด ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ EKS์˜ IRSA ๊ฐ€ ๋™์ž‘ํ•˜์—ฌ ํŒŒ๋“œ๊ฐ€ AWS ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

 

๋ฆฌ์†Œ์Šค ์‚ญ์ œ

kubectl delete pod eks-iam-test3
pod "eks-iam-test3" deleted

eksctl delete iamserviceaccount --cluster $CLUSTER_NAME --name my-sa --namespace default
2025-03-16 06:48:14 [โ„น]  1 iamserviceaccount (default/my-sa) was included (based on the include/exclude rules)
2025-03-16 06:48:14 [โ„น]  1 task: {
    2 sequential sub-tasks: {
        delete IAM role for serviceaccount "default/my-sa" [async],
        delete serviceaccount "default/my-sa",
    } }2025-03-16 06:48:15 [โ„น]  will delete stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2025-03-16 06:48:15 [โ„น]  deleted serviceaccount "default/my-sa"