본문 바로가기
AWS/EKS

[AEWS2] 6-3. EKS IRSA & Pod Identity

by okms1017 2024. 4. 13.
728x90

✍ Posted by Immersive Builder  Seong

 

IRSA(IAM Role for Service Account)

파드 내 어플리케이션 컨테이너가 IAM 역할을 사용할 수 있게 해주는 기능입니다. 

파드가 특정 IAM 역할로 Assume할 때 토큰을 AWS에 전송하고, AWS는 토큰과 EKS IdP를 통해 해당 IAM 역할을 사용할 수 있는지 검증합니다. 

 

IRSA 동작원리

 

파드(어플리케이션)에 Service Account를 할당하고 IAM Role을 RoleBinding하여 구현합니다. 

파드에 Access Key ID를 하드코딩하지 않아 보안상 바람직하고, 노드의 인스턴스 프로파일을 공유하지 않으므로 최소 권한 원칙을 만족합니다. 

 

IRSA

* Service Account Token Volume Projection

- 서비스 계정 토큰의 시크릿 기반 볼륨 대신 projected volume을 사용합니다. 

- 서비스 계정 토큰에서 지원하지 않는 대상(audience), 유효기간(expiration) 등 토큰의 속성을 지정할 수 있습니다. 

 

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /var/run/secrets/tokens
      name: vault-token
  serviceAccountName: build-robot
  volumes:
  - name: vault-token
    projected:
      sources:
      - serviceAccountToken:
          path: vault-token
          expirationSeconds: 7200
          audience: vault

 

* Bound Service Account Token Volume 

- Service Account Admission Controller는 토큰 컨트롤러에서 생성한 만료되지 않은 Service Account Token에 시크릿 기반 볼륨 대신 다음과 같은 projected volume을 추가합니다. 

- serviceAccountToken: 기본 1시간 후 또는 파드가 삭제될 때 만료됩니다. 이 토큰은 파드에 연결되며 kube-apiserver를 위해 존재합니다. 

- configMap: kube-apiserver에 대한 연결을 확인하는 데 사용되는 CA 번들을 포함합니다. 

- downwardAPI: 파드의 네임스페이스를 참조합니다.  

 

- name: kube-api-access-<random-suffix>
  projected:
    defaultMode: 420 
    sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
            - key: ca.crt
              path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
            - fieldRef:
                apiVersion: v1
                fieldPath: metadata.namespace
              path: namespace

 

* Configure a Pod to Use a Projected Volume for Storage

- secret, configMap, downwardAPI, serviceAccountToken의 볼륨 마운트를 하나의 디렉터리에 통합하는 기능입니다. 

 

apiVersion: v1
kind: Pod
metadata:
  name: test-projected-volume
spec:
  containers:
  - name: test-projected-volume
    image: busybox:1.28
    args:
    - sleep
    - "86400"
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: user
      - secret:
          name: pass

 

# Create the Secrets:
## Create files containing the username and password:
$ echo -n "admin" > ./username.txt
$ echo -n "1f2d1e2e67df" > ./password.txt

## Package these files into secrets:
$ kubectl create secret generic user --from-file=./username.txt
secret/user created
$ kubectl create secret generic pass --from-file=./password.txt
secret/pass created

# 파드 생성
$ kubectl apply -f https://k8s.io/examples/pods/storage/projected.yaml
pod/test-projected-volume created

# 파드 확인
$ kubectl get pod test-projected-volume -o yaml | kubectl neat | yh
```
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: user
      - secret:
          name: pass
  - name: kube-api-access-slltd
    projected:
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              fieldPath: metadata.namespace
            path: namespace
```

# 시크릿 확인
$ kubectl exec -it test-projected-volume -- ls /projected-volume/
password.txt  username.txt

$ kubectl exec -it test-projected-volume -- cat /projected-volume/username.txt ;echo
admin

$ kubectl exec -it test-projected-volume -- cat /projected-volume/password.txt ;echo
1f2d1e2e67df

# 삭제
$ kubectl delete pod test-projected-volume && kubectl delete secret user pass
pod "test-projected-volume" deleted

 

IRSA 실습

1. ServiceAccountToken을 생성하지 않고 파드를 배포해봅니다. (automountServiceAccountToken: false)

로그를 통해 S3 연동에 실패하였음을 확인합니다. 

 

