[AWES 3기] 6주차 스터디 내용 정리

ajufresh·4일 전
0

기초 이론

  • 정보 보안 3요소 : 기밀성, 무결성, 가용성
  • 액세스 제어(AAA) : 인증(Who?), 인가(What?), 감사
  • 암호
    • 공유 비밀키 암호화 방식 (shared secret key) : DES, 3DES, RC4/5
    • 공개키 암호화 방식 (public key) : RSA

[암호화 시 고려할 사항]
1. 보내는 사람 - 받는 사람 간 신원 확인 (수신자, 송신자 확인)
2. 변조를 확인할 수 있는 방법 (무결성, 해시 알고리즘)
3. 온라인 상에서 안전하게 전달하는 방법

[단방향 알고리즘]

  • 파일의 무결성을 확인하기 위해 해시 함수 적용의 결과값(다이제스*)을 비교
  • 해시 값의 길이 != 메시지 길이와 관계 없음 => 메시지가 1비트, 1기가바이트라도 고정된 길이의 해시 값을 출력
  • 메시지가 1비트라도 변화하면 해시 값은 매우 높은 확률로 다른 값이 되어야 함
  • 해시 값으로부터 메시지를 역산할 수 없음
  • 충돌을 발경하는 것이 어려움

[공개키 암호 방식]

  • 두 개의 키 사용
    • 공개키(Public Key): 누구나 접근 가능한 키
    • 개인키(Private Key): 소유자만 알고 있는 비밀 키
  • 작동 원리
    • 메시지 암호화: 수신자의 공개키로 암호화
    • 메시지 복호화: 수신자의 개인키로만 복호화 가능


(생활코딩 유튜브)

*다만 공개키 암호 방식은 지수승 연산 등과 같은 계산시간을 많이 필요로 하며, 덧셈과 같은 고속처리가 가능한 연산을 사용한 대칭키 방식에 비해, 처리속도가 느리다. 그래서 대부분 대칭 키를 사용 (공개키는 통신 쌍방 시 사용할 대칭키를 안전하게 분배할 때 사용)

[메시지 인증]
1. 해시 (Hash) 방법
2. Message Authentication Code(MAC), 메시지 인증 코드 - 메시지 무결성, 상대방 인증 수행
3. Hashed MAC : 사용자 인증 기능도 제공
4. 메시지 암호 방법에 의한 메시지 인증 방법 : 메시지 복호형 디지털 서명 기능

[RSA 디지털 서명(Sign) 방식]
공개키로 암호화 → 개인키로 복호화!
개인키로 암호화 → 공개키로 복호화!


(생활코딩 유튜브)

  1. A는 자신의 공개키를 공개 ⇒ B가 공개키를 획득
  2. A는 평문 메시지, (평문 메시지 + 자신의 개인키)암호화 값을 B에게 전달 ⇒ B가 평문 메시지와 암호화 값을 획득
  3. B는 암호화 값을 A의 공개키로 복호화하여 평문 메시지를 추출하고, 같이 전달받은 평문 메시지와 비교함.
  4. 이때 같을 경우, 반드시 A가 보낸 것임을 확인
    ⇒ 이것을 전자 서명

[RSA 전자 서명 따라해보기]
1. https://emn178.github.io/online-tools/rsa/key-generator/ 접속
2. [RSA Sign Message] 클릭
3. private key와 메세지에 테스트 메세지 입력

encryption은 상대방의 암호키를 사용했는데, 여기에서는 나의 공개키를 사용해서 서명한다.


output이 나오는 것을 확인 가능하다.

=> 이 메세지가 상대방에게 전송된 것!

  1. Verify signature

public key, signature 채워넣고 verify하면 valid가 뜨는 것을 확인 가능!

=> 공개키와 개인키로 암복호화가 가능하다.
X.509 공인인증서가 해당 방법으로 암복호화를 한다.

[X.509 공인인증서와 활용]

  1. 사전 준비
  • 인증서 발급
    • A와 B는 각자 신뢰할 수 있는 인증 기관(CA)으로부터 X.509 공인인증서를 발급받는다.
    • 인증서에는 각자의 공개키, 신원 정보, 유효기간, CA의 디지털 서명이 포함된다.
  • 개인키 보관
    • A와 B는 각자의 개인키를 안전하게 보관한다.
  1. 통신 시작
  • 인증서 교환
    • A가 B에게 통신을 요청한다.
    • A는 자신의 X.509 인증서를 B에게 전송한다.
    • B는 자신의 X.509 인증서를 A에게 전송한다.
  • 인증서 검증
    • A와 B는 상대방의 인증서가 신뢰할 수 있는 CA에 의해 서명되었는지 확인다.
    • 인증서의 유효기간을 확인한다.
    • 인증서가 해지되었는지 CRL(Certificate Revocation List) 또는 OCSP(Online Certificate Status Protocol)를 통해 확인한다.
  1. 세션 키 생성 및 교환
  • 임시 세션 키 생성
    • A는 대칭 암호화에 사용할 임시 세션 키를 생성한다.
  • 세션 키 암호화 및 전송
    • A는 생성한 세션 키를 B의 공개키(인증서에서 추출)로 암호화한다.
    • 암호화된 세션 키를 B에게 전송한다.
  • 세션 키 복호화
    • B는 자신의 개인키를 사용하여 세션 키를 복호화한다.
    • 이제 A와 B는 동일한 세션 키를 공유한다.
  1. 안전한 통신
  • 대칭 암호화 통신
    • A와 B는 공유된 세션 키를 사용하여 대칭 암호화 방식으로 메시지를 암호화하여 주고받는다.
  • 메시지 무결성 검증
    • 각 메시지에 MAC(Message Authentication Code) 또는 HMAC를 추가하여 메시지의 무결성을 검증한다.
    • 세션 키로부터 파생된 키를 사용하여 MAC를 생성하고 검증한다.

인증서란 CA인 제가 사용자의 신분을 보장하고, 동봉된 키는 사용자의 공개키가 확실함을 CA가 보장하는 것이다.

인증서에도 계층 구조가 있다.

