티스토리 뷰
CloudNet@ 가시다님이 진행하는 AWS EKS Hands-on Study 내용 참고.
3. EKS IRSA & Pod Identity
- [추천 영상]
☞ EC2 Instance Profile : 사용하기 편하지만, 최소 권한 부여 원칙에 위배하며 보안상 권고하지 않음 - 링크
# 설정 예시 1 : eksctl 사용 시
eksctl create cluster --name $CLUSTER_NAME ... --external-dns-access --full-ecr-access --asg-access
# 설정 예시 2 : eksctl로 yaml 파일로 노드 생성 시
cat myeks.yaml
...
managedNodeGroups:
- amiFamily: AmazonLinux2
iam:
withAddonPolicies:
albIngress: false
appMesh: false
appMeshPreview: false
autoScaler: true
awsLoadBalancerController: false
certManager: true
cloudWatch: true
ebs: false
efs: false
externalDNS: true
fsx: false
imageBuilder: true
xRay: false
...
# 설정 예시 3 : 테라폼
...
☞ 필요 지식 : Service Account Token Volume Projection, Admission Control, JWT(JSON Web Token), OIDC
Service Account Token Volume Projection : '서비스 계정 토큰'의 시크릿 기반 볼륨 대신 'projected volume' 사용
- Service Account Token (SAT) Volume Projection - 링크
- 서비스 계정 토큰을 이용해서 서비스와 서비스, 즉 파드(pod)와 파드(pod)의 호출에서 자격 증명으로 사용할 수 있을까요?
- 불행히도 기본 서비스 계정 토큰으로는 사용하기에 부족함이 있습니다. 토큰을 사용하는 대상(audience), 유효 기간(expiration) 등 토큰의 속성을 지정할 필요가 있기 때문입니다.
- Service Account Token Volume Projection 기능을 사용하면 이러한 부족한 점들을 해결할 수 있습니다.
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 바인딩된 서비스 어카운트 토큰 볼륨 - 링크 영어
- FEATURE STATE: Kubernetes v1.22 [stable]
- 서비스 어카운트 어드미션 컨트롤러는 토큰 컨트롤러에서 생성한 만료되지 않은 서비스 계정 토큰에 시크릿 기반 볼륨 대신 다음과 같은 프로젝티드 볼륨을 추가한다.
- name: kube-api-access-<random-suffix>
projected:
defaultMode: 420 # 420은 rw- 로 소유자는 읽고쓰기 권한과 그룹내 사용자는 읽기만, 보통 0644는 소유자는 읽고쓰고실행 권한과 나머지는 읽고쓰기 권한
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
- 프로젝티드 볼륨은 세 가지로 구성된다. PSAT (Projected Service Account Tokens)
- kube-apiserver로부터 TokenRequest API를 통해 얻은 서비스어카운트토큰(ServiceAccountToken). 서비스어카운트토큰은 기본적으로 1시간 뒤에, 또는 파드가 삭제될 때 만료된다. 서비스어카운트토큰은 파드에 연결되며 kube-apiserver를 위해 존재한다.
- kube-apiserver에 대한 연결을 확인하는 데 사용되는 CA 번들을 포함하는 컨피그맵(ConfigMap).
- 파드의 네임스페이스를 참조하는 DownwardA
- Configure a Pod to Use a Projected Volume for Storage : 시크릿 컨피그맵 downwardAPI serviceAccountToken의 볼륨 마운트를 하나의 디렉터리에 통합 - 링크
- 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.
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
kubectl create secret generic pass --from-file=./password.txt
# 파드 생성
kubectl apply -f https://k8s.io/examples/pods/storage/projected.yaml
# 파드 확인
kubectl get pod test-projected-volume -o yaml | kubectl neat
...
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: user
- secret:
name: pass
- name: kube-api-access-4fq24
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
k8s api 접근 단계
- AuthN → AuthZ → Admisstion Control 권한이 있는 사용자에 한해서 관리자(Admin)가 특정 행동을 제한(validate) 혹은 변경(mutate) - 링크 Slack
- AuthN & AuthZ - MutatingWebhook - Object schema validation - ValidatingWebhook → etcd
- Admission Control도 Webhook으로 사용자에게 API가 열려있고, 사용자는 자신만의 Admission Controller를 구현할 수 있으며, 이를 Dynamic Admission Controller라고 부르고, 크게 MutatingWebhook 과 ValidatingWebhook 로 나뉩니다.
- MutatingWebhook은 사용자가 요청한 request에 대해서 관리자가 임의로 값을 변경하는 작업입니다.
- ValidatingWebhook은 사용자가 요청한 request에 대해서 관리자기 허용을 막는 작업입니다.
#
kubectl get mutatingwebhookconfigurations
NAME WEBHOOKS AGE
aws-load-balancer-webhook 3 3h43m
kube-prometheus-stack-admission 1 3h36m
pod-identity-webhook 1 6h42m
vpc-resource-mutating-webhook 1 6h42m
#
kubectl get validatingwebhookconfigurations
NAME WEBHOOKS AGE
aws-load-balancer-webhook 3 3h43m
kube-prometheus-stack-admission 1 3h36m
vpc-resource-validating-webhook 2 6h42m
JWT : Bearer type - JWT(JSON Web Token) X.509 Certificate의 lightweight JSON 버전
- Bearer type 경우, 서버에서 지정한 어떠한 문자열도 입력할 수 있습니다. 하지만 굉장히 허술한 느낌을 받습니다.
- 이를 보완하고자 쿠버네티스에서 Bearer 토큰을 전송할 때 주로 JWT (JSON Web Token) 토큰을 사용합니다.
- JWT는 X.509 Certificate와 마찬가지로 private key를 이용하여 토큰을 서명하고 public key를 이용하여 서명된 메세지를 검증합니다.
- 이러한 메커니즘을 통해 해당 토큰이 쿠버네티스를 통해 생성된 valid한 토큰임을 인증할 수 있습니다.
- X.509 Certificate의 lightweight JSON 버전이라고 생각하면 편리합니다.
- jwt는 JSON 형태로 토큰 형식을 정의한 스펙입니다. jwt는 쿠버네티스에서 뿐만 아니라 다양한 웹 사이트에서 인증, 권한 허가, 세션관리 등의 목적으로 사용합니다.
- Header: 토큰 형식와 암호화 알고리즘을 선언합니다.
- Payload: 전송하려는 데이터를 JSON 형식으로 기입합니다.
- Signature: Header와 Payload의 변조 가능성을 검증합니다.
- 각 파트는 base64 URL 인코딩이 되어서 .으로 합쳐지게 됩니다.
OIDC : 사용자를 인증해 사용자에게 액세스 권한을 부여할 수 있게 해주는 프로토콜 ⇒ [커피고래]님 블로그 OpenID Connect - 링크
- OAuth 2.0 : 권한허가 처리 프로토콜, 다른 서비스에 접근할 수 있는 권한을 획득하거나 반대로 다른 서비스에게 권한을 부여할 수 있음 - 생활코딩
- 위임 권한 부여 Delegated Authorization, 사용자 인증 보다는 제한된 사람에게(혹은 시스템) 제한된 권한을 부여하는가, 예) 페이스북 posting 권한
- Access Token : 발급처(OAuth 2.0), 서버의 리소스 접근 권한
- OpenID : 비영리기관인 OpenID Foundation에서 추진하는 개방형 표준 및 분산 인증 Authentication 프로토콜, 사용자 인증 및 사용자 정보 제공(id token) - 링크
- ID Token : 발급처(OpenID Connect), 유저 프로필 정보 획득
- OIDC OpenID Connect = OpenID 인증 + OAuth2.0 인가, JSON 포맷을 이용한 RESful API 형식으로 인증 - 링크
- iss: 토큰 발행자
- sub: 사용자를 구분하기 위한 유니크한 구분자
- email: 사용자의 이메일
- iat: 토큰이 발행되는 시간을 Unix time으로 표기한 것
- exp: 토큰이 만료되는 시간을 Unix time으로 표기한 것
- aud: ID Token이 어떤 Client를 위해 발급된 것인지.
- IdP Open Identify Provider : 구글, 카카오와 같이 OpenID 서비스를 제공하는 신원 제공자.
- OpenID Connect에서 IdP의 역할을 OAuth가 수행 - 링크
- RP Relying Party : 사용자를 인증하기 위해 IdP에 의존하는 주체
☞ IRSA 소개 : 파드가 특정 IAM 역할로 Assume 할때 토큰을 AWS에 전송하고, AWS는 토큰과 EKS IdP를 통해 해당 IAM 역할을 사용할 수 있는지 검증
https://youtu.be/wgH9xL_48vM?t=1163
- The IAM service uses these public keys to validate the token. The workflow is as follows - JWT(JSON Web Token), JWKS(JSON Web Key Set)
- AWS SDK는 AWS_ROLE_ARN 및 AWS_WEB_IDENTITY_TOKEN_FILE 이름의 환경변수를 읽어들여 Web Identity 토큰으로 AssumeRoleWithWebIdentify를 호출함으로써 Assume Role을 시도하여 임시 자격 증명을 획득하고, 특정 IAM Role 역할을 사용할 수 있게 됩니다.
- 이때 Assume Role 동작을 위한 인증은 AWS가 아닌 외부 Web IdP(EKS IdP)에 위임하여 처리합니다.
- EKS IdP를 identity provider로 등록하고, 파드가 Web Identify 토큰을 통해 IAM 역할을 Assume 할 수 있게 Trust Relationship 설정이 필요합니다.
- AWS CloudTrail 에 AssumeRoleWithWebIdentity - Link
1. Projected service account tokens are identities valid within a Kubernetes cluster. However, you could exchange for a valid token elsewhere.
2. The Amazon IAM service can receive such tokens and verify their identities by looking into the iss field of the JWT token.
3. If the identity is legit, it can issue its own token.
4. The new token can be used to access services in Amazon Web Services.
☞ 실습1
# 파드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
# 확인
kubectl get pod
kubectl describe pod
# 로그 확인
kubectl logs eks-iam-test1
# 파드1 삭제
kubectl delete pod eks-iam-test1
- CloudTrail 이벤트 ListBuckets 확인 → 기록 표시까지 약간의 시간 필요 - Link
{
...
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAU6GDVW6N7QXMJ67YM:i-05beac6b1d64479d6",
"arn": "arn:aws:sts::339712784283:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-dhnwBVR9OLts/i-05beac6b1d64479d6",
"accountId": "339712784283",
"accessKeyId": "ASIAU6GDVW6NTRRRSQ5R",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "AROAU6GDVW6N7QXMJ67YM",
"arn": "arn:aws:iam::339712784283:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-dhnwBVR9OLts",
"accountId": "339712784283",
"userName": "eksctl-myeks-nodegroup-ng1-NodeInstanceRole-dhnwBVR9OLts"
},
"attributes": {
"creationDate": "2025-03-15T21:06:00Z",
"mfaAuthenticated": "false"
},
"ec2RoleDelivery": "2.0"
}
},
"eventTime": "2025-03-15T21:57:15Z",
"eventSource": "s3.amazonaws.com",
"eventName": "ListBuckets",
"awsRegion": "ap-northeast-2",
"sourceIPAddress": "3.34.183.41",
"userAgent": "[aws-cli/2.24.24 md/awscrt#0.23.8 ua/2.1 os/linux#6.1.128-136.201.amzn2023.x86_64 md/arch#x86_64 lang/python#3.12.9 md/pyimpl#CPython m/C cfg/retry-mode#standard md/installer#docker md/distrib#amzn.2 md/prompt#off md/command#s3.ls]",
"errorCode": "AccessDenied",
"errorMessage": "User: arn:aws:sts::339712784283:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-dhnwBVR9OLts/i-05beac6b1d64479d6 is not authorized to perform: s3:ListAllMyBuckets because no identity-based policy allows the s3:ListAllMyBuckets action",
"requestParameters": {
"Host": "s3.ap-northeast-2.amazonaws.com"
},
...
}
☞ 실습2 Service Accounts
- Kubernetes Pod는 Kubernetes 서비스 계정(Kubernetes Service Account)이라는 개념을 통해 ID(정체성)를 부여받습니다.
- 서비스 계정이 생성될 때, JWT 토큰이 자동으로 생성되며, 이는 Kubernetes 시크릿(Secret)으로 저장됩니다.
- 이 Secret은 Pod에 마운트될 수 있으며, 해당 서비스 계정이 Kubernetes API 서버에 인증하는 데 사용될 수 있습니다.
# 파드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
# 확인
kubectl get pod
kubectl describe pod
kubectl get pod eks-iam-test2 -o yaml
kubectl exec -it eks-iam-test2 -- ls /var/run/secrets/kubernetes.io/serviceaccount
kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token ;echo
# aws 서비스 사용 시도
kubectl exec -it eks-iam-test2 -- aws s3 ls
# 서비스 어카운트 토큰 확인
SA_TOKEN=$(kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
echo $SA_TOKEN
# jwt 혹은 아래 JWT 웹 사이트 이용 https://jwt.io/
jwt decode $SA_TOKEN --json --iso8601
...
#헤더
{
"alg": "RS256",
"kid": "3538c9b098f6273d45f603196f095176fd82d718"
}
# 페이로드 : OAuth2에서 쓰이는 aud, exp 속성 확인! > projectedServiceAccountToken 기능으로 토큰에 audience,exp 항목을 덧붙힘
## iss 속성 : EKS OpenID Connect Provider(EKS IdP) 주소 > 이 EKS IdP를 통해 쿠버네티스가 발급한 토큰이 유요한지 검증
{
"aud": [
"https://kubernetes.default.svc"
],
"exp": 1773612197,
"iat": 1742076197,
"iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/FDAFF52CF67728F6C2259CFE7C292369",
"jti": "bccf586b-519c-4eb5-ad76-047bdb6e3cc9",
"kubernetes.io": {
"namespace": "default",
"node": {
"name": "ip-192-168-3-88.ap-northeast-2.compute.internal",
"uid": "d486eada-9be0-44e6-b64e-872fdb995a36"
},
"pod": {
"name": "eks-iam-test2",
"uid": "7bf0bfa8-44bc-42ce-a76c-09e5ef6555fb"
},
"serviceaccount": {
"name": "default",
"uid": "a5c4ad52-8e9d-4dd4-84a9-7d6ca611ea61"
},
"warnafter": 1742079804
},
"nbf": 1742076197,
"sub": "system:serviceaccount:default:default"
}
# 파드2 삭제
kubectl delete pod eks-iam-test2
- 이 JWT의 페이로드에서 볼 수 있듯이, 발급자(issuer)는 OIDC 제공자(OIDC Provider)입니다. 이 토큰의 대상(audience)은 https://kubernetes.default.svc로 설정되어 있습니다.
- 이는 클러스터 내부에서 Kubernetes API 서버에 접근하는 데 사용되는 주소입니다.Kubernetes는 유효성 검사 웹훅(Validating Webhook) 및 **변형 웹훅(Mutating Webhook)**을 지원하며, AWS는 EKS 클러스터에 사전 설치된 **아이덴티티 웹훅(Identity Webhook)**을 제공합니다. 이 웹훅은 Pod 생성 API 호출을 감지하여 Pod에 추가 토큰을 주입할 수 있습니다. 또한, 이 웹훅은 AWS에서 운영하는 자가 관리(Self-Managed) Kubernetes 클러스터에도 설치할 수 있으며, 설치 방법은 이 가이드를 통해 확인할 수 있습니다. 이 OIDC 준수 토큰(compliant OIDC token)은 AWS API에 인증하는 데 사용할 수 있는 토큰을 찾기 위한 기반을 제공합니다.그러나 Kubernetes Pod에서 AWS API를 사용하는 데 필요한 두 번째 토큰을 주입하기 위해 추가 구성 요소가 필요합니다.
☞ 실습3 IRSA - 링크
- IRSA 동작 : k8s파드 → AWS 서비스 사용 시 ⇒ AWS STS/IAM ↔ IAM OIDC Identity Provider(EKS IdP) 인증/인가
- : This webhook is for mutating pods that will require AWS IAM acces
- 웹훅이 우리 Pod에 새로운 토큰을 주입할 수 있도록 하기 위해, 새로운 Kubernetes 서비스 계정(Service Account)을 생성하고, 해당 서비스 계정에 AWS IAM 역할 ARN을 주석(annotation)으로 추가한 다음, 이 새로운 Kubernetes 서비스 계정을 Kubernetes Pod에서 참조할 것입니다. eksctl 도구를 사용하면 몇 가지 단계를 자동화할 수 있지만, 모든 과정은 수동으로도 수행할 수 있습니다.
- The eksctl create iamserviceaccount command creates:
- A Kubernetes Service Account
- An IAM role with the specified IAM policy
- A trust policy on that IAM role
- Finally, it will also annotate the Kubernetes Service Account with the IAM Role Arn created.
# 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)
# 확인 >> 웹 관리 콘솔에서 CloudFormation Stack >> IAM Role 확인
# aws-load-balancer-controller IRSA는 어떤 동작을 수행할 것 인지 생각해보자!
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
# Inspecting the newly created Kubernetes Service Account, we can see the role we want it to assume in our pod.
kubectl get sa
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::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1MJUYW59O6QGH
Image pull secrets: <none>
Mountable secrets: <none>
Tokens: <none>
Events: <none>
- AWS 관리 콘솔에서 이 IAM 역할이 어떻게 구성되어 있는지 확인해 보겠습니다. IAM으로 이동한 후 IAM 역할(IAM Roles)에서 해당 역할을 검색합니다. 서비스 계정을 설명(describe)할 때 Annotations 필드를 확인할 수 있습니다. ⇒ IAM Role 확인
- 신뢰 관계(Trust relationships) 탭을 선택한 후 "Edit trust relationship"을 클릭하여 정책 문서를 확인합니다.
- 이 정책에서는 system:serviceaccount:default:my-sa라는 ID가 sts:AssumeRoleWithWebIdentity 액션을 사용하여 역할을 가정(assume)할 수 있도록 허용하고 있습니다. 이 정책의 주체(Principal) 는 OIDC 공급자(OIDC provider)입니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::339712784283:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/FDAFF52CF67728F6C2259CFE7C292369"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.ap-northeast-2.amazonaws.com/id/FDAFF52CF67728F6C2259CFE7C292369:aud": "sts.amazonaws.com",
"oidc.eks.ap-northeast-2.amazonaws.com/id/FDAFF52CF67728F6C2259CFE7C292369:sub": "system:serviceaccount:default:my-sa"
}
}
}
]
}
- Now let’s see what happens when we use this new Service Account within a Kubernetes 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
# 해당 SA를 파드가 사용 시 mutatingwebhook으로 Env,Volume 추가함 : AWS IAM 역할을 Pod에 자동으로 주입
kubectl get mutatingwebhookconfigurations pod-identity-webhook -o yaml
# 파드 생성 yaml에 없던 내용이 추가됨!!!!!
# Pod Identity Webhook은 mutating webhook을 통해 아래 Env 내용과 1개의 볼륨을 추가함
kubectl get pod eks-iam-test3
kubectl get pod eks-iam-test3 -o yaml
...
volumeMounts:
- 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 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
...
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::339712784283:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-nPJmqNjof7KO
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-wkt7f (ro)
...
Volumes:
aws-iam-token:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 86400
kube-api-access-wkt7f:
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
kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn
"arn:aws:sts::339712784283:assumed-role/eksctl-myeks-addon-iamserviceaccount-default--Role1-nPJmqNjof7KO/botocore-session-1742077181"
# 되는 것고 안되는 것은 왜그런가?
kubectl exec -it eks-iam-test3 -- aws s3 ls
kubectl exec -it eks-iam-test3 -- aws ec2 describe-instances --region ap-northeast-2
kubectl exec -it eks-iam-test3 -- aws ec2 describe-vpcs --region ap-northeast-2
- AWS CloudTrail 이벤트 중 AssumeRoleWithWebIdentity - Link
- Kubectl과 jq를 사용하여 Pod을 검사하면, 이제 두 개의 볼륨이 Pod에 마운트되어 있음을 확인할 수 있습니다.
그중 두 번째 볼륨은 변형 웹훅(mutating webhook)을 통해 마운트된 것입니다. aws-iam-token은 여전히 Kubernetes API 서버에 의해 생성되지만, 새로운 OIDC JWT 대상(audience)이 설정되었습니다.
# 파드에 볼륨 마운트 2개 확인
kubectl get pod eks-iam-test3 -o json | jq -r '.spec.containers | .[].volumeMounts'
[
{
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
"name": "kube-api-access-wkt7f",
"readOnly": true
},
{
"mountPath": "/var/run/secrets/eks.amazonaws.com/serviceaccount",
"name": "aws-iam-token",
"readOnly": true
}
]
# aws-iam-token 볼륨 정보 확인 : JWT 토큰이 담겨져있고, exp, aud 속성이 추가되어 있음
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"
}
}
]
}
}
#
kubectl get MutatingWebhookConfiguration
NAME WEBHOOKS AGE
aws-load-balancer-webhook 3 4h10m
kube-prometheus-stack-admission 1 4h3m
pod-identity-webhook 1 7h9m
vpc-resource-mutating-webhook 1 7h9m
# pod-identity-webhook 확인
kubectl describe MutatingWebhookConfiguration pod-identity-webhook
kubectl get MutatingWebhookConfiguration pod-identity-webhook -o yaml
...
name: iam-for-pods.amazonaws.com
# iam-for-pods.amazonaws.com은 AWS EKS에서 Pod Identity Webhook의 Mutating Webhook으로, 다음과 같은 작업을 수행합니다:
# Pod 생성 시 호출되어 ServiceAccount의 IAM 역할 정보를 확인.
# Pod에 환경 변수(AWS_ROLE_ARN, AWS_WEB_IDENTITY_TOKEN_FILE)와 토큰 볼륨을 주입.
# Pod이 AWS 리소스에 안전하고 세밀하게 접근할 수 있도록 인증 메커니즘 제공.
- 실행 중인 Pod에 exec 명령어를 사용하여 접속하고 이 토큰을 검사하면, 이전 서비스 계정(SA) 토큰과 약간 다르게 보인다는 것을 알 수 있습니다.
- 이제 이 토큰의 대상(audience)이 sts.amazonaws.com으로 설정되어 있으며, 이 토큰을 생성하고 서명한 발급자(issuer)는 여전히 우리의 OIDC 제공자입니다. 또한, 이 토큰의 만료 시간이 24시간으로 훨씬 짧아졌습니다. Pod 정의 또는 서비스 계정(Service Account) 정의에서 eks.amazonaws.com/token-expiration 주석(annotation)을 사용하여 서비스 계정의 만료 시간을 조정할 수 있습니다.
- 변형 웹훅(mutating webhook)은 Pod에 추가 토큰을 마운트하는 것 이상의 역할을 수행합니다.변형 웹훅은 환경 변수를 주입하는 역할도 합니다.
# AWS_WEB_IDENTITY_TOKEN_FILE 확인
IAM_TOKEN=$(kubectl exec -it eks-iam-test3 -- cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token)
echo $IAM_TOKEN
# JWT 웹 확인
{
"aud": [
"sts.amazonaws.com"
],
"exp": 1742163249,
"iat": 1742076849,
"iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/FDAFF52CF67728F6C2259CFE7C292369",
"jti": "1961584b-5ab1-4d46-b27b-04a7fd99dfd1",
"kubernetes.io": {
"namespace": "default",
"node": {
"name": "ip-192-168-3-88.ap-northeast-2.compute.internal",
"uid": "d486eada-9be0-44e6-b64e-872fdb995a36"
},
"pod": {
"name": "eks-iam-test3",
"uid": "797479f4-1ed6-4a3f-92ae-73072c29b7f1"
},
"serviceaccount": {
"name": "my-sa",
"uid": "58e3bdd7-955b-40b3-a46c-29a311ff06fc"
}
},
"nbf": 1742076849,
"sub": "system:serviceaccount:default:my-sa"
}
# env 변수 확인
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::339712784283:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-nPJmqNjof7KO"
},
{
"name": "AWS_WEB_IDENTITY_TOKEN_FILE",
"value": "/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
}
]
- 이제 우리의 워크로드가 IAM과 인증을 시도할 수 있는 토큰을 가지게 되었으므로, 다음 단계는 AWS IAM이 이러한 토큰을 신뢰하도록 만드는 것입니다. AWS IAM은 OIDC(OpenID Connect) ID 공급자를 사용한 연합된 ID(Federated Identities)를 지원합니다. 이 기능을 통해 IAM은 유효한 OIDC JWT를 받은 후, 지원되는 ID 공급자를 사용하여 AWS API 호출을 인증할 수 있습니다. 그런 다음, 이 토큰을 AWS STS의 AssumeRoleWithWebIdentity API 작업에 전달하여 임시 IAM 자격 증명을 받을 수 있습니다.
- Kubernetes 워크로드에서 사용하는 OIDC JWT 토큰은 암호학적으로 서명되어 있으며, IAM은 AWS STS AssumeRoleWithWebIdentity API 작업이 임시 자격 증명을 발급하기 전에 이러한 토큰을 신뢰하고 검증해야 합니다.
Kubernetes의 Service Account Issuer Discovery 기능의 일환으로, EKS는 퍼블릭 OpenID 공급자 구성 문서(Discovery endpoint) 및 토큰 서명을 검증하기 위한 공개 키(JSON Web Key Sets – JWKS)를 https://OIDC_PROVIDER_URL/.well-known/openid-configuration 에서 호스팅하고 있습니다.
# Let’s take a look at this endpoint. We can use the aws eks describe-cluster command to get the OIDC Provider URL.
IDP=$(aws eks describe-cluster --name myeks --query cluster.identity.oidc.issuer --output text)
# Reach the Discovery Endpoint
curl -s $IDP/.well-known/openid-configuration | jq -r '.'
# In the above output, you can see the jwks (JSON Web Key set) field, which contains the set of keys containing the public keys used to verify JWT (JSON Web Token).
# Refer to the documentation to get details about the JWKS properties.
curl -s $IDP/keys | jq -r '.'
- AWS CloudTrail 이벤트 중 AssumeRoleWithWebIdentity - Link
- IRSA를 가장 취약하게 사용하는 방법 : 정보 탈취 시 키/토큰 발급 약용 가능 - 링크
- AWS는 JWT 토큰의 유효성만 확인 하지만 토큰 파일과 서비스 계정에 지정된 실제 역할 간의 일관성을 보장하지는 않음 → Condition 잘못 설정 시, 토큰과 역할 ARN만 있다면 동일 토큰으로 다른 역할을 맡을 수 있음
- 실습 확인 후 파드 삭제 및 IRSA 제거
# 실습 확인 후 파드 삭제 및 IRSA 제거
kubectl delete pod eks-iam-test3
eksctl delete iamserviceaccount --cluster $CLUSTER_NAME --name my-sa --namespace default
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
kubectl get sa
☞ [신기능] EKS Pod Identity
- Amazon EKS Pod Identity: a new way for applications on EKS to obtain IAM credentials - Link
- Amazon EKS Pod Identity simplifies IAM permissions for applications on Amazon EKS clusters - Link
- [EKS Workshop] EKS Pod Identity : 오픈소스 Agent, Add-on 설치 지원 - Link
- eks-pod-identity-agent 설치
#
ADDON=eks-pod-identity-agent
aws eks describe-addon-versions \
--addon-name $ADDON \
--kubernetes-version 1.31 \
--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
# 모니터링
watch -d kubectl get pod -A
# 설치
aws eks create-addon --cluster-name $CLUSTER_NAME --addon-name eks-pod-identity-agent
혹은
eksctl create addon --cluster $CLUSTER_NAME --name eks-pod-identity-agent --version 1.3.5
# 확인
eksctl get addon --cluster $CLUSTER_NAME
kubectl -n kube-system get daemonset eks-pod-identity-agent
kubectl -n kube-system get pods -l app.kubernetes.io/name=eks-pod-identity-agent
kubectl get ds -n kube-system eks-pod-identity-agent -o yaml
...
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
...
# 네트워크 정보 확인
## EKS Pod Identity Agent uses the hostNetwork of the node and it uses port 80 and port 2703 on a link-local address on the node.
## This address is 169.254.170.23 for IPv4 and [fd00:ec2::23] for IPv6 clusters.
for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ss -tnlp | grep eks-pod-identit; echo "-----";done
for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ip -c route; done
for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ip -c -br -4 addr; done
for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ip -c addr; done
- 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 ap-northeast-2
# 확인
kubectl get sa
eksctl get podidentityassociation --cluster $CLUSTER_NAME
ASSOCIATION ARN NAMESPACE SERVICE ACCOUNT NAME IAM ROLE ARN
arn:aws:eks:ap-northeast-2:911283464785:podidentityassociation/myeks/a-blaanudo8dc1dbddw default s3-sa arn:aws:iam::911283464785: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:911283464785:podidentityassociation/myeks/a-pm07a3bg79bqa3p24",
"associationId": "a-pm07a3bg79bqa3p24"
}
]
}
# 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"
]
}
]
}
- EKS → 액세스 : Pod Identity 연결 확인 → 편집 클릭 해보기
- 테스트용 파드 생성 및 확인 : AssumeRoleForPodIdentity - Link
- AWS CloudTrail 중 AssumeRoleForPodIdentity - Link
# 서비스어카운트, 파드 생성
kubectl create sa s3-sa
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
#
kubectl get pod eks-pod-identity -o yaml | kubectl neat
kubectl exec -it eks-pod-identity -- aws sts get-caller-identity --query Arn
kubectl exec -it eks-pod-identity -- aws s3 ls
kubectl exec -it eks-pod-identity -- env | grep AWS
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
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE=/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
# 토큰 정보 확인
kubectl exec -it eks-pod-identity -- ls /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/
kubectl exec -it eks-pod-identity -- cat /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
- 실습 리소스 삭제
eksctl delete podidentityassociation --cluster $CLUSTER_NAME --namespace default --service-account-name s3-sa
kubectl delete pod eks-pod-identity
kubectl delete sa s3-sa
- IRSA vs EKS Pod Identity
EKS Pod Identity
|
IRSA
|
|
Role extensibility
|
You have to update the IAM role’s trust policy with the new EKS cluster OIDC provider endpoint each time you want to use the role in a new cluster.
|
You have to setup the role one time, to establish trust with the newly introduced EKS service principal “pods.eks.amazonaws.com”. After this one-time step, you don’t need to update the role’s trust policy each time it is used in a new cluster.
|
Account scalability
|
EKS cluster has an OpenID Connect (OIDC) issuer URL associated with it. To use IRSA, a unique OpenID connect provider needs to be created in IAM for each EKS cluster. IAM OIDC provider has a default global limit of 100 per AWS account. Keep this limit in consideration as you grow the number of EKS clusters per account.
|
EKS Pod Identity doesn’t require users to setup IAM OIDC provider, so this limit doesn’t apply.
|
Role scalability
|
In IRSA, you define the trust relationship between an IAM role and service account in the role’s trust policy. By default, the length of trust policy size is 2048. This means that you can typically define four trust relationships in a single policy. While you can get the trust policy length limit increased, you are typically limited to a maximum of eight trust relationships within a single policy.
|
EKS Pod Identity doesn’t require users to define trust relationship between IAM role and service account in IAM trust policy, so this limit doesn’t apply.
|
Role reusability
|
IAM role session tags are not supported.
|
IAM credentials supplied by EKS Pod Identity include support for role session tags. Role session tags enable administrators to author a single IAM role that can be used with multiple service accounts, with different effective permissions, by allowing access to AWS resources based on tags attached to them.
|
Cluster readiness
|
IAM roles used in IRSA need to wait for the cluster to be in a “Ready” state, to get the cluster’s OpenID Connect Provider URL to complete the IAM role trust policy configuration
|
IAM roles used in Pod identity can be created ahead of time.
|
Environments supported
|
IRSA can be used in EKS, EKS-A, ROSA, self-managed Kubernetes clusters on Amazon EC2
|
EKS Pod Identity is purpose built for EKS.
|
Supported EKS versions
|
All supported EKS versions
|
EKS version 1.24 and above. See EKS user guide for details.
|
Cross account access
|
Cross account here refers to the scenario where your EKS cluster is in one AWS account and the AWS resources that are being accessed by your applications is in another AWS account. In IRSA, you can configure cross account IAM permissions either by creating an IAM identity provider in the account your AWS resources live or by using chained AssumeRole operation. See EKS user guide on IRSA Cross-account IAM permissions for details.
|
EKS Pod Identity supports cross account access through resource policies and chained AssumeRole operation. See the previous section “How to perform cross account access with EKS Pod Identity” for details.
|
Mapping inventory
|
You can find the mapping of IAM roles to service accounts by parsing individual IAM role’s trust policy or by inspecting the annotations added to service accounts.
|
EKS Pod Identity offers a new ListPodIdentityAssociations API to centrally see the mapping of roles to service accounts.
|
- 고려사항
4. OWASP Kubernetes Top Ten
☞ 파드 탈옥하여 호스트 탈취 - Blog
- 파드 권한과 호스트 네임스페이스 공유로 호스트 탈취
NODE_NAME=myk8s-control-plane
kubectl create -n kube-system -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: root-shell-$NODE_NAME
namespace: kube-system
spec:
nodeName: $NODE_NAME
containers:
- command:
- /bin/cat
image: alpine:3
name: root-shell
securityContext:
privileged: true
tty: true
stdin: true
volumeMounts:
- mountPath: /host
name: hostroot
hostNetwork: true
hostPID: true
hostIPC: true
tolerations:
- effect: NoSchedule
operator: Exists
- effect: NoExecute
operator: Exists
volumes:
- hostPath:
path: /
name: hostroot
EOF
kubectl -n kube-system exec -it root-shell-$NODE_NAME -- chroot /host /bin/bash
root@myk8s-control-plane:/# id
uid=0(root) gid=0(root) groups=0(root),1(daemon),2(bin),3(sys),4(adm),6(disk),10(uucp),11,20(dialout),26(tape),27(sudo)
- 데몬셋으로 실행 ttl
kubectl create serviceaccount -n kube-system root-shell
kubectl create -n kube-system -f - <<EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: root-shell
namespace: kube-system
spec:
revisionHistoryLimit: 0
selector:
matchLabels:
app: root-shell
template:
metadata:
labels:
app: root-shell
spec:
terminationGracePeriodSeconds: 0
containers:
- command:
- /bin/cat
image: alpine:3
name: root-shell
tty: true
stdin: true
volumeMounts:
- mountPath: /host
name: hostroot
securityContext:
privileged: true
hostNetwork: true
hostPID: true
hostIPC: true
serviceAccountName: root-shell
hostNetwork: true
tolerations:
- effect: NoSchedule
operator: Exists
- effect: NoExecute
operator: Exists
volumes:
- hostPath:
path: /
name: hostroot
updateStrategy:
rollingUpdate:
maxUnavailable: 100%
type: RollingUpdate
EOF
- 윈도우 쿠버네티스에서 persistence volume에 명령어를 적으면, 명령어가 실행되는 취약점 - Link
- OWASP Kubernetes Top Ten : K8S 보안 위협 - Link
☞ (최성욱님) 악성코드분석님 : EKS pod가 IMDS API를 악용하는 시나리오 - 링크 Github Youtube → 실습*
☞ (최성욱님) 악성코드분석님 : Kubelet 미흡한 인증/인가 설정 시 위험 + kubeletct 툴 - 링크 Youtube → 실습*
5. Kyverno
Cloud Native Live: Cloud Native Policy as code with Kyverno!
[CNKCD2024] 쿠버네티스 정책 및 권한 관리를 위한 기술들 (이상근)
Kyverno : K8S Native Policy Mgmt - Link , Blog , Playground , Policy , Docs , Github , ddii , devocean , whchoi98 , Youtube
- [EKS Workshop] Policy management with Kyverno - Link
- Managing Pod Security on Amazon EKS with Kyverno - 링크 & PSS - Link
- Kyverno (Greek for “govern”) is a policy engine designed specifically for Kubernetes.
- Kyverno는 클라우드 네이티브 컴퓨팅 재단(CNCF, Cloud Native Computing Foundation) 프로젝트로, 팀이 협업하고 정책을 코드로(Policy-as-Code) 적용할 수 있도록 합니다.
- Kyverno는 YAML로 작성된 선언형 Kubernetes 리소스를 사용하며, 새로운 정책 언어를 배울 필요가 없습니다. 또한, 정책 적용 결과는 Kubernetes 리소스 및 이벤트로 제공됩니다.
- Kyverno 정책은 리소스 종류(Resource Kind), 이름(Name), 라벨 선택자(Label Selectors) 등을 사용하여 리소스를 매칭할 수 있으며, 다양한 방법으로 세밀한 정책 적용이 가능합니다.
- Kyverno 정책은 **리소스 구성을 검증(Validate), 변형(Mutate), 생성(Generate)**할 수 있으며,
이미지 서명 및 인증(Attestation) 검증도 지원하여 **소프트웨어 공급망 보안 표준(Supply Chain Security Standards)**을 완벽하게 적용할 수 있도록 합니다.
- 기능 - Link
- Kubernetes 리소스로서의 정책 (새로운 언어를 배울 필요 없음!)
- 모든 리소스를 검증(Validate), 변형(Mutate), 생성(Generate), 또는 정리(Cleanup, 제거) 가능
- 소프트웨어 공급망 보안을 위해 컨테이너 이미지를 검증
- 이미지 메타데이터 검사
- 라벨 선택자(Label Selectors) 및 와일드카드(Wildcards)를 사용하여 리소스 매칭
- 오버레이(Overlays, Kustomize와 유사!)를 사용하여 검증 및 변형 수행
- 네임스페이스 간 구성(Configuration) 동기화
- 허가 제어(Admission Controls)를 통해 규정을 준수하지 않는 리소스를 차단하거나 정책 위반 사항을 보고
- 자체 서비스 보고(Self-Service Reports, 독점적인 감사 로그 없음!)
- 자체 서비스 정책 예외(Self-Service Policy Exceptions) 지원
- Kyverno CLI를 사용하여 CI/CD 파이프라인에서 정책을 테스트하고 리소스를 검증한 후 클러스터에 적용
- Git 및 Kustomize와 같은 익숙한 도구를 사용하여 정책을 코드(Code)로 관리
- 동작 : Dynamic Admission Control 로 실행, Mutating/Validating admission 에서 동작하여 허용/거부 결과 반환
- 두 개의 주요 구성 요소는 Webhook Server와 Webhook Controller입니다.
- Webhook Server는 Kubernetes API 서버에서 들어오는 AdmissionReview 요청을 처리하고, 이를 엔진(Engine)으로 전달하여 처리합니다.
- 이 Webhook Server는 Webhook Controller에 의해 동적으로 구성되며, Webhook Controller는 설치된 정책을 감시하고, 해당 정책과 일치하는 리소스에 대해서만 웹훅 요청을 수행하도록 웹훅을 수정합니다.
- Webhook은 Kubernetes API 서버에서 들어오는 AdmissionReview 요청을 처리하고 엔진(Engine)으로 전달하는 서버입니다.
- 이 Webhook은 Webhook Controller에 의해 동적으로 구성되며, 설치된 정책을 감시하고 해당 정책과 일치하는 리소스에 대해서만 웹훅 요청을 수행하도록 웹훅을 수정합니다.
- Cert Renewer(인증서 갱신기)는 웹훅에 필요한 인증서(Certificate)를 감시하고 갱신하는 역할을 합니다. 이 인증서는 Kubernetes Secrets에 저장됩니다.
- Background Controller는 기존 리소스를 변형(Mutate-Existing)하거나 새로운 리소스를 생성(Generate)하는 모든 정책을 처리하며, UpdateRequests(중간 리소스)를 조정(Reconcile)하여 이를 수행합니다.
- Report Controllers는 정책 보고서(Policy Reports)를 생성하고 조정하는 역할을 하며, 이를 위한 중간 리소스인 Admission Reports 및 Background Scan Reports를 기반으로 동작합니다.
6. 파드/컨테이너 보안 컨텍스트
- 보안 컨텍스트 내용과 실습 참고 책 ⇒ 쿠버네티스 완벽 가이드
☞ 컨테이너 보안 컨텍스트 SecurityContext - 링크 ← 파드가 아님음 주의!
- 각 컨테이너에 대한 보안 설정 → 침해사고 발생 시 침해사고를 당한 권한을 최대한 축소하여 그 사고에 대한 확대를 방치
종류
|
개요
|
privileged
|
특수 권한을 가진 컨테이너로 실행
|
capabilities
|
Capabilities 의 추가와 삭제
|
allowPrivilegeEscalation
|
컨테이너 실행 시 상위 프로세스보다 많은 권한을 부여할지 여부
|
readOnlyRootFilesystem
|
root 파일 시스템을 읽기 전용으로 할지 여부
|
runAsUser
|
실행 사용자
|
runAsGroup
|
실행 그룹
|
runAsNonRoot
|
root 에서 실행을 거부
|
seLinuxOptions
|
SELinux 옵션
|
컨테이너 보안 컨텍스트 확인 : kube-system 파드 내 컨테이너 대상
kubectl get pod -n kube-system -o jsonpath={.items[*].spec.containers[*].securityContext} | jq
{
"allowPrivilegeEscalation": false,
"readOnlyRootFilesystem": true,
"runAsNonRoot": true
}
...
readOnlyRootFilesystem : root 파일 시스템을 읽기 전용으로 사용
#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: rootfile-readonly
spec:
containers:
- name: netshoot
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
securityContext:
readOnlyRootFilesystem: true
terminationGracePeriodSeconds: 0
EOF
# 파일 생성 시도
kubectl exec -it rootfile-readonly -- touch /tmp/text.txt
touch: /tmp/text.txt: Read-only file system
command terminated with exit code 1
# 기존 파일 수정 시도 : 아래 /etc/hosts파일 말고 다른 파일로 예제 만들어 두자
## 기본적으로 mount 옵션이 ro 이긴 한데. 특정 파일이나 폴더가 rw로 mount가 되어서 그곳에서는 파일 생성, 삭제등이 가능하네요.
## 특히 /etc/hosts 파일은 HostAliases로 항목 추가가 가능한데, 해당 파링은 kubelet에 의해 관리되고, 파드 생성/재시작 중 덮었여질 수 있다.
## /dev 라던가 /sys/fs/cgroup 폴더 안에서도 가능하네요.
## /etc/hostname 같은 경우는 호스트와 별도의 파일이지만 mount가 / (ro)에 속하게 되어 제한이 걸리네요.
kubectl exec -it rootfile-readonly -- cat /etc/hosts
kubectl exec -it rootfile-readonly -- sh -c "echo write > /etc/hosts"
kubectl exec -it rootfile-readonly -- cat /etc/hosts
# 특정 파티션, 파일의 ro/rw 확인
kubectl exec -it rootfile-readonly -- mount | grep hosts
/dev/root on /etc/hosts type ext4 (rw,relatime,discard)
kubectl exec -it rootfile-readonly -- mount | grep ro
overlay on / type overlay (ro,relatime~~~~~~~~~~
## /proc, /dev, /sys/fs/cgroup, /etc/hosts, /proc/kcore, /proc/keys, /proc/timer_list
kubectl exec -it rootfile-readonly -- mount | grep rw
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
...
# 파드 상세 정보 확인
kubectl get pod rootfile-readonly -o jsonpath={.spec.containers[0].securityContext} | jq
{
"readOnlyRootFilesystem": true
}
Linux Capabilities : Give a process some privileges, but not all the privileges of the root user - 링크
Linux Capabilities : 슈퍼 유저의 힘을 작은 조각으로 나눔, Capabilities are a per-thread attribute - 링크 man-pages
# Linux Capabilities 확인 : 현재 38개
capsh --print
...
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
# proc 에서 확인 : bit 별 Capabilities - 링크
cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
# 노드 Linux Capabilities 확인 : 아래 굵은색은 파드 기본 Linux Capabilities(14개)
ssh ec2-user@$N1 sudo yum -y install libcap-ng-utils
ssh ec2-user@$N1 sudo capsh --print
cap_chown 파일이나 디렉토리의 소유자를 변경할 수 있는 권한
cap_dac_override 파일이나 디렉토리의 접근 권한을 무시하고 파일이나 디렉토리에 대한 접근을 수행할 수 있는 권한 (DAC의 약자는 Discretionary access control이다)
cap_dac_read_search 파일이나 디렉토리를 읽거나 검색할 수 있는 권한
cap_fowner 파일이나 디렉토리의 소유자를 변경할 수 있는 권한
cap_fsetid 일이나 디렉토리의 Set-User-ID (SUID) 또는 Set-Group-ID (SGID) 비트를 설정할 수 있는 권한
cap_kill 다른 프로세스를 종료할 수 있는 권한
cap_setgid 프로세스가 그룹 ID를 변경할 수 있는 권한
cap_setuid 프로세스가 사용자 ID를 변경할 수 있는 권한
cap_setpcap 프로세스가 자신의 프로세스 권한을 변경할 수 있는 권한
cap_linux_immutable 파일의 immutability(불변성) 속성을 변경할 수 있는 권한을 제공
cap_net_bind_service 프로그램이 특정 포트에 바인딩(bind)하여 소켓을 개방할 수 있는 권한
cap_net_broadcast 프로세스가 네트워크 브로드캐스트 메시지를 보낼 수 있는 권한
cap_net_admin 네트워크 인터페이스나 소켓 설정을 변경할 수 있는 권한
cap_net_raw 네트워크 패킷을 송수신하거나 조작할 수 있는 권한
cap_ipc_lock 메모리 영역을 잠금(lock)하고 언락(unlock)할 수 있는 권한
cap_ipc_owner IPC 리소스(Inter-Process Communication Resources)를 소유하고, 권한을 변경할 수 있는 권한
cap_sys_module 커널 모듈을 로드하거나 언로드할 수 있는 권한
cap_sys_rawio 입출력(I/O) 포트와 같은 하드웨어 리소스를 직접 접근할 수 있는 권한
cap_sys_chroot 프로세스가 chroot() 시스템 콜을 호출하여 프로세스의 루트 디렉토리를 변경할 수 있는 권한
cap_sys_ptrace 다른 프로세스를 추적(trace)하거나 디버깅할 수 있는 권한
cap_sys_pacct 프로세스 회계(process accounting)를 위한 파일에 접근할 수 있는 권한
cap_sys_admin 시스템 관리자 권한을 제공하는 권한
cap_sys_boot 시스템 부팅과 관련된 작업을 수행할 수 있는 권한
cap_sys_nice 프로세스의 우선순위를 변경할 수 있는 권한
cap_sys_resource 자원 제한(resource limit)과 관련된 작업을 수행할 수 있는 권한
cap_sys_time 시스템 시간을 변경하거나, 시간 관련 시스템 콜을 사용할 수 있는 권한
cap_sys_tty_config 터미널 설정을 변경할 수 있는 권한
cap_mknod mknod() 시스템 콜을 사용하여 파일 시스템에 특수 파일을 생성할 수 있는 권한
cap_lease 파일의 잠금과 관련된 작업을 수행할 수 있는 권한
cap_audit_write 시스템 감사(audit) 로그에 대한 쓰기 권한
cap_audit_control 시스템 감사(audit) 설정과 관련된 작업을 수행할 수 있는 권한
cap_setfcap 파일 시스템 캡러빌리티(file system capability)을 설정할 수 있는 권한
cap_mac_override SELinux 또는 AppArmor과 같은 MAC(Mandatory Access Control) 시스템을 우회하고 자신의 프로세스가 접근 가능한 파일, 디바이스, 네트워크 등을 제한 없이 접근할 수 있는 권한
cap_mac_admin SELinux 또는 AppArmor과 같은 MAC(Mandatory Access Control) 시스템을 관리하고 수정할 수 있는 권한
cap_syslog 시스템 로그를 읽거나, 쓸 수 있는 권한
cap_wake_alarm 시스템의 RTC(Real-Time Clock)를 사용하여 장치를 깨우거나 슬립 모드를 해제할 수 있는 권한
cap_block_suspend 시스템의 전원 관리 기능 중 하나인 Suspend(절전 모드)를 방지하는 권한
cap_audit_read 시스템 감사(audit) 로그를 읽을 수 있는 권한
- 파드의 Linux Capabilities 기본 확인
# 샘플 파드 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: sample-capabilities
spec:
containers:
- name: nginx-container
image: masayaaoyama/nginx:capsh
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# 파드의 Linux Capabilities 기본 확인
kubectl exec -it sample-capabilities -- capsh --print | grep Current
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+ep
cap_chown,
cap_dac_override,
cap_fowner,
cap_fsetid,
cap_kill,
cap_setgid,
cap_setuid,
cap_setpcap,
cap_net_bind_service,
cap_net_raw,
cap_sys_chroot,
cap_mknod,
cap_audit_write,
cap_setfcap+ep
# proc 에서 확인 : bit 별 Capabilities - 링크
kubectl exec -it sample-capabilities -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
# 파드에서 시간 변경 시도
kubectl exec -it sample-capabilities -- date
Sat Mar 15 22:54:26 UTC 2025
# 파드에서 시간 변경 시도
kubectl exec -it sample-capabilities -- date -s "12:00:00"
kubectl exec -it sample-capabilities -- date
---
# 파드가 배포된 워커노드 확인
NAME NOMINATED NODE
sample-capabilities2 i-05beac6b1d64479d6
# 파드가 배포된 워커노드 IP 확인
kubectl get node -o wide | grep i-05beac6b1d64479d6
i-038c6921803a6372b 172.30.42.82 3.36.92.81
# 노드1로 접근
# ssh ec2-user@$N1
# 노드1에 systemd-timesyncd 종료
# root@i-038c6921803a6372b:~# systemctl stop systemd-timesyncd
# 파드에서 시간 변경 확인 >> 파드 변경 확인 후 노드의 date와 비교해보자
kubectl exec -it sample-capabilities -- date -s "12:00:00"
kubectl exec -it sample-capabilities -- date
- 파드에 Linux Capabilities 부여 및 삭제
#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: sample-capabilities2
spec:
containers:
- name: nginx-container
image: masayaaoyama/nginx:capsh
command: ["tail"]
args: ["-f", "/dev/null"]
securityContext:
capabilities:
add: ["NET_ADMIN", "SYS_TIME"]
drop: ["AUDIT_WRITE"]
terminationGracePeriodSeconds: 0
EOF
# 파드의 Linux Capabilities 기본 확인
kubectl exec -it sample-capabilities2 -- capsh --print | grep Current
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_admin,cap_net_raw,cap_sys_chroot,cap_sys_time,cap_mknod,cap_setfcap+ep
cap_chown,
cap_dac_override,
cap_fowner,
cap_fsetid,
cap_kill,
cap_setgid,
cap_setuid,
cap_setpcap,
cap_net_bind_service,
cap_net_admin, # 추가
cap_net_raw,
cap_sys_chroot,
cap_sys_time, # 추가
cap_mknod,
cap_setfcap+ep
# 제거됨 cap_audit_write
# 파드 상세 정보 확인
kubectl get pod sample-capabilities2 -o jsonpath={.spec.containers[0].securityContext} | jq
{
"capabilities": {
"add": [
"NET_ADMIN",
"SYS_TIME"
],
"drop": [
"AUDIT_WRITE"
]
}
}
# proc 에서 확인 : bit 별 Capabilities - 링크
kubectl exec -it sample-capabilities2 -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm: 000000008a0435fb
CapEff: 000000008a0435fb
# 파드에서 시간 변경 시도 : 시간 동기화하는 다른 우선순위가 있는것 같습니다! 혹신 아시는 분들은 댓글 부탁드립니다!
## 확인해 보니 시간이 변경되었지만 해당 파드가 동작되는 노드의 시간을 sync 하고 있습니다.
## 노드에 접근해서 systemd-timesyncd 종료하면은 노드의 바뀐 시간까지는 따라가는 것을 확인했습니다.
## systemctl stop systemd-timesyncd 그리고 파드에서 시간 변경시 해당 노드의 시간도 바뀌는 것을 확인 했습니다.
kubectl exec -it sample-capabilities2 -- date
kubectl exec -it sample-capabilities2 -- date -s "12:00:00"
kubectl exec -it sample-capabilities2 -- date
- 특수 권한 컨테이너 생성 : 호스트와 동등한 권한 부여됨
#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: sample-capabilities3
spec:
containers:
- name: nginx-container
image: masayaaoyama/nginx:capsh
command: ["tail"]
args: ["-f", "/dev/null"]
securityContext:
privileged: true
terminationGracePeriodSeconds: 0
EOF
# 파드의 Linux Capabilities 기본 확인
kubectl exec -it sample-capabilities3 -- capsh --print | grep Current
# 파드 상세 정보 확인
kubectl get pod sample-capabilities3 -o jsonpath={.spec.containers[0].securityContext} | jq
{
"privileged": true
}
# proc 에서 확인 : bit 별 Capabilities - 링크
kubectl exec -it sample-capabilities3 -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm: 000001ffffffffff
CapEff: 000001ffffffffff
- 다음 실습을 위해서 파드 삭제: kubectl delete pod --all
☞ 파드 보안 컨텍스트
- 파드 레벨에서 보안 컨텍스트를 적용 : 파드에 포함된 모든 컨테이너가 영향을 받음
- 파드와 컨테이너 정책 중복 시, 컨테이너 정책이 우선 적용됨
종류
|
개요
|
runAsUser
|
실행 사용자
|
runAsGroup
|
실행 그룹
|
runAsNonRoot
|
root 에서 실행을 거부
|
supplementalGroups
|
프라이머리 GUI에 추가로 부여할 GID 목록을 지정
|
fsGroup
|
파일 시스템 그룹 지정
|
systls
|
덮어 쓸 커널 파라미터 지정
|
seLinuxOptions
|
SELinux 옵션 지정
|
컨테이너 보안 컨텍스트 확인 : kube-system 파드 내 컨테이너 대상
kubectl get pod -n kube-system -o jsonpath={.items[*].spec.securityContext} | jq
...
실행 사용자 변경 : runuser 파드는 실행 사용자를 nobody(UID:65534) 사용자로 실행, 실행권한에 서브그룹 1001/1002 추가
#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: rundefault
spec:
containers:
- name: centos
image: centos:7
command: ["tail"]
args: ["-f", "/dev/null"]
securityContext:
readOnlyRootFilesystem: true
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: runuser
spec:
securityContext:
runAsUser: 65534
runAsGroup: 65534
supplementalGroups:
- 1001
- 1002
containers:
- name: centos
image: centos:7
command: ["tail"]
args: ["-f", "/dev/null"]
securityContext:
readOnlyRootFilesystem: true
terminationGracePeriodSeconds: 0
EOF
#
kubectl get pod rundefault -o jsonpath={.spec.securityContext} | jq
kubectl get pod runuser -o jsonpath={.spec.securityContext} | jq
# 실행 사용자 정보 확인
kubectl exec -it rundefault -- id
uid=0(root) gid=0(root) groups=0(root)
kubectl exec -it runuser -- id
uid=65534 gid=65534 groups=65534,1001,1002
# 프로세스 정보 확인
kubectl exec -it rundefault -- ps -axo uid,user,gid,group,pid,comm
UID USER GID GROUP PID COMMAND
0 root 0 root 1 tail
0 root 0 root 13 ps
kubectl exec -it runuser -- ps -axo uid,user,gid,group,pid,comm
UID USER GID GROUP PID COMMAND
65534 65534 65534 65534 1 tail
65534 65534 65534 65534 19 ps
root 사용자로 실행 제한 : 실행 사용자를 변경하지 않고 단순히 root 사용자로 실행을 거부하도록 설정 시 동작은 어떻게 될까?
#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: nonroot
spec:
securityContext:
runAsNonRoot: true
containers:
- name: netshoot
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# 이벤트 확인
kubectl events --for pod/nonroot
파일 시스템 그룹 지정
- 일반적으로 마운트한 볼륨의 소유자와 그룹은 root:root로 되어 있다. 실행 사용자를 변경한 경우에는 마운트한 볼륨에 권한이 없는 경우가 있다
- 따라서 마운트하는 볼륨의 그룹을 변경할 수 있도록 되어 있다 (setdig도 설정된다) - 예) emptyDir 혹은 PV 등 volumeMounts
#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: fsgoup1
spec:
volumes:
- name: vol1
emptyDir: {}
containers:
- name: centos
image: centos:7
command: [ "sh", "-c", "sleep 1h" ]
volumeMounts:
- name: vol1
mountPath: /data/demo
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: fsgoup2
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
volumes:
- name: vol2
emptyDir: {}
containers:
- name: centos
image: centos:7
command: [ "sh", "-c", "sleep 1h" ]
volumeMounts:
- name: vol2
mountPath: /data/demo
terminationGracePeriodSeconds: 0
EOF
#
kubectl get pod fsgoup1 -o jsonpath={.spec.securityContext} | jq
kubectl get pod fsgoup2 -o jsonpath={.spec.securityContext} | jq
# 실행 사용자 정보 확인
kubectl exec -it fsgoup1 -- id
uid=0(root) gid=0(root) groups=0(root)
kubectl exec -it fsgoup2 -- id
uid=1000 gid=3000 groups=3000,2000
# 프로세스 정보 확인
kubectl exec -it fsgoup1 -- ps -axo uid,user,gid,group,pid,comm
kubectl exec -it fsgoup2 -- ps -axo uid,user,gid,group,pid,comm
# 디렉터리 정보 확인 : fsgoup2파드의 마운트 볼륨 그룹의 GID가 2000 (fsGroup: 2000)
kubectl exec -it fsgoup1 -- ls -l /data
drwxrwxrwx 2 root root 4096 Apr 1 05:15 demo
kubectl exec -it fsgoup2 -- ls -l /data
drwxrwsrwx 2 root 2000 4096 Apr 1 05:15 demo
# fsgoup2파드에서 파일 생성 및 확인
kubectl exec -it fsgoup2 -- sh -c "echo write > /data/demo/sample.txt"
kubectl exec -it fsgoup2 -- cat /data/demo/sample.txt
kubectl exec -it fsgoup2 -- ls -l /data/demo/sample.txt
-rw-r--r-- 1 1000 2000 6 Apr 1 05:20 /data/demo/sample.txt
# fsgoup2파드에서 다른 디렉토리에 파일 생성 시도 >> 안되는 이유가 멀까요?
kubectl exec -it fsgoup2 -- sh -c "echo write > /data/sample.txt"
sh: /data/sample.txt: Permission denied
command terminated with exit code 1
sysctl을 사용한 커널 파라미터 설정 : 커널 파라미터 변경 적용을 위해서는 컨테이너에서도 설정 필요, 파드 수준 적용으로 컨테이너 간에 공유됨 - 링크
- 커널 파라미터는 안전한 것(safe)과 안전하지 않은 것(unsafe)으로 분류된다.
- 안전한 것 safe : 호스트의 커널과 적절하게 분리되어 있으며 다른 파드에 영향이 없는 것, 파드가 예상치 못한 리소스를 소비하지 않는 것
- kernel.shm_rmid_forced
- net.ipv4.ip_local_port_range
- net.ipv4.tcp_syncookies
- net.ipv4.ping_group_range (since Kubernetes 1.18),
- net.ipv4.ip_unprivileged_port_start (since Kubernetes 1.22).
- 안전하지 않은 것 unsafe : 사실상 대부분의 커널 파라미터 ⇒ 적용을 위해서는 kubelet 설정 필요
- 안전한 것 safe : 호스트의 커널과 적절하게 분리되어 있으며 다른 파드에 영향이 없는 것, 파드가 예상치 못한 리소스를 소비하지 않는 것
# Unsafe sysctls are enabled on a node-by-node basis with a flag of the kubelet
kubelet --allowed-unsafe-sysctls 'kernel.msg*,net.core.somaxconn' ...
- unsafe 파라미터를 변경 시도 :
#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: unsafe
spec:
securityContext:
sysctls:
- name: net.core.somaxconn
value: "12345"
containers:
- name: centos-container
image: centos:7
command: ["/bin/sleep", "3600"]
terminationGracePeriodSeconds: 0
EOF
#
kubectl events --for pod/unsafe
LAST SEEN TYPE REASON OBJECT MESSAGE
4s Normal Scheduled Pod/unsafe Successfully assigned default/unsafe to i-01af337c3d1004e24
- safe 파라미터 수정 ⇒ sysctl2 파드가 배포된 노드의 net.ipv4.ip_local_port_range 값과 다를 경우에는 어떻게 동작할까요?
#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: sysctl1
spec:
containers:
- name: centos-container
image: centos:7
command: ["/bin/sleep", "3600"]
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: sysctl2
spec:
securityContext:
sysctls:
- name: net.ipv4.ip_local_port_range
value: "1025 61000"
containers:
- name: centos-container
image: centos:7
command: ["/bin/sleep", "3600"]
terminationGracePeriodSeconds: 0
EOF
#
kubectl get pod sysctl1 -o jsonpath={.spec.securityContext} | jq
kubectl get pod sysctl2 -o jsonpath={.spec.securityContext} | jq
#
kubectl exec -it sysctl1 -- sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 60999
kubectl exec -it sysctl2 -- sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 1025 61000
initContainer 와 privileged 를 활용하여 unsafe 커널 파라미터를 강제로 변경
#
curl -s -O https://raw.githubusercontent.com/MasayaAoyama/kubernetes-perfect-guide/ko/2nd-edition/samples/chapter13/sample-sysctl-initcontainer.yaml
cat sample-sysctl-initcontainer.yaml| yq
kubectl apply -f sample-sysctl-initcontainer.yaml
#
kubectl describe pod sample-sysctl-initcontainer
kubectl get pod sample-sysctl-initcontainer -o json | jq
# 확인
kubectl exec -it sample-sysctl-initcontainer -c tools-container -- sysctl net.core.somaxconn
net.core.somaxconn = 12345
- 다음 실습을 위해서 파드 삭제: kubectl delete pod --all
7. Securing Secrets
EKS 워크숍 보안 솔루션 정리 : Sealed Secrets + AWS KMS , Amazon GuardDuty + 사고 대응 , AWS WAF
- [EKS Workshop] Securing Secrets Using Sealed Secrets - Link
Valut Secret Operator on K8s - Youtube 개요 설치 실습 비교 GitHub
https://www.youtube.com/watch?v=DQLDH2bgmro
- VSO 실습 코드(Dynamic,PKI,Static) : https://github.com/hashicorp/vault-secrets-operator/tree/main/demo/infra/app
- VSO 실습 코드(Static) : https://github.com/hashicorp-education/learn-vault-secrets-operator/tree/main/vault
- Vault Static Secret 샘플 : https://developer.hashicorp.com/vault/tutorials/kubernetes/vault-secrets-operator
- VSO API Reference : https://developer.hashicorp.com/vault/docs/platform/k8s/vso/api-reference
8. Amazon EKS Best Practices Guide for Security
보안 모범 사례 - Amazon EKS
이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.
docs.aws.amazon.com
kube-bench - Link
Falco : [workshop studio] Real Time Threat Detection with Falco - Link , Blog
datree - Link
(실습 완료 후) 자원 삭제
1. [운영서버 EC2]에서 원클릭 삭제 진행 : Karpenter 실습 환경 준비를 위해서 현재 EKS 실습 환경 전부 삭제
# eksctl delete cluster --name $CLUSTER_NAME && aws cloudformation delete-stack --stack-name $CLUSTER_NAME
nohup sh -c "eksctl delete cluster --name $CLUSTER_NAME && aws cloudformation delete-stack --stack-name $CLUSTER_NAME" > /root/delete.log 2>&1 &
# (옵션) 삭제 과정 확인
tail -f delete.log
2. testuser IAM User는 AWS 웹 관리콘솔에서 삭제
'AEWS study' 카테고리의 다른 글
7주차 - EKS Mode/Nodes - #2 (0) | 2025.03.22 |
---|---|
7주차 - EKS Mode/Nodes - #1 (0) | 2025.03.20 |
6주차 - EKS Security - #1 (0) | 2025.03.15 |
5주차 - EKS Autoscaling - #2 (0) | 2025.03.08 |
5주차 - EKS Autoscaling - #1 (0) | 2025.03.07 |