# Test Pod 1 배포 
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: eks-iam-test1
spec:
  containers:
    - name: my-aws-cli
      image: amazon/aws-cli:latest
      args: ['s3', 'ls']
  restartPolicy: Never
  automountServiceAccountToken: false
  terminationGracePeriodSeconds: 0
EOF
pod/eks-iam-test1 created

# 배포 확인
$ kubectl get pod
NAME            READY   STATUS    RESTARTS   AGE
eks-iam-test1   1/1     Running   0          13s
$ kubectl describe pod
```
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  24s   default-scheduler  Successfully assigned default/eks-iam-test1 to ip-192-168-3-166.ap-northeast-2.compute.internal
  Normal  Pulling    24s   kubelet            Pulling image "amazon/aws-cli:latest"
  Normal  Pulled     12s   kubelet            Successfully pulled image "amazon/aws-cli:latest" in 11.717s (11.717s including waiting)
  Normal  Created    12s   kubelet            Created container my-aws-cli
  Normal  Started    12s   kubelet            Started container my-aws-cli
```

# 로그 확인
$ kubectl logs eks-iam-test1
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

# Test Pod 1 삭제
$ kubectl delete pod eks-iam-test1
pod "eks-iam-test1" deleted

 

CloudTrail - S3 Access Denied

 

2. ServiceAccountToken을 생성하여 파드를 배포해봅니다. 

Service Account가 생성될 때 JWT Token이 자동으로 생성됩니다. 

결과는 여전히 S3 연동에 실패하는 모습입니다. 

 

# Test Pod 2 배포
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: eks-iam-test2
spec:
  containers:
    - name: my-aws-cli
      image: amazon/aws-cli:latest
      command: ['sleep', '36000']
  restartPolicy: Never
  terminationGracePeriodSeconds: 0
EOF
pod/eks-iam-test2 created

# 배포 확인
$ kubectl get pod
NAME            READY   STATUS    RESTARTS   AGE
eks-iam-test2   1/1     Running   0          28s
$ kubectl describe pod
```
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  41s   default-scheduler  Successfully assigned default/eks-iam-test2 to ip-192-168-2-115.ap-northeast-2.compute.internal
  Normal  Pulling    40s   kubelet            Pulling image "amazon/aws-cli:latest"
  Normal  Pulled     28s   kubelet            Successfully pulled image "amazon/aws-cli:latest" in 12.034s (12.034s including waiting)
  Normal  Created    28s   kubelet            Created container my-aws-cli
  Normal  Started    28s   kubelet            Started container my-aws-cli
```
$ kubectl get pod eks-iam-test2 -o yaml | kubectl neat | yh
apiVersion: v1
kind: Pod
metadata:
  name: eks-iam-test2
  namespace: default
spec:
  containers:
  - command:
    - sleep
    - "36000"
    image: amazon/aws-cli:latest
    name: my-aws-cli
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-vv7g6
      readOnly: true
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Never
  serviceAccountName: default
  terminationGracePeriodSeconds: 0
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: kube-api-access-vv7g6
    projected:
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              fieldPath: metadata.namespace
            path: namespace
$ kubectl exec -it eks-iam-test2 -- ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt  namespace  token

# aws 서비스 사용 시도
$ kubectl exec -it eks-iam-test2 -- aws s3 ls
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

# 서비스 어카운트 토큰 확인
$ SA_TOKEN=$(kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
echo $SA_TOKEN
eyJhbGciO*****.eyJhdWQ*****.Y45aSLmcn*****

# Test Pod 2 삭제
$ kubectl delete pod eks-iam-test2

 

JWT Token Decoded
CloudTrail - S3 Access Denied

 

3. 쿠버네티스 Service Account(my-sa)를 생성하고 특정 IAM Role을 바인딩합니다. 

 

# Create an iamserviceaccount - AWS IAM role bound to a Kubernetes service account
$ 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)
2024-04-14 03:47:56 [ℹ]  1 existing iamserviceaccount(s) (kube-system/aws-load-balancer-controller) will be excluded
2024-04-14 03:47:56 [ℹ]  1 iamserviceaccount (default/my-sa) was included (based on the include/exclude rules)
2024-04-14 03:47:56 [!]  serviceaccounts that exist in Kubernetes will be excluded, use --override-existing-serviceaccounts to override
2024-04-14 03:47:56 [ℹ]  1 task: {
    2 sequential sub-tasks: {
        create IAM role for serviceaccount "default/my-sa",
        create serviceaccount "default/my-sa",
    } }2024-04-14 03:47:56 [ℹ]  building iamserviceaccount stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2024-04-14 03:47:56 [ℹ]  deploying stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2024-04-14 03:47:56 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"