[운영서버 인증서 확인]

#
sudo sysctl fs.inotify.max_user_watches=524288
sudo sysctl fs.inotify.max_user_instances=512

#
kind create cluster --name myk8s

# 인증서 확인
docker exec -it myk8s-control-plane ls -l /etc/kubernetes/pki


쿠버네티스 컨트롤 플레인의 /etc/kubernetes/pki 디렉터리에 있는 인증서와 키 파일들을 보여준다.

클러스터 루트 CA

  • ca.crt: 클러스터의 루트 인증 기관(CA) 인증서
  • ca.key: 클러스터 CA의 개인키 (민감한 정보로 root만 접근 가능)

API 서버 인증서

  • apiserver.crt: API 서버의 인증서 (kube-apiserver가 자신을 식별하기 위해 사용)
  • apiserver.key: API 서버의 개인키

API 서버 클라이언트 인증서

  • apiserver-kubelet-client.crt: API 서버가 kubelet에 접근할 때 사용하는 클라이언트 인증서
  • apiserver-kubelet-client.key: 해당 클라이언트의 개인키
  • apiserver-etcd-client.crt: API 서버가 etcd에 접근할 때 사용하는 클라이언트 인증서
  • apiserver-etcd-client.key: 해당 클라이언트의 개인키

프론트 프록시 인증

  • front-proxy-ca.crt: 프론트 프록시 CA 인증서 (kubectl proxy, aggregated API 서버용)
  • front-proxy-ca.key: 프론트 프록시 CA 개인키
  • front-proxy-client.crt: 프론트 프록시 클라이언트 인증서
  • front-proxy-client.key: 프론트 프록시 클라이언트 개인키

서비스 어카운트

  • sa.key: 서비스 어카운트 토큰 서명에 사용되는 개인키
  • sa.pub: 서비스 어카운트 토큰 검증에 사용되는 공개키

etcd 디렉터리

  • etcd/: etcd 관련 인증서와 키를 포함하는 별도 디렉터리 (별도의 PKI 인프라)
 
docker exec -it myk8s-control-plane cat /etc/kubernetes/pki/ca.crt
docker exec -it myk8s-control-plane openssl x509 -in /etc/kubernetes/pki/ca.crt -noout -text

인증서 기본 정보

  • 버전: X.509 버전 3
  • 서명 알고리즘: sha256WithRSAEncryption (SHA-256 해시 함수, RSA 암호화 사용)

발급자 및 주체

  • 발급자(Issuer): CN = kubernetes("kubernetes"라는 이름의 CA가 self-signed)
  • 주체(Subject): CN = kubernetes (동일)

공개키 정보

  • 알고리즘: RSA 암호화

키 사용 목적(Key Usage)

  • Digital Signature: 디지털 서명 생성 가능
  • Key Encipherment: 키 암호화 가능
  • Certificate Sign: 다른 인증서 서명 가능
kubectl get certificatesigningrequests
kubectl describe certificatesigningrequests

[kind 설치자의 kubeconfig 정보 확인]

# 아래 출력되는 client-certificate-data 값을 위 사이트에 붙여넣고 DECODE : 끝에 == 빼먹지 말것!
# 참고로 '운영서버1(EKS 관리자)'에서도 .kube/config 확인해보고 비교해보자.
cat $HOME/.kube/config
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0...
    server: https://127.0.0.1:50032
  name: kind-myk8s
contexts:
- context:
    cluster: kind-myk8s
    user: kind-myk8s
  name: kind-myk8s
current-context: kind-myk8s
kind: Config
preferences: {}
users:
- name: kind-myk8s
  user:
    client-certificate-data: LS0tL...
    client-key-data: LS0tLS1CR....

  • certificate-authority-data: kubectl이 API 서버의 신원을 검증하는 데 사용하는 Base64 인코딩된 CA 인증서
  • client-certificate-data: kubectl이 API 서버에 자신의 신원을 증명하는 데 사용하는 Base64 인코딩된 클라이언트 인증서
  • client-key-data: kubectl이 API 서버와의 암호화 통신 및 인증에 사용하는 Base64 인코딩된 개인 키
vi myuser.crt
-----BEGIN CERTIFICATE-----
MIIDKTCCAhGgAwIBAgIIeKzXmvzBrkswDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
...


openssl x509 -in myuser.crt -noout -text

Subject: O=kubeadm:cluster-admins, CN=kubernetes-admin인 . 것확인

cat $HOME/.kube/config
    client-key-data: LS0tLS1CR....
...

비밀 키이다.

cat $HOME/.kube/config
    certificate-authority-data: LS0...

루트 인증서이다.

[신규 관리자를 위한 인증서 설정]

# 하위 인증서를 위한 비밀키 생성
openssl genrsa -out fresh.key 2048

# 확인
cat fresh.key
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCpb0u8E7Km0CkX
...

# 인증서 사인 요청 파일(.csr) 파일 생성
openssl req -new -key fresh.key -out fresh.csr -subj "/O=kubeadm:cluster-admins/CN=fresh-cert"

# 확인
cat fresh.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICczCCAVsCAQAwLjEWMBQGA1UECgwNY2xvdWRuZXRhLW9yZzEUMBIGA1UEAwwL
...

# 출력 값을 아래 request 에 붙여넣기 : 끝에 == 빼먹지 말것!
cat fresh.csr | base64 | tr -d '\n'