2024-04-14 03:48:26 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2024-04-14 03:48:26 [ℹ]  created serviceaccount "default/my-sa"

$ eksctl get iamserviceaccount --cluster $CLUSTER_NAME
default         my-sa                           arn:aws:iam::732659419746:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-hZuZABdTBP99
kube-system     aws-load-balancer-controller    arn:aws:iam::732659419746:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-9jS2AOMjb6eu

# 확인 
$ kubectl get sa
NAME      SECRETS   AGE
default   0         8h
my-sa     0         33s

$ 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::732659419746:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-hZuZABdTBP99
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>

 

CloudFormation Stack - iamserviceaccount(my-sa)

MutatingWebhook에 의해 Environment와 Volume이 추가되고, S3 목록을 불러오는데 성공한 모습입니다. 

S3 ReadOnlyAccess 권한을 부여하였기 때문에 노드 또는 VPC에 대한 정보는 조회에 실패합니다.  

 

# Test Pod 3 배포 
$ 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
pod/eks-iam-test3 created

# 해당 SA를 파드가 사용 시 mutatingwebhook으로 Env,Volume 추가함
$ kubectl get mutatingwebhookconfigurations pod-identity-webhook -o yaml | kubectl neat | yh

# Pod Identity Webhook은 mutatingwebhook을 통해 아래 Env 내용과 1개의 볼륨을 추가함
$ kubectl get pod eks-iam-test3 -o yaml | kubectl neat | yh
apiVersion: v1
kind: Pod
metadata:
  name: eks-iam-test3
  namespace: default
spec:
  containers:
  - command:
    - sleep
    - "36000"
    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::732659419746:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-hZuZABdTBP99
    - name: AWS_WEB_IDENTITY_TOKEN_FILE
      value: /var/run/secrets/eks.amazonaws.com/serviceaccount/token
    image: amazon/aws-cli:latest
    name: my-aws-cli
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-gcs46
      readOnly: true
    - mountPath: /var/run/secrets/eks.amazonaws.com/serviceaccount
      name: aws-iam-token
      readOnly: true
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Never
  serviceAccountName: my-sa
  terminationGracePeriodSeconds: 0
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: aws-iam-token
    projected:
      sources:
      - serviceAccountToken:
          audience: sts.amazonaws.com
          expirationSeconds: 86400
          path: token
  - name: kube-api-access-gcs46
    projected:
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              fieldPath: metadata.namespace
            path: namespace

$ kubectl exec -it eks-iam-test3 -- ls /var/run/secrets/eks.amazonaws.com/serviceaccount
token

$ kubectl exec -it eks-iam-test3 -- cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token ; echo
eyJhb*****.eyJhdWQ*****.RPOun4*****

$ 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::732659419746:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-hZuZABdTBP99
      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-gcs46 (ro)
```
Volumes:
  aws-iam-token:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  86400
  kube-api-access-gcs46:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
```

# 파드에서 aws cli 사용 확인
$ eksctl get iamserviceaccount --cluster $CLUSTER_NAME
NAMESPACE       NAME                            ROLE ARN
default         my-sa                           arn:aws:iam::732659419746:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-hZuZABdTBP99
kube-system     aws-load-balancer-controller    arn:aws:iam::732659419746:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-9jS2AOMjb6eu

$ kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn
"arn:aws:sts::732659419746:assumed-role/eksctl-myeks-addon-iamserviceaccount-default--Role1-hZuZABdTBP99/botocore-session-1713035603"

# S3 권한에 따른 성공/실패 
$ kubectl exec -it eks-iam-test3 -- aws s3 ls
2020-05-31 04:22:17 audio-for-wordpress-11009175126ac46ab1b3674d4bdb43b43bf07212
2020-06-09 17:24:11 bucket-for-aws-translate
2020-06-02 04:36:39 bucket-for-bootstrap
2021-03-10 01:38:35 bucket-for-php-source-code
2021-03-10 08:29:34 cf-templates-11i7zst5zyo80-ap-northeast-2
2021-03-10 20:50:51 hn-input-bucket
2021-03-11 02:01:02 hn-output-bucket
2021-03-13 13:06:10 input-bucket-for-vod
2021-03-10 01:40:25 okms1017
2021-03-15 16:05:03 output-bucket-for-vod
2021-02-22 15:55:35 temporary-web-site
2021-03-10 16:11:55 vod-destination920a3c57-ure3yz5sjtah
2021-03-10 16:11:58 vod-logs6819bb44-q11yuh9ktkmi
2021-03-10 16:12:38 vod-source71e471f1-5r8rnv2ptxxv

$ kubectl exec -it eks-iam-test3 -- aws ec2 describe-instances --region ap-northeast-2
An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation. User: arn:aws:sts::732659419746:assumed-role/eksctl-myeks-addon-iamserviceaccount-default--Role1-hZuZABdTBP99/botocore-session-1713035603 is not authorized to perform: ec2:DescribeInstances because no identity-based policy allows the ec2:DescribeInstances action

$ kubectl exec -it eks-iam-test3 -- aws ec2 describe-vpcs --region ap-northeast-2
An error occurred (UnauthorizedOperation) when calling the DescribeVpcs operation: You are not authorized to perform this operation. User: arn:aws:sts::732659419746:assumed-role/eksctl-myeks-addon-iamserviceaccount-default--Role1-hZuZABdTBP99/botocore-session-1713035603 is not authorized to perform: ec2:DescribeVpcs because no identity-based policy allows the ec2:DescribeVpcs action

 

JWT Token Decoded
CloudTrail - S3 Access Success

Public OpenID Provider 정보를 조회할 수도 있습니다. 

 

$ IDP=$(aws eks describe-cluster --name myeks --query cluster.identity.oidc.issuer --output text)
$ curl -s $IDP/.well-known/openid-configuration | jq -r '.'
{
  "issuer": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/8D687B71EBE1B368120C1A1450DD3178",
  "jwks_uri": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/8D687B71EBE1B368120C1A1450DD3178/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"
  ]
}

 

Amazon EKS OpenID Provider URL

* IRSA 취약점 

- AWS는 JWT 토큰의 유효성만 확인할 뿐 토큰과 Service Account에 지정된 IAM Role 간에 일관성을 보장하지 않습니다. 

- IAM Role의 Condition을 *.* 등으로 잘못 설정한 경우, 토큰과 IAM Role ARN만 있다면 동일 토큰으로 다른 IAM Role을 맡을 수 있습니다. 

- EKS OIDC는 외부에서 접근가능한 Public Endpoint입니다. 

 

EKS Pod Identity

  • .IRSA의 보안 취약점을 개선하기 위해 신규 출시된 기능입니다. 
  • 신규 버전의 클러스터 구성 시, OIDC Provider 재생성에 따른 마이그레이션이 불가피합니다. 
  • Amazon EKS Pod Identity Agent add-on만 추가하면 됩니다. 
  • Tag 값에 의한 ABAC을 지원합니다. 
  • CloudTrail과 통합되어 Audit Log를 남길 수 있습니다. 

EKS Pod Identity Architecture

eks-pod-identity-agent를 add-on으로 설치합니다. 

 

# eks pod identity agent add=on 설치 
$ ADDON=eks-pod-identity-agent
$ aws eks describe-addon-versions \
    --addon-name $ADDON \
    --kubernetes-version 1.28 \
    --query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" \
    --output text
v1.2.0-eksbuild.1
True
v1.1.0-eksbuild.1
False
v1.0.0-eksbuild.1
False

$ aws eks create-addon --cluster-name $CLUSTER_NAME --addon-name eks-pod-identity-agent
{
    "addon": {
        "addonName": "eks-pod-identity-agent",
        "clusterName": "myeks",
        "status": "CREATING",
        "addonVersion": "v1.2.0-eksbuild.1",
        "health": {
            "issues": []
        },
        "addonArn": "arn:aws:eks:ap-northeast-2:732659419746:addon/myeks/eks-pod-identity-agent/2ac76c89-6b88-a40f-10af-19e517110c20",
        "createdAt": "2024-04-14T05:07:54.699000+09:00",
        "modifiedAt": "2024-04-14T05:07:54.716000+09:00",
        "tags": {}
    }
}

# 설치 확인
$ eksctl get addon --cluster $CLUSTER_NAME
NAME                    VERSION                 STATUS          ISSUES  IAMROLE  
eks-pod-identity-agent  v1.2.0-eksbuild.1       ACTIVE          0