# CSR 요청 : k8s 내부적으로 루트인증서의 비밀키로 서명해 반환. 즉 간접적으로 루트 인증서의 비밀키를 사용할 수 있음 셈.
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: fresh-csr
spec:
  signerName: kubernetes.io/kube-apiserver-client
  groups:
  - system:masters
  - system:authenticated
  request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ2V6Q0NBV01DQVFBd05qRWZNQjBHQTFVRUNnd1dhM1ZpWldGa2JUcGpiSFZ6ZEdWeUxXRmtiV2x1Y3pFVApNQkVHQTFVRUF3d0tabkpsYzJndFkyVnlkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DCmdnRUJBTWp3NmRKVXVYSC9lQ3ZENDhaUDM1NW1LYUVlTExBTGtOVmlHQzVtYVd1ZkhTTlhTRWlCazI4NlFZN1YKZTQyVnFQZGVPQXd6ZTdCdmRZbXpHMS9GaFE2VmFvNDhhdkJpQ2NkZEJvZmpkWHU5VGFvc2xWN0pJc0ZtOUhKcQpObzZjd1ltbnh1OEhNK3NqR1U5bC9oMHp2Vkp5SFJ5bHBOa3BnRDVHeE1qU3dZVzVkVEFpNmhVYmZoNUdybEFVCkVjYU5XNVpXT1pqWWNIMDQramdXY0VvT2NoTEpXSmJEeXd4UktwSktPWUdUK0lPcWRYc2orczRZRW5sN3dSbGMKUWNqajM4ZEsyRHV6c1ROTHEvYUV1UXdPVVp6NkZ1czRKR1A1aWVFSzExQml2TzZCYUxaWExOV3daTGVsRWtIagoyNUVOaVNXSE9oUHFuaGFyL2ZTa08xMDV4YWtDQXdFQUFhQUFNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUMzClpucjRzR01TSVI5NmU4VVZiaHRySzBXUTRjRzkvOEthRndxQWo0QytnbVhUMHlPNmt6SnUrVFp2aVNIU2xGYWUKS0F0VlRqQ2ZWcEJlS3UycjVZVk41QjlWOUxYWGtGaEV5ekVaTXRNbEdiUFg5bEZnY1NObXZ4T00xOGRMc1dkVgp1Wkk2aUVuU1BsSU0wM1BWaE5wWGRpUXdJSFVUU21LWW9KRENmd3RwVjJnY1RaMllPaHV1M2NIMk4vVlIvYnhTCnBHVHQvcjhiTkM2V2RVNHFUYjhtNlZjQ1ZTZWJ1U3M3V1RkT3EzUm9WQW9ESEQ3T3UzUlZrRHF1OTVkV01CVzEKQXdPZzdRRVlndjVtajIrb1JsZFg3ZHh2YnVRUTZDZU9yUDNSbFAvL1pPVDNPRnZsZnE1ZGVIS1RkZllWRmJvbgo1RDVianE4Y2hMQzVvOEJnSGFjbAotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0K
  usages:
  - digital signature
  - key encipherment
  - client auth
EOF

# csr 확인
kubectl get csr

...

fresh-csr이 생겼으나 아직 pending 상태이다.


# 'k8s 관리자' 입장에서 해당 서명 요청을 승인하자
kubectl certificate approve fresh-csr

# 확인 : 정상적으로 하위 인증서가 발급됨
kubectl get csr




# csr 에서 하위 인증서 추출
kubectl get csr fresh-csr -o jsonpath='{.status.certificate}' | base64 -d
kubectl get csr fresh-csr -o jsonpath='{.status.certificate}' | base64 -d > fresh.crt

#
openssl x509 -in fresh.crt -noout -text


# kubeconfig 에 새로운 사용자 등록
kubectl config set-credentials fresh-user --client-certificate=fresh.crt --client-key=fresh.key
kubectl config set-context kind-fresh --cluster=kind-myk8s --user=fresh-user

cat ~/.kube/config
kubectl config use-context kind-fresh

# ctx kind-fresh 로 k8s 정보 확인 시도
kubectl get node

유저명도 kind-myeks => kind-fresh로 변경된 것을 변경된 것을 확인할 수 있다.

이런 과정을 자동으로 해주는 것이 cert-manager!

[[K8S Docs] Security]

https://kubernetes.io/docs/concepts/security/controlling-access/

  • 인증(Authentication)
    • 사용자 또는 서비스 계정의 신원을 확인한다.
    • X.509 인증서, 베어러 토큰, 인증 프록시 등 다양한 방식을 지원
  • 인가(Authorization)
    • 인증된 사용자의 요청 권한을 확인한다.
    • RBAC, ABAC, Node, Webhook 등의 모듈을 사용
  • Admission Control
    • 객체 생성/수정/삭제 요청을 검증하고 추가 처리
    • 변경(Mutating)과 검증(Validating) 어드미션 컨트롤러로 구성된다.

[Practical Guide to Kubernetes API]
https://www.youtube.com/watch?v=PN3VqbZqmD8

  • Kubernetes API는 본질적으로 RESTful => 간단히 말해 통신을 위해 REST(Representational State Transfer) 아키텍처 스타일 과 표준을 따른다.
  • Stateless Communication - 클라이언트에서 서버로의 각 요청은 이전 상호작용에 의존하지 않고 요청을 이해하고 이행하는 데 필요한 모든 정보를 포함해야 한다. 즉, Kubernetes API와 통신할 때마다 이전 API 호출을 기억할 필요가 없다. 각 상호작용은 이전 메시지를 참조할 필요 없이 새 메시지를 보내는 것처럼 독립적이다.
  • Uniform Interface RESTful한 특성으로 인해 Kubernetes API는 매우 일관적이고 표준화된 통신 인터페이스를 가진다.
  • Self-descriptive Messages - RESTful API는 설명적 메시지를 사용하여 클라이언트와 서버 간에 통신한다. 즉, 각 상호 작용에는 요청된 작업과 처리 방법에 대한 정보가 포함된다.


https://blog.kubesimplify.com/practical-guide-to-kubernetes-api

0. 실습 환경 배포 & 소개

# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/myeks-6week.yaml

# 변수 지정
CLUSTER_NAME=myeks
SSHKEYNAME=<SSH  페이 이름>
MYACCESSKEY=<IAM Uesr 액세스 >
MYSECRETKEY=<IAM Uesr 시크릿 >

# CloudFormation 스택 배포
aws cloudformation deploy --template-file myeks-6week.yaml --stack-name $CLUSTER_NAME --parameter-overrides KeyName=$SSHKEYNAME SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32  MyIamUserAccessKeyID=$MYACCESSKEY MyIamUserSecretAccessKey=$MYSECRETKEY ClusterBaseName=$CLUSTER_NAME --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text

1. K8S 인증 인가