$ kubectl -n kube-system get daemonset eks-pod-identity-agent
NAME                     DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
eks-pod-identity-agent   3         3         3       3            3           <none>          2m2s

$ kubectl -n kube-system get pods -l app.kubernetes.io/name=eks-pod-identity-agent
NAME                           READY   STATUS    RESTARTS   AGE
eks-pod-identity-agent-drgdp   1/1     Running   0          2m40s
eks-pod-identity-agent-gdxcc   1/1     Running   0          2m40s
eks-pod-identity-agent-mlgtd   1/1     Running   0          2m40s

$ kubectl get ds -n kube-system eks-pod-identity-agent -o yaml | kubectl neat | yh
```
      containers: 
      - args: 
        - --port
        - "80"
        - --cluster-name
        - myeks
        - --probe-port
        - "2703"
        command: 
        - /go-runner
        - /eks-pod-identity-agent
        - server
      ```
      ports: 
        - containerPort: 80
          name: proxy
          protocol: TCP
        - containerPort: 2703
          name: probes-port
          protocol: TCP
      ```
        securityContext: 
          capabilities: 
            add: 
            - CAP_NET_BIND_SERVICE
      ```
      hostNetwork: true
```

# 네트워크 정보 확인
$ for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ss -tnlp | grep eks-pod-identit; echo "-----";done
LISTEN 0      4096        127.0.0.1:2703       0.0.0.0:*    users:(("eks-pod-identit",pid=183201,fd=8))
LISTEN 0      4096   169.254.170.23:80         0.0.0.0:*    users:(("eks-pod-identit",pid=183201,fd=7))
LISTEN 0      4096   [fd00:ec2::23]:80            [::]:*    users:(("eks-pod-identit",pid=183201,fd=6))
-----
LISTEN 0      4096        127.0.0.1:2703       0.0.0.0:*    users:(("eks-pod-identit",pid=184451,fd=8))
LISTEN 0      4096   169.254.170.23:80         0.0.0.0:*    users:(("eks-pod-identit",pid=184451,fd=7))
LISTEN 0      4096   [fd00:ec2::23]:80            [::]:*    users:(("eks-pod-identit",pid=184451,fd=6))
-----
LISTEN 0      4096        127.0.0.1:2703       0.0.0.0:*    users:(("eks-pod-identit",pid=182539,fd=8))
LISTEN 0      4096   169.254.170.23:80         0.0.0.0:*    users:(("eks-pod-identit",pid=182539,fd=3))
LISTEN 0      4096   [fd00:ec2::23]:80            [::]:*    users:(("eks-pod-identit",pid=182539,fd=4))

$ for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ip -c route; done
default via 192.168.1.1 dev eth0
169.254.169.254 dev eth0
169.254.170.23 dev pod-id-link0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.162
192.168.1.42 dev eni4563f7471f4 scope link
192.168.1.59 dev enicc4a85187de scope link
192.168.1.75 dev eni4befcb4bd4e scope link
192.168.1.119 dev eni6600d0d73f2 scope link
192.168.1.191 dev eni0731788645b scope link
192.168.1.217 dev eni875cf18273c scope link
default via 192.168.2.1 dev eth0
169.254.169.254 dev eth0
169.254.170.23 dev pod-id-link0
192.168.2.0/24 dev eth0 proto kernel scope link src 192.168.2.115
192.168.2.124 dev eni97a288408de scope link
192.168.2.135 dev enicc8eb113c61 scope link
192.168.2.149 dev eni137bf751a9d scope link
192.168.2.151 dev eni22db331a2dd scope link
192.168.2.177 dev eni807bbc2abe0 scope link
default via 192.168.3.1 dev eth0
169.254.169.254 dev eth0
169.254.170.23 dev pod-id-link0
192.168.3.0/24 dev eth0 proto kernel scope link src 192.168.3.166
192.168.3.103 dev enib793c0192f2 scope link
192.168.3.151 dev enic14dc1120c4 scope link
192.168.3.171 dev enic12b321181e scope link
192.168.3.234 dev eni28080bf06fb scope link
192.168.3.245 dev enie56f4c00502 scope link

$ for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ip -c -br -4 addr; done
lo               UNKNOWN        127.0.0.1/8
eth0             UP             192.168.1.162/24
eth1             UP             192.168.1.14/24
eth2             UP             192.168.1.185/24
pod-id-link0     UNKNOWN        169.254.170.23/32
lo               UNKNOWN        127.0.0.1/8
eth0             UP             192.168.2.115/24
eth1             UP             192.168.2.202/24
pod-id-link0     UNKNOWN        169.254.170.23/32
lo               UNKNOWN        127.0.0.1/8
eth0             UP             192.168.3.166/24
eth1             UP             192.168.3.76/24
pod-id-link0     UNKNOWN        169.254.170.23/32

$ for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ip -c addr; done
```
14: pod-id-link0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 96:f7:03:87:29:7e brd ff:ff:ff:ff:ff:ff
    inet 169.254.170.23/32 scope global pod-id-link0
       valid_lft forever preferred_lft forever
    inet6 fd00:ec2::23/128 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::94f7:3ff:fe87:297e/64 scope link
       valid_lft forever preferred_lft forever