[Controlling Access to the Kubernetes API]

  • API 요청 처리 흐름
    • 인증(Authentication)
    • 인가(Authorization)
    • 어드미션 제어(Admission Control)
      • 요청 내용의 유효성을 검사하고 필요시 수정한다.
      • Mutating 컨트롤러가 먼저 실행되어 리소스를 수정한다.
      • Validating 컨트롤러가 다음으로 실행되어 검증만 수행한다.
      • 어드미션 컨트롤에 실패하면 요청은 거부된다.
        • 기본 컨트롤러: DefaultStorageClass, NamespaceLifecycle
        • 동적 컨트롤러: MutatingAdmissionWebhook, ValidatingAdmissionWebhook

[실습 환경]

  • 쿠버네티스에 사용자를 위한 서비스 어카운트(Service Account, SA)를 생성 : dev-k8s, infra-k8s
  • 사용자는 각기 다른 권한(Role, 인가)을 가짐 : dev-k8s(dev-team 네임스페이스 내 모든 동작) , infra-k8s(dev-team 네임스페이스 내 모든 동작)
  • 각각 별도의 kubectl 파드를 생성하고, 해당 파드에 SA 를 지정하여 권한에 대한 테스트를 진행
# 네임스페이스(Namespace, NS) 생성 및 확인
kubectl create namespace dev-team
kubectl create ns infra-team

# 네임스페이스 확인
kubectl get ns

# 네임스페이스에 각각 서비스 어카운트 생성 : serviceaccounts 약자(=sa)
kubectl create sa dev-k8s -n dev-team
kubectl create sa infra-k8s -n infra-team

# 서비스 어카운트 정보 확인
kubectl get sa -n dev-team
kubectl get sa dev-k8s -n dev-team -o yaml

kubectl get sa -n infra-team
kubectl get sa infra-k8s -n infra-team -o yaml


서비스 어카운트를 통해 API server를 사용할 수도 있다. (key = token)

# 각각 네임스피이스에 kubectl 파드 생성 - 컨테이너이미지
# docker run --rm --name kubectl -v /path/to/your/kube/config:/.kube/config bitnami/kubectl:latest
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: dev-kubectl
  namespace: dev-team
spec:
  serviceAccountName: dev-k8s
  containers:
  - name: kubectl-pod
    image: bitnami/kubectl:1.31.4
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: infra-kubectl
  namespace: infra-team
spec:
  serviceAccountName: infra-k8s
  containers:
  - name: kubectl-pod
    image: bitnami/kubectl:1.31.4
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# 확인
kubectl get pod -A
kubectl get pod -o dev-kubectl -n dev-team -o yaml
 serviceAccount: dev-k8s
 ...
kubectl get pod -o infra-kubectl -n infra-team -o yaml
 serviceAccount: infra-k8s
...


# 파드에 기본 적용되는 서비스 어카운트(토큰) 정보 확인
kubectl exec -it dev-kubectl -n dev-team -- ls /run/secrets/kubernetes.io/serviceaccount
kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/token
kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/namespace
kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/ca.crt

# 각각 파드로 Shell 접속하여 정보 확인 : 단축 명령어(alias) 사용
alias k1='kubectl exec -it dev-kubectl -n dev-team -- kubectl'
alias k2='kubectl exec -it infra-kubectl -n infra-team -- kubectl'

# 권한 테스트
k1 get pods
k1 run nginx --image nginx:1.20-alpine
k1 get pods -n kube-system

k2 get pods
k2 run nginx --image nginx:1.20-alpine
k2 get pods -n kube-system

# (옵션) kubectl auth can-i 로 kubectl 실행 사용자가 특정 권한을 가졌는지 확인
k1 auth can-i get pods
no

권한이 없어서 에러가 난다.

# Print the supported API resources on the server.
kubectl api-resources

# Print the supported API resources with more information
kubectl api-resources -o wide
  
# Print the supported API resources with a specific APIGroup
kubectl api-resources --api-group=""
kubectl api-resources --api-group="apps"
kubectl api-resources --api-group=metrics.k8s.io
kubectl api-resources --api-group=admissionregistration.k8s.io
kubectl api-resources --api-group=rbac.authorization.k8s.io
kubectl api-resources --api-group=apiextensions.k8s.io


# 각각 네임스페이스내의 모든 권한에 대한 롤 생성
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: role-dev-team
  namespace: dev-team
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]
EOF

cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: role-infra-team
  namespace: infra-team
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]
EOF

# 롤 확인 
kubectl get roles -n dev-team
kubectl get roles -n infra-team
kubectl get roles -n dev-team -o yaml
kubectl describe roles role-dev-team -n dev-team
...
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  *.*        []                 []              [*]

# 롤바인딩 생성 : '서비스어카운트 <-> 롤' 간 서로 연동
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: roleB-dev-team
  namespace: dev-team
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: role-dev-team
subjects:
- kind: ServiceAccount
  name: dev-k8s
  namespace: dev-team
EOF

cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: roleB-infra-team
  namespace: infra-team
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: role-infra-team
subjects:
- kind: ServiceAccount
  name: infra-k8s
  namespace: infra-team
EOF

# 롤바인딩 확인
kubectl get rolebindings -n dev-team
kubectl get rolebindings -n infra-team
kubectl get rolebindings -n dev-team -o yaml
kubectl describe rolebindings roleB-dev-team -n dev-team
...
Role:
  Kind:  Role
  Name:  role-dev-team
Subjects:
  Kind            Name     Namespace
  ----            ----     ---------
  ServiceAccount  dev-k8s  dev-team

# 각각 파드로 Shell 접속하여 정보 확인 : 단축 명령어(alias) 사용
alias k1='kubectl exec -it dev-kubectl -n dev-team -- kubectl'
alias k2='kubectl exec -it infra-kubectl -n infra-team -- kubectl'

# 권한 테스트
k1 get pods 
k1 run nginx --image nginx:1.20-alpine
k1 get pods
k1 delete pods nginx
k1 get pods -n kube-system
k1 get pods -n kube-system -v=6
k1 get nodes
k1 get nodes -v=6

k2 get pods 
k2 run nginx --image nginx:1.20-alpine
k2 get pods
k2 delete pods nginx
k2 get pods -n kube-system
k2 get nodes

# (옵션) kubectl auth can-i 로 kubectl 실행 사용자가 특정 권한을 가졌는지 확인
k1 auth can-i get pods
yes

아까와 다르게 롤 바인딩 한 뒤에 명령어 실행에 성공한다.
k1 get pods -n kube-system는 안되는 것을 확인할 수 있다.

kubectl delete ns dev-team infra-team

2. EKS 인증/인가

동작: 사용자/애플리케이션 → k8s 사용 시 ⇒ 인증은 AWS IAM, 인가는 K8S RBAC

  • role : apiGroups 와 resources 로 지정된 리소스에 대해 verbs 권한을 인가
  • verbs : *(모두 처리), create(생성), delete(삭제), get(조회), list(목록조회), patch(일부업데이트), update(업데이트), watch(변경감시)
# 설치
kubectl krew install access-matrix rbac-tool rbac-view rolesum whoami

# k8s 인증된 주체 확인
kubectl whoami
arn:aws:iam::9112...:user/admin


# Show an RBAC access matrix for server resources
kubectl access-matrix -h
kubectl access-matrix # Review access to cluster-scoped resources
kubectl access-matrix --namespace default # Review access to namespaced resources in 'default'

# RBAC Lookup by subject (user/group/serviceaccount) name
kubectl rbac-tool -h
kubectl rbac-tool lookup
kubectl rbac-tool lookup system:masters
  SUBJECT        | SUBJECT TYPE | SCOPE       | NAMESPACE | ROLE
+----------------+--------------+-------------+-----------+---------------+
  system:masters | Group        | ClusterRole |           | cluster-admin

kubectl rbac-tool lookup system:nodes # eks:node-bootstrapper
kubectl rbac-tool lookup system:bootstrappers # eks:node-bootstrapper
kubectl describe ClusterRole eks:node-bootstrapper

# RBAC List Policy Rules For subject (user/group/serviceaccount) name
kubectl rbac-tool policy-rules
kubectl rbac-tool policy-rules -e '^system:.*'
kubectl rbac-tool policy-rules -e '^system:authenticated'

# Generate ClusterRole with all available permissions from the target cluster
kubectl rbac-tool show

# Shows the subject for the current context with which one authenticates with the cluster
kubectl rbac-tool whoami
{Username: "arn:aws:iam::911283...:user/admin",      <<-- 과거 "kubernetes-admin"에서 변경됨
 UID:      "aws-iam-authenticator:911283.:AIDA5ILF2FJI...",
 Groups:   ["system:authenticated"],                 <<-- 과거 "system:master"는 안보임
 Extra:    {accessKeyId:  ["AKIA5ILF2FJI....."],
            arn:          ["arn:aws:iam::9112834...:user/admin"],
            canonicalArn: ["arn:aws:iam::9112834...:user/admin"],
            principalId:  ["AIDA5ILF2FJI...."],
            sessionName:  [""]}}
            sigs.k8s.io/aws-iam-authenticator/principalId: ["AIDA5IL..."]}}