```

 

Amazon EKS Add-on (Pod Identity Agent)
AmazonEKSWorkerNodePolicy - AssumeRoleForPodIdentity

podidentityassociation을 설정합니다. 

 

$ eksctl create podidentityassociation \
--cluster $CLUSTER_NAME \
--namespace default \
--service-account-name s3-sa \
--role-name s3-eks-pod-identity-role \
--permission-policy-arns arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
--region $AWS_REGION

# 확인
$ eksctl get podidentityassociation --cluster $CLUSTER_NAME
ASSOCIATION ARN                                                                                 NAMESPACE       SERVICE ACCOUNT NAME    IAM ROLE ARN
arn:aws:eks:ap-northeast-2:732659419746:podidentityassociation/myeks/a-sru7xlkuw3uzavtp5        default         s3-sa                   arn:aws:iam::732659419746:role/s3-eks-pod-identity-role

$ aws eks list-pod-identity-associations --cluster-name $CLUSTER_NAME | jq
{
  "associations": [
    {
      "clusterName": "myeks",
      "namespace": "default",
      "serviceAccount": "s3-sa",
      "associationArn": "arn:aws:eks:ap-northeast-2:732659419746:podidentityassociation/myeks/a-sru7xlkuw3uzavtp5",
      "associationId": "a-sru7xlkuw3uzavtp5"
    }
  ]
}

# ABAC - sts:Tagsession 추가
$ aws iam get-role --query 'Role.AssumeRolePolicyDocument' --role-name s3-eks-pod-identity-role | jq .
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "pods.eks.amazonaws.com"
      },
      "Action": [
        "sts:AssumeRole",
        "sts:TagSession"
      ]
    }
  ]
}

 

s3-eks-pod-identity-role
ABAC (sts:TagSession)
Amazon EKS Pod Identity

Service Accout와 파드를 생성하여 동작을 확인합니다. 

 

# 서비스어카운트, 파드 생성
$ kubectl create sa s3-sa
serviceaccount/s3-sa created

$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: eks-pod-identity
spec:
  serviceAccountName: s3-sa
  containers:
    - name: my-aws-cli
      image: amazon/aws-cli:latest
      command: ['sleep', '36000']
  restartPolicy: Never
  terminationGracePeriodSeconds: 0
EOF
pod/eks-pod-identity created

$ kubectl get pod eks-pod-identity -o yaml | kubectl neat| yh
apiVersion: v1
kind: Pod
metadata:
  name: eks-pod-identity
  namespace: default
spec:
  containers:
  - command:
    - sleep
    - "36000"
    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_CONTAINER_CREDENTIALS_FULL_URI
      value: http://169.254.170.23/v1/credentials
    - name: AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE
      value: /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
    image: amazon/aws-cli:latest
    name: my-aws-cli
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-grvtd
      readOnly: true
    - mountPath: /var/run/secrets/pods.eks.amazonaws.com/serviceaccount
      name: eks-pod-identity-token
      readOnly: true
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Never
  serviceAccountName: s3-sa
  terminationGracePeriodSeconds: 0
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: eks-pod-identity-token
    projected:
      sources:
      - serviceAccountToken:
          audience: pods.eks.amazonaws.com
          expirationSeconds: 86400
          path: eks-pod-identity-token
  - name: kube-api-access-grvtd
    projected:
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              fieldPath: metadata.namespace
            path: namespace

$ kubectl exec -it eks-pod-identity -- aws sts get-caller-identity --query Arn
"arn:aws:sts::732659419746:assumed-role/s3-eks-pod-identity-role/eks-myeks-eks-pod-id-957ea121-9c25-492e-b6c1-7e83fe2152fa"

$ kubectl exec -it eks-pod-identity -- aws s3 ls
2020-05-31 04:22:17 audio-for-wordpress-11009175126ac46ab1b3674d4bdb43b43bf07212
2020-06-09 17:24:11 bucket-for-aws-translate
2020-06-02 04:36:39 bucket-for-bootstrap
2021-03-10 01:38:35 bucket-for-php-source-code
2021-03-10 08:29:34 cf-templates-11i7zst5zyo80-ap-northeast-2
2021-03-10 20:50:51 hn-input-bucket
2021-03-11 02:01:02 hn-output-bucket
2021-03-13 13:06:10 input-bucket-for-vod
2021-03-10 01:40:25 okms1017
2021-03-15 16:05:03 output-bucket-for-vod
2021-02-22 15:55:35 temporary-web-site
2021-03-10 16:11:55 vod-destination920a3c57-ure3yz5sjtah
2021-03-10 16:11:58 vod-logs6819bb44-q11yuh9ktkmi
2021-03-10 16:12:38 vod-source71e471f1-5r8rnv2ptxxv

$ kubectl exec -it eks-pod-identity -- env | grep AWS
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE=/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
AWS_STS_REGIONAL_ENDPOINTS=regional
AWS_DEFAULT_REGION=ap-northeast-2
AWS_REGION=ap-northeast-2
AWS_CONTAINER_CREDENTIALS_FULL_URI=http://169.254.170.23/v1/credentials

# 토큰 정보 확인
$ kubectl exec -it eks-pod-identity -- ls /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/
eks-pod-identity-token
$ kubectl exec -it eks-pod-identity -- cat /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
eyJhbGc*****.eyJhdWQ*****.ksNQHgN*****

 

 


[출처] 

1) CloudNet@, AEWS 실습 스터디

2)  AWS Summit Korea 2022 - Amazon EKS 마이그레이션 요점정리, 강인호 Solution Architect

3) https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#serviceaccount-token-volume-projection

 

Configure Service Accounts for Pods

Kubernetes offers two distinct ways for clients that run within your cluster, or that otherwise have a relationship to your cluster's control plane to authenticate to the API server. A service account provides an identity for processes that run in a Pod, a

kubernetes.io

4) https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#bound-service-account-token-volume

 

Managing Service Accounts

A ServiceAccount provides an identity for processes that run in a Pod. A process inside a Pod can use the identity of its associated service account to authenticate to the cluster's API server. For an introduction to service accounts, read configure servic

kubernetes.io

5) https://kubernetes.io/docs/tasks/configure-pod-container/configure-projected-volume-storage/

 

Configure a Pod to Use a Projected Volume for Storage

This page shows how to use a projected Volume to mount several existing volume sources into the same directory. Currently, secret, configMap, downwardAPI, and serviceAccountToken volumes can be projected. Note: serviceAccountToken is not a volume type. Bef

kubernetes.io

6) https://aws.amazon.com/ko/blogs/containers/diving-into-iam-roles-for-service-accounts/

 

Diving into IAM Roles for Service Accounts | Amazon Web Services

A common challenge architects face when designing a Kubernetes solution on AWS is how to grant containerized workload permissions to access an AWS service or resource. AWS Identity and Access Management (IAM) provides fine-grained access control where you

aws.amazon.com

7) https://aws.amazon.com/ko/blogs/containers/amazon-eks-pod-identity-a-new-way-for-applications-on-eks-to-obtain-iam-credentials/

 

Amazon EKS Pod Identity: a new way for applications on EKS to obtain IAM credentials | Amazon Web Services

Introduction At AWS we are constantly striving to improve customer experience. For instance, we launched IAM Roles for Service Accounts (IRSA) in 2019 that allows customers to configure Kubernetes (k8s) applications running on AWS with fine-grained AWS Ide

aws.amazon.com

 

728x90

'AWS > EKS' 카테고리의 다른 글

[AEWS2] 6-5. Kyverno  (0) 2024.04.13
[AEWS2] 6-4. OWASP Kubernetes Top 10  (0) 2024.04.13
[AEWS2] 6-2. EKS Authentication & Authorization  (0) 2024.04.13
[AEWS2] 6-1. JWT 란?  (0) 2024.04.13
[AEWS2] 5-2. Karpenter  (1) 2024.04.07