# Summarize RBAC roles for subjects : ServiceAccount(default), User, Group
kubectl rolesum -h
kubectl rolesum aws-node -n kube-system    # sa
kubectl rolesum -k User system:kube-proxy  # user
kubectl rolesum -k Group system:masters    # group
kubectl rolesum -k Group system:nodes
kubectl rolesum -k Group system:authenticated
Policies:
• [CRB] */system:basic-user ⟶  [CR] */system:basic-user
  Resource                                       Name  Exclude  Verbs  G L W C U P D DC  
  selfsubjectaccessreviews.authorization.k8s.io  [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
  selfsubjectreviews.authentication.k8s.io       [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
  selfsubjectrulesreviews.authorization.k8s.io   [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
• [CRB] */system:discovery ⟶  [CR] */system:discovery
• [CRB] */system:public-info-viewer ⟶  [CR] */system:public-info-viewer

# [운영서버1 EC2 : 터미널1] A tool to visualize your RBAC permissions
kubectl rbac-view
INFO[0000] Getting K8s client
INFO[0000] serving RBAC View and http://localhost:8800

## 이후 해당 운영서버1 EC2 공인 IP:8800 웹 접속 : 최초 접속 후 정보 가져오는데 다시 시간 걸림 (2~3분 정도 후 화면 출력됨) 
echo -e "RBAC View Web http://$(curl -s ipinfo.io/ip):8800"


가지고 있는 role list를 확인할 수 있다.

[EKS 인증/인가 확인]
API Server -> k8s, IAM User -> aws
k8s의 자격증명과 aws의 자격증명을 이어주는 방법? => pre-signed url을 가지고 iam에서 확인하고, k8s의 리소스를 사용할 수 있도록 통과시켜줌

  1. kubectl 명령 → aws eks get-token → STS에 토큰 요청
# sts caller id의 ARN 확인
aws sts get-caller-identity --query Arn
"arn:aws:iam::<자신의 Account ID>:user/admin"

# kubeconfig 정보 확인
cat ~/.kube/config
...
- name: admin@myeks.ap-northeast-2.eksctl.io
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args:
      - eks
      - get-token
      - --output
      - json
      - --cluster-name
      - myeks
      - --region
      - ap-northeast-2
      command: aws
      env:
      - name: AWS_STS_REGIONAL_ENDPOINTS
        value: regional
      interactiveMode: IfAvailable
      provideClusterInfo: false

# Get  a token for authentication with an Amazon EKS cluster.
# This can be used as an alternative to the aws-iam-authenticator.
aws eks get-token help

# 임시 보안 자격 증명(토큰)을 요청 : expirationTimestamp 시간경과 시 토큰 재발급됨
aws eks get-token --cluster-name $CLUSTER_NAME | jq
aws eks get-token --cluster-name $CLUSTER_NAME | jq -r '.status.token'
aws eks get-token --cluster-name $CLUSTER_NAME --debug | jq
...

token => EKS Cluster 인증을 위해 요청한 토큰. aws-iam-authenticator의 대체

  1. kubectl의 Client-Go 라이브러리는 Pre-Signed URL을 Bearer Token으로 EKS API Cluster Endpoint로 요청
# aws eks get-token --cluster-name $CLUSTER_NAME --debug | jq 출력 내용 아래 부분 확인
DEBUG - Endpoint provider result: https://sts.ap-northeast-2.amazonaws.com

Action=GetCallerIdentity&

Version=2011-06-15&

X-Amz-Algorithm=AWS4-HMAC-SHA256&

X-Amz-Credential=AKIA5ILF.../20230525/ap-northeast-2/sts/aws4_request&

X-Amz-Date=20230525T120720Z&

X-Amz-Expires=60&

X-Amz-SignedHeaders=host;x-k8s-aws-id&

X-Amz-Signature=6e09b846da702767f38c78831986cb558.....

나에 대한 서명 & 무결성 처리

  1. EKS API는 Token Review 를 Webhook token authenticator에 요청 ⇒ (STS GetCallerIdentity 호출) AWS IAM 해당 호출 인증 완료 후 User/Role에 대한 ARN 반환
# tokenreviews api 리소스 확인 
kubectl api-resources | grep authentication
tokenreviews                                   authentication.k8s.io/v1               false        TokenReview

# List the fields for supported resources.
kubectl explain tokenreviews
...
DESCRIPTION:
     TokenReview attempts to authenticate a token to a known user. Note:
     TokenReview requests may be cached by the webhook token authenticator
     plugin in the kube-apiserver.

결국 AWS IAM쪽으로 요청을 보내야되는데, 이때 사용하는게 tokenreviews이다.
= TokenReview API는 제공된 토큰을 인증하여 알려진 사용자와 연결시키는 시도를 한다. 즉, 토큰이 유효한지 검증하고 해당 토큰을 가진 사용자가 누구인지 확인하는 역할을 한다.

  1. [ConfigMap 방식] 이제 쿠버네티스 RBAC 인가를 처리한다.

해당 IAM User/Role 확인이 되면 k8s aws-auth configmap에서 mapping 정보를 확인한 후, k8s 인가 허가가 되면 최종적으로 동작 실행을 한다.

# aws-auth 컨피그맵 확인 : 현재는 IAM access entry 방식 우선 적용으로 아래 출력 내용과 다를 수 있습니다.
kubectl get cm -n kube-system aws-auth -o yaml

# EKS 설치한 IAM User 정보 
kubectl rbac-tool whoami
{Username: "kubernetes-admin",
 UID:      "aws-iam-authenticator:9112834...:AIDA5ILF2FJIR2.....",
 Groups:   ["system:masters",
            "system:authenticated"],
...

# system:masters , system:authenticated 그룹의 정보 확인
kubectl rbac-tool lookup system:masters
kubectl rbac-tool lookup system:authenticated
kubectl rolesum -k Group system:masters
kubectl rolesum -k Group system:authenticated

# system:masters 그룹이 사용 가능한 클러스터 롤 확인 : cluster-admin
kubectl describe clusterrolebindings.rbac.authorization.k8s.io cluster-admin

# cluster-admin 의 PolicyRule 확인 : 모든 리소스  사용 가능!
kubectl describe clusterrole cluster-admin


# system:authenticated 그룹이 사용 가능한 클러스터 롤 확인
kubectl describe ClusterRole system:discovery
kubectl describe ClusterRole system:public-info-viewer
kubectl describe ClusterRole system:basic-user
kubectl describe ClusterRole eks:podsecuritypolicy:privileged

[EC2 Instance Profile(IAM Role)에 맵핑된 k8s rbac 확인 해보기]

# 노드에 STS ARN 정보 확인 : Role 뒤에 인스턴스 ID!
for node in $N1 $N2 $N3; do ssh ec2-user@$node aws sts get-caller-identity --query Arn; done
"arn:aws:sts::911283464785:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-LHQ7DWHQQRZJ/i-07c9162ed08d23e6f"
"arn:aws:sts::911283464785:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-LHQ7DWHQQRZJ/i-00d9d24c0af0d6815"
"arn:aws:sts::911283464785:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-LHQ7DWHQQRZJ/i-031e672f89572abe8"

# aws-auth 컨피그맵 확인
kubectl describe configmap -n kube-system aws-auth
...
mapRoles:
----
- groups:
  - system:nodes
  - system:bootstrappers
  rolearn: arn:aws:iam::911283464785:role/eksctl-myeks-nodegroup-ng-f6c38e4-NodeInstanceRole-1OU85W3LXHPB2
  username: system:node:{{EC2PrivateDNSName}}
...

# Get IAM identity mapping(s)
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
ARN												USERNAME		GROUPS					ACCOUNT
arn:aws:iam::911283464785:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-1OS1WSTV0YB9X	system:node:{{EC2PrivateDNSName}}	system:bootstrappers,system:nodes
...


# awscli 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: awscli-pod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: awscli-pod
  template:
    metadata:
      labels:
        app: awscli-pod
    spec:
      containers:
      - name: awscli-pod
        image: amazon/aws-cli
        command: ["tail"]
        args: ["-f", "/dev/null"]
      terminationGracePeriodSeconds: 0
EOF

# 파드 생성 확인
kubectl get pod -owide

# 파드 이름 변수 지정
APODNAME1=$(kubectl get pod -l app=awscli-pod -o jsonpath="{.items[0].metadata.name}")
APODNAME2=$(kubectl get pod -l app=awscli-pod -o jsonpath="{.items[1].metadata.name}")
echo $APODNAME1, $APODNAME2

# awscli 파드에서 EC2 InstanceProfile(IAM Role)의 ARN 정보 확인
kubectl exec -it $APODNAME1 -- aws sts get-caller-identity --query Arn
kubectl exec -it $APODNAME2 -- aws sts get-caller-identity --query Arn

# awscli 파드에서 EC2 InstanceProfile(IAM Role)을 사용하여 AWS 서비스 정보 확인
kubectl exec -it $APODNAME1 -- aws ec2 describe-instances --region ap-northeast-2 --output table --no-cli-pager
kubectl exec -it $APODNAME2 -- aws ec2 describe-vpcs --region ap-northeast-2 --output table --no-cli-pager
 
# EC2 메타데이터 확인 : IDMSv1은 Disable, IDMSv2 활성화 상태, IAM Role - 링크
kubectl exec -it $APODNAME1 -- bash 
-----------------------------------
...

NodeRole이 광범위하게 지정되어있는데 이러면 보안에 취약하기 때문에 적절한 권한만 주는게 좋다.

# node 의 IAM Role ARN을 변수로 지정
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
NODE_ROLE=<각자 자신의 노드 Role 이름>
NODE_ROLE=eksctl-myeks-nodegroup-ng1-NodeInstanceRole-cBF2j4a4K5OP

# awscli 파드에서 kubeconfig 정보 생성 및 확인 >> kubeconfig 에 정보가 기존 iam user와 차이점은?
kubectl exec -it $APODNAME1 -- aws eks update-kubeconfig --name $CLUSTER_NAME --role-arn $NODE_ROLE
kubectl exec -it $APODNAME1 -- cat /root/.kube/config
...
  - --role
  - eksctl-myeks-nodegroup-ng1-NodeInstanceRole-3GQR27I04PAJ

kubectl exec -it $APODNAME2 -- aws eks update-kubeconfig --name $CLUSTER_NAME --role-arn $NODE_ROLE
kubectl exec -it $APODNAME2 -- cat /root/.kube/config

3. EKS IRSA & Pod Identity

파드를 최소 권한으로 사용하기 위해서는? (IRSA vs 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 : 테라폼
...

==> 이렇게 설정하면 노드 전체에 권한이 들어감 (Instance Profile)

보통 IRSA보단 Pod Identity를 더 권장함

[IRSA (IAM Roles for Service Accounts)]
AWS EKS에서 제공하는 기능으로, Kubernetes 서비스 계정을 AWS IAM 역할과 연결할 수 있게 해준다.

  • 구성 요소
    • OpenID Connect(OIDC) 제공자
    • IAM 역할
    • Kubernetes 서비스 계정 + 특별 어노테이션
  • 작동 방식
    • Pod에 서비스 계정을 할당
    • 서비스 계정에는 AWS IAM 역할 ARN을 지정하는 어노테이션 추가
    • EKS Pod Identity 에이전트(aws-node DaemonSet의 일부)가 AWS STS 토큰을 관리
    • 컨테이너 내부에서는 환경 변수를 통해 AWS SDK가 자동으로 자격 증명 사용

EKS Pod Identity(AWS Pod Identity)
IRSA의 후속 기능으로, AWS가 2023년에 출시한 보다 간소화된 Pod 인증 메커니즘

  • 구성 요소
    • EKS Pod Identity Agent
    • IAM 역할
    • Kubernetes 서비스 계정 + 관련 어노테이션
  • 작동 방식
    • EKS Pod Identity Agent가 IAM 자격 증명 관리
    • OIDC 제공자 설정 필요 없음
    • 서비스 계정에 IAM 역할 연결은 유사하나 설정 프로세스가 단순화됨

[Service Account Token (SAT) Volume Projection]

서비스 계정 토큰을 이용해서 서비스와 서비스, 즉 파드(pod)와 파드(pod)의 호출에서 자격 증명으로 사용하기 위해 대상(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 바인딩된 서비스 어카운트 토큰 볼륨]

- 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
  1. kube-apiserver로부터 TokenRequest API를 통해 얻은 서비스어카운트토큰(ServiceAccountToken). 서비스어카운트토큰은 기본적으로 1시간 뒤에, 또는 파드가 삭제될 때 만료된다. 서비스어카운트토큰은 파드에 연결되며 kube-apiserver를 위해 존재한다.
  2. kube-apiserver에 대한 연결을 확인하는 데 사용되는 CA 번들을 포함하는 컨피그맵(ConfigMap).
  3. 파드의 네임스페이스를 참조하는 DownwardA

[Configure a Pod to Use a Projected Volume for Storage]

# 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:
      defaultMode: 420
      sources:
      - secret:
          name: user
      - secret:
          name: pass
  - name: kube-api-access-n6n9v
    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

# 시크릿 확인
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

all-in-one이라는 projected volume이 정의되어 있으며, user 시크릿, pass 시크릿을 하나의 디렉토리에 투영한다.
kube-api-access 볼륨이라는 파드 YAML에서 또 다른 projected 볼륨인 kube-api-access-n6n9v가 자동으로 생성되고, 이 볼륨에서는 serviceAccountToken (API 서버에 인증하기 위한 토큰), configMap (CA 인증서), downwardAPI (파드의 네임스페이스 정보)를 하나의 디렉토리에 투영한다.

즉, 파드 내에서 /projected-volume/ 디렉토리를 확인하면 두 시크릿의 파일들이 함께 마운트되어 있는 것을 확인할 수 있다.
username.txt: "admin" 내용 포함
password.txt: "1f2d1e2e67df" 내용 포함

[k8s api 접근 단계]

[JWT]

JSON 형태로 토큰 형식을 정의한 스펙
- Header: 토큰 형식와 암호화 알고리즘
- Payload: 전송하려는 데이터를 JSON 형식으로 기입
- Signature: Header와 Payload의 변조 가능성을 검증

[OIDC]
사용자를 인증해 사용자에게 액세스 권한을 부여할 수 있게 해주는 프로토콜

  • OAuth 2.0 : 권한허가 처리 프로토콜, 다른 서비스에 접근할 수 있는 권한을 획득하거나 반대로 다른 서비스에게 권한을 부여할 수 있음
  • OIDC OpenID Connect = OpenID 인증 + OAuth2.0 인가, JSON 포맷을 이용한 RESful API 형식으로 인증
  • IdP Open Identify Provider : 구글, 카카오와 같이 OpenID 서비스를 제공하는 신원 제공자
  • RP Relying Party : 사용자를 인증하기 위해 IdP에 의존하는 주체

[실습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

instance profile로 실행하는 것을 확인할 수 있다.

[실습2 - Service Account]

# 파드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": "1a8fcaee12b3a8f191327b5e9b997487ae93baab"
}

# 페이로드 : OAuth2에서 쓰이는 aud, exp 속성 확인! > projectedServiceAccountToken 기능으로 토큰에 audience,exp 항목을 덧붙힘
## iss 속성 : EKS OpenID Connect Provider(EKS IdP) 주소 > 이 EKS IdP를 통해 쿠버네티스가 발급한 토큰이 유요한지 검증
{
  "aud": [
    "https://kubernetes.default.svc"  # 해당 주소는 k8s api의 ClusterIP 서비스 주소 도메인명, kubectl get svc kubernetes
  ],
  "exp": 1716619848,
  "iat": 1685083848,
  "iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/F6A7523462E8E6CDADEE5D41DF2E71F6",
  "jti": "ee823c34-f020-4f77-90f3-61fab4de244a",
  "kubernetes.io": {
    "namespace": "default",
    "node": {
      "name": "ip-192-168-1-70.ap-northeast-2.compute.internal",
      "uid": "f4fabf42-4a9c-43f0-8a1e-edeb2a8fbb42"
    },
    "pod": {
      "name": "eks-iam-test2",
      "uid": "10dcccc8-a16c-4fc7-9663-13c9448e107a"
    },
    "serviceaccount": {
      "name": "default",
      "uid": "acb6c60d-0c5f-4583-b83b-1b629b0bdd87"
    },
    "warnafter": 1685087455
  },
  "nbf": 1685083848,
  "sub": "system:serviceaccount:default:default"
}

# 파드2 삭제
kubectl delete pod eks-iam-test2

마찬가지로 에러가 발생한다.

[실습3 - IRSA]

# 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)


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>

my-sa라는 service account에 정책을 부착시키는 것이다.

pod에서 사용할 Role이 생성된 것을 확인할 수 있다.

# 파드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

# Pod Identity Webhook은 mutating webhook을 통해 아래 Env 내용과 1개의 볼륨을 추가함
kubectl get pod eks-iam-test3
kubectl get pod eks-iam-test3 -o yaml

Role이 방금 sa 생성한 것이 들어간 것을 확인할 수 있다
=> mutating admission 단계에서 webhook이 pod spec을 변조시키는 것이다!

# 파드에서 aws cli 사용 확인
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn
"arn:aws:sts::911283464785:assumed-role/eksctl-myeks-addon-iamserviceaccount-default-Role1-GE2DZKJYWCEN/botocore-session-1685179271"

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

이번에는 아까와 달리 된 것을 확인할 수 있다 (에러가 안남)

[EKS Pod Identity]
IRSA는 이름만 알면 권한을 탈취할 수 있다.

기존에는 AWS 인증을 위해 제 3자(IDC Provider)가 있었는데, 이걸 AWS 내에서 다 처리하게 함 (Pod Identity Agent, EKS Auth API)

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


# 
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"
      ]
    }
  ]
}

# 서비스어카운트, 파드 생성
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

마찬가지가 잘된다 => 아까와 다르게 IRSA가 아니라 Pod Identity로 조회 한것

4. OWASP Kubernetes Top Ten

5. Kyverno

Policy-as-Code

Kyverno policies can be used to validate, mutate, and generate resource configurations, and also validate image signatures and attestations, providing all the necessary building blocks for a complete software supply chain security standards enforcement.

# 설치
# EKS 설치 시 참고 https://kyverno.io/docs/installation/platform-notes/#notes-for-eks-users
# 모니터링 참고 https://kyverno.io/docs/monitoring/
cat << EOF > kyverno-value.yaml
config:
  resourceFiltersExcludeNamespaces: [ kube-system ]

admissionController:
  serviceMonitor:
    enabled: true

backgroundController:
  serviceMonitor:
    enabled: true

cleanupController:
  serviceMonitor:
    enabled: true

reportsController:
  serviceMonitor:
    enabled: true
EOF
kubectl create ns kyverno
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno --version 3.3.7 -f kyverno-value.yaml -n kyverno

# 확인
kubectl get all -n kyverno
kubectl get crd | grep kyverno
kubectl get pod,svc -n kyverno

# (참고) 기본 인증서 확인 https://kyverno.io/docs/installation/customization/#default-certificates
# step-cli 설치 https://smallstep.com/docs/step-cli/installation/
wget https://dl.smallstep.com/cli/docs-cli-install/latest/step-cli_amd64.rpm
sudo rpm -i step-cli_amd64.rpm

#
kubectl -n kyverno get secret
kubectl -n kyverno get secret kyverno-svc.kyverno.svc.kyverno-tls-ca -o jsonpath='{.data.tls\.crt}' | base64 -d
kubectl -n kyverno get secret kyverno-svc.kyverno.svc.kyverno-tls-ca -o jsonpath='{.data.tls\.crt}' | base64 -d | step certificate inspect --short
X.509v3 Root CA Certificate (RSA 2048) [Serial: 0]
  Subject:     *.kyverno.svc
  Issuer:      *.kyverno.svc
  Valid from:  2024-04-07T06:05:52Z
          to:  2025-04-07T07:05:52Z

#
kubectl get validatingwebhookconfiguration kyverno-policy-validating-webhook-cfg -o jsonpath='{.webhooks[0].clientConfig.caBundle}' | base64 -d | step certificate inspect --short
X.509v3 Root CA Certificate (RSA 2048) [Serial: 0]
  Subject:     *.kyverno.svc
  Issuer:      *.kyverno.svc
  Valid from:  2024-04-07T06:05:52Z
          to:  2025-04-07T07:05:52Z

*Kyverno를 사용하면 prometheus에서도 사용이 가능하다.

  • Validation : 파드에 라벨(lables) 검증
  • Mutation : 파드에 라벨(lables) 추가
  • Generation : We will use a Kyverno generate policy to generate an image pull secret in a new Namespace
profile
공블로그

0개의 댓글

관련 채용 정보