기초 이론
암호화
정의: 암호화는 평문 원본 데이터를 특정 키를 써서 알아볼 수 없는 암호문으로 바꾸는 과정임.
목적: 데이터를 보호해서 비인가된 사용자가 접근 못 하게 하고 통신 중 데이터가 노출돼도 해독 못 하게 함.
복호화
정의: 복호화는 암호문을 원래 평문으로 되돌리는 과정임.
목적: 복호화는 암호화된 데이터를 인가된 사용자만 읽을 수 있게 해줌.
암호화 방식
암호화 과정에서 필요한 키를 쌍방이 확보할 수 있는 방법에서 공유 비밀키 방식(shared secret key)과 공개키 방식(public key)이 있음.
- 공유 비밀키 암호화 방식 (shared secret key) : DES, 3DES, RC4/5
- 공개키 암호화 방식 (public key) : RSA
공유 비밀키 암호화 방식
암호화랑 복호화에 똑같은 키를 쓰는 대칭키 암호 방식임.
송신자랑 수신자가 같은 비밀키를 공유해야 하고, 이 키를 통해 데이터를 암호화하거나 복호화함.
장점: 계산 속도가 빠르고 효율적임.
단점: 비밀키를 공유하는 과정에서 키가 누출되면 보안이 뚫릴 위험이 있음.
비밀키 분배 방법에는 오프라인으로 직접 만나서 교환하거나, 디피-헬만 알고리즘 같은 공개키 기반 키 교환 방식, 중앙기관을 이용하는 방식이 있음.
공개 키 암호화 방식
공개키 방식은 암호화랑 복호화에 서로 다른 키를 쓰는 비대칭 암호 방식임.
하나는 누구나 볼 수 있는 공개키이고 다른 하나는 본인만 알고 있는 개인키임.
암호화 과정
송신자가 수신자의 공개키로 데이터를 암호화함.
암호화된 데이터는 수신자만 자신의 개인키를 써서 복호화할 수 있음.
복호화 과정
개인키로 데이터를 암호화하고 공개키로 복호화하면 데이터 제공자의 신원을 확인할 수 있음.
장점: 비밀키를 따로 공유할 필요가 없으므로 유출 가능성이 낮아 보안성이 높음.
단점: 계산이 복잡해서 대칭키 방식보다 속도가 느림.
메시지 인증
해시(Hash) 방법
해시 함수를 사용해 메시지를 고정된 길이의 해시 값으로 변환하여 무결성을 확인하는 방식임.
송신자가 메시지를 해싱한 후 그 값을 수신자에게 전달하고 수신자가 동일한 해싱 과정을 통해 결과를 비교함으로써 메시지가 변조되지 않았음을 확인할 수 있음.
장점:
계산이 빠르고 효율적임.
메시지의 무결성을 간단히 검증 가능함.
단점:
기밀성을 제공하지 않음.
해시 값만으로는 송신자의 신원을 확인할 수 없음.
MAC 방법
메시지와 비밀 키를 결합하여 생성된 코드로, 메시지의 무결성과 인증을 제공하는 방식임.
송신자는 MAC을 생성해 메시지와 함께 전송하고 수신자는 동일한 키로 MAC을 재생성하여 두 값을 비교함으로써 메시지가 변조되지 않았는지 확인함.
장점:
무결성과 인증을 동시에 제공함.
비밀 키를 사용하므로 송신자의 신원을 확인 가능함.
단점:
비밀 키를 공유해야 하므로 키 관리가 어려울 수 있음.
HMAC 방법
MAC 방식의 일종으로, 해시 함수와 비밀 키를 결합하여 MAC을 생성하는 방식임.
내부 패딩(ipad)과 외부 패딩(opad)을 사용해 보안을 강화하며 SHA-256 같은 해시 알고리즘을 활용함.
장점:
기존 해시 함수의 보안성을 그대로 활용 가능함.
내부/외부 패딩을 사용해 키 유출 위험을 줄임.
다양한 해시 알고리즘과 결합 가능함.
단점:
대칭키 기반이므로 키 관리가 필요함.
메시지 암호 방법(메시지 복호형 디지털 서명 기능)
메시지 암호 방법
공개키 암호화를 이용해 송신자가 개인 키로 메시지를 암호화하고 수신자가 공개 키로 복호화하여 메시지의 출처와 무결성을 확인하는 방식임.
이 과정은 디지털 서명의 기능도 포함하며 송신자가 메시지를 보냈음을 인증하는 기능도 함.
장점:
부인 방지가 가능함(송신자가 보냈음을 증명).
기밀성과 무결성을 동시에 제공함.
단점:
공개키 암호화는 계산이 복잡해 속도가 느림.
RSA 디지털 서명(Sign) 방식
RSA 알고리즘을 기반으로 메시지의 출처를 인증하고 무결성을 보장하는 방식임.
송신자가 개인 키로 메시지의 해시 값을 암호화하여 서명을 생성하고 수신자는 송신자의 공개 키를 사용해 서명을 복호화하여 메시지가 변조되지 않았음을 확인함.
서명 과정
- 송신자가 메시지를 해싱하여 고정된 길이의 해시 값을 생성함.
- 이 해시 값을 개인 키로 암호화하여 디지털 서명을 만듦.
- 메시지와 디지털 서명을 함께 수신자에게 전송함.
검증 과정
- 수신자가 받은 메시지를 해싱하여 동일한 해시 값을 생성함.
- 송신자의 공개 키로 디지털 서명을 복호화하여 원래의 해시 값을 복원함.
- 두 해시 값이 일치하면 메시지가 변조되지 않았음을 확인하고, 송신자의 신원을 인증함.
장점:
- 송신자의 신원을 인증할 수 있음(부인 방지).
- 메시지의 무결성을 보장함.
단점:
- RSA 알고리즘은 계산량이 많아 속도가 느림.
- 공개 키와 개인 키 관리가 필요함.
x.509 공인 인증서와 활용
X.509 공인 인증서는 국제 표준화된 공개키 기반 구조(PKI)를 따르는 디지털 인증서임.
이 인증서는 공개키와 사용자(개인, 조직, 혹은 서버)의 신원을 안전하게 연결하여 데이터 암호화, 신원 확인, 디지털 서명 검증 등 다양한 보안 기능을 제공함.
인증서는 신뢰할 수 있는 인증 기관(CA)에 의해 발급되며 이를 통해 사용자나 서버의 신뢰성을 검증함.
- SSL/TLS 암호화: 웹 브라우저와 서버 간의 안전한 통신을 보장하며 HTTPS 연결에 사용됨.
- 디지털 서명: 문서나 소프트웨어 코드에 디지털 서명을 추가하여 무결성과 출처를 보장함.
- 이메일 보안(S/MIME): 이메일 메시지를 암호화하고 발신자의 신원을 검증함.
- VPN 인증: 가상사설망(VPN)에서 사용자나 장치의 신원을 확인하는 데 사용됨.
- 사용자 인증: 온라인 뱅킹, 전자상거래 등에서 사용자의 신원을 검증함.
EC2 kind(k8s) x.509 인증서 확인
인증서 정보 확인
#
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
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
docker exec -it myk8s-control-plane cat /etc/kubernetes/pki/apiserver-kubelet-client.crt
docker exec -it myk8s-control-plane openssl x509 -in /etc/kubernetes/pki/apiserver-kubelet-client.crt -noout -text
# CSR 확인 : EKS에서도 같이 확인해보자!
kubectl get certificatesigningrequests
Kind 설치자의 kubeconfig 정보 확인
Base64 Decode and Encode - Online
Decode from Base64 format or encode into it with various advanced options. Our site has an easy to use online tool to convert your data.
www.base64decode.org
# 아래 출력되는 client-certificate-data 값을 위 사이트에 붙여넣고 DECODE : 끝에 == 빼먹지 말 것
cat $HOME/.kube/config
# base64 디코딩
echo "<MY encrypt>" | base64 -d
# 어떤 인증서인지 확인
vi myuser.crt
-----BEGIN CERTIFICATE-----
MIIDKTCCAhGgAwIBAgIIeKzXmvzBrkswDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
...
# 아래 출력되는 client-key-data 값 어떤 키인지 확인
cat $HOME/.kube/config
client-key-data: LS0tLS1CR....
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAzO6NiTZgSKiIhdwc1KIfrvRPFfstapKbo/70DPXH12hyBaaZ
...
# 아래 출력되는 certificate-authority-data 값 어떤 인증서인지 확인
cat $HOME/.kube/config
certificate-authority-data: LS0...
base64로 인코딩 돼있어서 디코딩 시 인증서 형태로 출력되는 점 확인
신규 관리자를 위한 인증서 설정
# 하위 인증서를 위한 비밀키 생성
openssl genrsa -out yandhi.key 2048
# 확인
cat yandhi.key
# 인증서 사인 요청 파일(.csr) 파일 생성
openssl req -new -key yandhi.key -out yandhi.csr -subj "/O=kubeadm:cluster-admins/CN=yandhi-cert"
# 확인
cat yandhi.csr
# 출력 값을 아래 request 에 붙여넣기
cat yandhi.csr | base64 | tr -d '\n'
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: yandhi-csr
spec:
signerName: kubernetes.io/kube-apiserver-client
groups:
- system:masters
- system:authenticated
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ2ZEQ0NBV1FDQVFBd056RWZNQjBHQTFVRUNnd1dhM1ZpWldGa2JUcGpiSFZ6ZEdWeUxXRmtiV2x1Y3pFVQpNQklHQTFVRUF3d0xlV0Z1WkdocExXTmxjblF3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUUR1S2owVlhKWWxYalZ2dEwxSjMzM1FyS0NnZFJzV1hndzczbjUzZENqTjRJSGhiN0VqZHdUKytTeHcKYTc1WUJTVnprYzRqWGtDM0M4bzhSV0VEWGpiUjNCbEhHMWd4VnQ0dUlWZU5JRTRlYkZ4TzBQYnUyeHNqQ0picQpZV3ZaenFnNm1VaWJtekgwbUJMM1RaQUhrbTNJNmN5SGhFWGxPVUQ2MWNRWENzOHp1MnBJdjk2SEtPNkw0NkFBCjI5OHBDU1o0cTczdU1EWFlnNk9VNk8vV0Y2dG1IU1NQQW9zd2RUdnJreG9SWEJSejZmL1pTNlNxSUovT3I3ZUQKWjFhNDdHZ0JVMmdqSFQ0OTAzTlVHWDIrc1pyV2hzTC9EK3VPWGp6TW9RK082WnRLYnduTkkxRkEwenVwTUY4cgpWbHpYQW52cmp2UU5nbzVGYlVqZ1ZNWVlGY2duQWdNQkFBR2dBREFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBCkZSZC9yRTlsUlREcmFUWkttU01sM3lRS2JBTzI3QVFJMXQ4M1NmTmh5VldWak1Ua1k5d1lDVExQdnVXSGZEamgKSXZRclNuMzdpbWVMQmpxQ1lIOCtMTG1ZNlJGSEJQQVRFMGlBM0FxQ0dOUFM3bUpBdHBZVStoeTRpUWgxeTJPLwo1ckI2VW9MOGpRR2tic0pLdnhIVnFKNVdad0hUWDd0N3laR1RmcE5MdCtqUytVL0Q2clVNZVpFLzl3VnhBQndtCnhKRnBkN0xPR0IvdVFyaGRWL2dVTlNsNW5RM2dHR0Ruc1ZaSnA4ckI1VEZsdDkvL2FIWHJSb3J5TktNbyt4ME8KV2ZBTjhiblZlaDA3UVkreUdaMzk1ZmptNHpnZU1QMTN6aTdLcEVNd2JLR0hiTitZNFR3Yml1RWhIUnpSSG8ydgpsNkZXWHZCUEhwbjdXYW9vT0VRaHRBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tCg==
usages:
- digital signature
- key encipherment
- client auth
EOF
# csr 확인
kubectl get csr
# 'k8s 관리자' 입장에서 해당 서명 요청을 승인하자
kubectl certificate approve yandhi-csr
# 확인 : 정상적으로 하위 인증서가 발급됨
kubectl get csr
# csr 에서 하위 인증서 추출
kubectl get csr gasida-csr -o jsonpath='{.status.certificate}' | base64 -d
kubectl get csr gasida-csr -o jsonpath='{.status.certificate}' | base64 -d > yandhi.crt
#
openssl x509 -in yandhi.crt -noout -text
# kubeconfig 에 새로운 사용자 등록
kubectl config set-credentials yandhi-user --client-certificate=yandhi.crt --client-key=yandhi.key
kubectl config set-context kind-yandhi --cluster=kind-myk8s --user=yandhi-user
cat ~/.kube/config
kubectl config use-context kind-yandhi # 혹은 kubectl ctx kind-yandhi
# ctx kind-yandhi 로 k8s 정보 확인 시도
kubectl get node
K8S(API 접근) 인증/인가
API 서버 사용 : kubectl(config, 다수 클러스터 관리 가능), 서비스 어카운트, https(x.509 Client Certs)
API 서버 접근 과정 : 인증 → 인가 → Admission Control(API 요청 검증, 필요 시 변형 - 예. ResourceQuota, LimitRange)
인증 (Authentication)
X.509 Client Certs : kubeconfig 에 CA crt(발급 기관 인증서) , Client crt(클라이언트 인증서) , Client key(클라이언트 개인키) 를 통해 인증
kubectl : 여러 클러스터(kubeconfig)를 관리 가능 - contexts 에 클러스터와 유저 및 인증서/키 참고
Service Account : 기본 서비스 어카운트(default) - 시크릿(CA crt 와 token)
인가 (Authorization)
인가 방식 : RBAC(Role, RoleBinding), ABAC, Webhook, Node Authorization
RBAC : 역할 기반의 권한 관리, 사용자와 역할을 별개로 선언 후 두가지를 조합(binding)해서 사용자에게 권한을 부여하여 kubectl or API로 관리 가능
- Namespace/Cluster - Role/ClusterRole, RoleBinding/ClusterRoleBinding, Service Account
- Role(롤) - (RoleBinding 롤 바인딩) - Service Account(서비스 어카운트) : 롤 바인딩은 롤과 서비스 어카운트를 연결
- Role(네임스페이스내 자원의 권한) vs ClusterRole(클러스터 수준의 자원의 권한)
.kube/config 파일 내용
clusters : kubectl 이 사용할 쿠버네티스 API 서버의 접속 정보 목록. 원격의 쿠버네티스 API 서버의 주소를 추가해 사용 가능
users : 쿠버네티스의 API 서버에 접속하기 위한 사용자 인증 정보 목록. (서비스 어카운트의 토큰, 혹은 인증서의 데이터 등)
contexts : cluster 항목과 users 항목에 정의된 값을 조합해 최종적으로 사용할 쿠버네티스 클러스터의 정보(컨텍스트)를 설정.
- 예를 들어 clusters 항목에 클러스터 A,B 가 정의돼 있고, users 항목에 사용자 a,b 가 정의돼 있다면 cluster A + user a 를 조합해, 'cluster A 에 user a 로 인증해 쿠버네티스를 사용한다' 라는 새로운 컨텍스트를 정의할 수 있음.
- kubectl 을 사용하려면 여러 개의 컨텍스트 중 하나를 선택.
실습 환경
- 쿠버네티스에 사용자를 위한 서비스 어카운트(Service Account, SA)를 생성 : dev-k8s, infra-k8s
- 사용자는 각기 다른 권한(Role, 인가)을 가짐 : dev-k8s(dev-team 네임스페이스 내 모든 동작) , infra-k8s(dev-team 네임스페이스 내 모든 동작)
- 각각 별도의 kubectl 파드를 생성하고, 해당 파드에 SA 를 지정하여 권한에 대한 테스트를 진행
네임스페이스와 서비스 어카운트 생성 후 확인
- 파드 기동 시 서비스 어카운트 한 개가 할당되며, 서비스 어카운트 기반 인증/인가를 함, 미지정 시 기본 서비스 어카운트가 할당
- 서비스 어카운트에 자동 생성된 시크릿에 저장된 토큰으로 쿠버네티스 API에 대한 인증 정보로 사용 할 수 있다 ← 1.23 이전 버전의 경우에만 해당
# 네임스페이스(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
서비스 어카운트를 지정하여 파드 생성 후 권한 테스트
# 각각 네임스페이스에 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
kubectl get pod -o infra-kubectl -n infra-team -o yaml
# 파드에 기본 적용되는 서비스 어카운트(토큰) 정보 확인
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 # kubectl exec -it dev-kubectl -n dev-team -- kubectl get pods 와 동일한 실행 명령이다!
k1 run nginx --image nginx:1.20-alpine
k1 get pods -n kube-system
k2 get pods # kubectl exec -it infra-kubectl -n infra-team -- kubectl 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
각각 네임스페이스에 Role을 생성 후 서비스 어카운트 바인딩
- 롤(Role) : apiGroups 와 resources 로 지정된 리소스에 대해 verbs 권한을 인가
- 실행 가능한 조작(verbs) : *(모두 처리), create(생성), delete(삭제), get(조회), list(목록조회), patch(일부업데이트), update(업데이트), watch(변경감시)
# 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
# 롤바인딩 생성 : '서비스어카운트 <-> 롤' 간 서로 연동
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
서비스 어카운트를 지정해 생성한 파드에서 다시 권한 테스트
# 각각 파드로 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
EKS 인증/인가
사용자/애플리케이션 → k8s 사용 시 ⇒ 인증은 AWS IAM, 인가는 K8S RBAC
RBAC 관련 krew 플러그인
- 롤(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
# 서버 리소스 RBAC 접근 매트릭스 표시
kubectl access-matrix -h
kubectl access-matrix # 클러스터 범위 리소스 접근 검토
kubectl access-matrix --namespace default # 'default' 네임스페이스 리소스 접근 검토
# Subject(사용자/그룹/서비스 계정) RBAC 조회
kubectl rbac-tool -h
kubectl rbac-tool lookup
kubectl rbac-tool lookup system:masters
kubectl rbac-tool lookup system:nodes # eks:node-bootstrapper 역할 조회
kubectl rbac-tool lookup system:bootstrappers # eks:node-bootstrapper 역할 조회
kubectl describe ClusterRole eks:node-bootstrapper # eks:node-bootstrapper 역할 설명
# Subject별 RBAC 정책 규칙 나열
kubectl rbac-tool policy-rules
kubectl rbac-tool policy-rules -e '^system:.*' # 'system:'으로 시작하는 규칙 나열
kubectl rbac-tool policy-rules -e '^system:authenticated' # 'system:authenticated' 규칙 나열
# 클러스터 권한으로 ClusterRole 생성
kubectl rbac-tool show
# 현재 컨텍스트 인증 Subject 확인
kubectl rbac-tool whoami
# Subject(ServiceAccount, 사용자, 그룹) RBAC 역할 요약
kubectl rolesum -h
kubectl rolesum aws-node -n kube-system # 서비스 계정 요약
kubectl rolesum -k User system:kube-proxy # 사용자 요약
kubectl rolesum -k Group system:masters # 그룹 요약
kubectl rolesum -k Group system:nodes # 그룹 요약
kubectl rolesum -k Group system:authenticated # 그룹 요약
# [운영서버1 EC2 : 터미널1] A tool to visualize your RBAC permissions
kubectl rbac-view
## 이후 해당 운영서버1 EC2 공인 IP:8800 웹 접속 : 최초 접속 후 정보 가져오는데 다시 시간 걸림 (2~3분 정도 후 화면 출력됨)
echo -e "RBAC View Web http://$(curl -s ipinfo.io/ip):8800"
약어 | Verbs | 설명 |
G | Get | 리소스 조회 가능 |
L | List | 리소스 목록 나열 가능 |
W | Watch | 리소스 감시 가능 |
C | Create | 새 리소스 생성 가능 |
U | Update | 기존 리소스 수정 가능 |
P | Patch | 리소스 일부 패치 가능 |
D | Delete | 리소스 삭제 가능 |
DC | Delete Collection | 리소스 컬렉션(여러 개) 삭제 가능 |
EKS 인증/인가 확인
인증은 AWS IAM, 인가는 K8S RBAC에서 처리함.
- kubectl 명령 → aws eks get-token → STS에 토큰 요청 ⇒ 응답값 디코드(Pre-Signed URL 이며 GetCallerIdentity)
- STS Security Token Service : AWS 리소스에 대한 액세스를 제어할 수 있는 임시 보안 자격 증명(STS)을 생성하여 신뢰받는 사용자에게 제공할 수 있음
- AWS CLI 버전 1.16.156 이상에서는 별도 aws-iam-authenticator 설치 없이 aws eks get-token으로 사용 가능
# 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
EKS 인증/인가 실습
testuser 자격증명 설정 및 확인
# testuser 사용자 생성
aws iam create-user --user-name testuser
# 사용자에게 프로그래밍 방식 액세스 권한 부여
aws iam create-access-key --user-name testuser
# testuser 사용자에 정책을 추가
aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --user-name testuser
# get-caller-identity 확인
aws sts get-caller-identity --query Arn
"arn:aws:iam::911283464785:user/admin"
kubectl whoami
# EC2 IP 확인 : myeks-bastion-EC2-2 PublicIPAdd 확인
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table
[운영서버-2] testuser 자격증명 설정 및 확인
# 아래 실습 진행을 위해, kind(k8s) 삭제
kind delete cluster --name myk8s
mv ~/.kube/config ~/.kube/config.old
# get-caller-identity 확인
aws sts get-caller-identity --query Arn
# testuser 자격증명 설정
aws configure
AWS Access Key ID [None]: AKIA5ILF2F...
AWS Secret Access Key [None]: ePpXdhA3cP....
Default region name [None]: ap-northeast-2
# get-caller-identity 확인
aws sts get-caller-identity --query Arn
"arn:aws:iam::911283464785:user/testuser"
# kubectl 시도 >> testuser도 AdministratorAccess 권한을 가지고 있는데, 실패 이유는?
kubectl get node -v6
ls ~/.kube
[운영서버-1] testuser에 system:masters 그룹 부여로 EKS 관리자 수준 권한 설정
# 방안1 : eksctl 사용 >> iamidentitymapping 실행 시 aws-auth 컨피그맵 작성해줌
# Creates a mapping from IAM role or user to Kubernetes user and groups
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
eksctl create iamidentitymapping --cluster $CLUSTER_NAME --username testuser --group system:masters --arn arn:aws:iam::$ACCOUNT_ID:user/testuser
# 확인
kubectl get cm -n kube-system aws-auth -o yaml
[운영서버-2] testuser kubeconfig 생성 및 kubectl 사용 확인
# testuser kubeconfig 생성
CLUSTER_NAME=myeks
aws eks update-kubeconfig --name $CLUSTER_NAME --user-alias testuser
# 운영서버의 config와 비교해보자
cat ~/.kube/config
# kubectl 사용 확인
kubectl ns default
kubectl get node -v6
# rbac-tool 후 확인 >> 기존 계정과 비교해보자
kubectl rbac-tool whoami
[운영서버-1] testuser IAM 맵핑 삭제
# testuser IAM 맵핑 삭제
eksctl delete iamidentitymapping --cluster $CLUSTER_NAME --arn arn:aws:iam::$ACCOUNT_ID:user/testuser
# Get IAM identity mapping(s)
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
kubectl get cm -n kube-system aws-auth -o yaml
EC2 Instance Profile(IAM Role)에 맵핑된 k8s rbac 확인 해보기
노드 mapRoles 확인
# 노드에 STS ARN 정보 확인 : Role 뒤에 인스턴스 ID
for node in $N1 $N2 $N3; do ssh ec2-user@$node aws sts get-caller-identity --query Arn; done
# aws-auth 컨피그맵 확인 >> system:nodes 와 system:bootstrappers 의 권한은 어떤게 있는지 확인
# username 확인, 인스턴스 ID? EC2PrivateDNSName?
kubectl describe configmap -n kube-system aws-auth
# Get IAM identity mapping(s)
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
awscli 파드를 추가하고, 해당 노드(EC2)의 IMDS 정보 확인 : AWS CLI v2 파드 생성
# 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 서비스 정보 확인 >> 별도 IAM 자격 증명이 없는데 어떻게 가능한 것일까요?
# > 최소권한부여 필요!!! >>> 보안이 허술한 아무 컨테이너나 탈취 시, IMDS로 해당 노드의 IAM Role 사용 가능!
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
-----------------------------------
아래부터는 파드에 bash shell 에서 실행
curl -s http://169.254.169.254/ -v
...
# Token 요청
curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" ; echo
curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" ; echo
# Token을 이용한 IMDSv2 사용
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
echo $TOKEN
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" –v http://169.254.169.254/ ; echo
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" –v http://169.254.169.254/latest/ ; echo
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" –v http://169.254.169.254/latest/meta-data/iam/security-credentials/ ; echo
# 위에서 출력된 IAM Role을 아래 입력 후 확인
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" –v http://169.254.169.254/latest/meta-data/iam/security-credentials/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-1DC6Y2GRDAJHK
{
"Code" : "Success",
"LastUpdated" : "2023-05-27T05:08:07Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIA5ILF2FJI5VPIYJHP",
"SecretAccessKey" : "rke6HXkaG4B/YLGUyAYtDnd3eMrjXlCpiB4h+9XJ",
"Token" : "IQoJb3JpZ2luX2VjEAUaDmFwLW5vcnRoZWFzdC0yIkcwRQIhAOLQr2c2k4UwqTR2Iof2wq9Faduno1a2FX07ASHsO/rCAiAvCqQRv/JrSDZNZKNMloaBTR4s91O+RNWfSlfNluimmirLBQg+EAEaDDkxMTI4MzQ2NDc4NSIM1/RJmwkWziNhz8TEKqgFZZr1FpwHmLWNzdCdbNxtPk/YhbqbED6JzFiWJssqdRq4UniamoGrkV75oaf7o0CXQTlgK7r6f07goYA268UlqGx9XHKmeSoUt3ZTG79B1BIiSW22JVFzs4/fMpcwQLFv1lJKcGOqKehXrlq4yQ2zln4QTi/S30rp2ARiUfjgdp2+gkWKzOVWkdgoKtn3OAfdI/hBJHiz1eDPZsqqzv8eyP6sdo1xHJ6OY7xjLtTHWRaQpt6SStKTzsN88sJi9NebBXV63FJ6EkGNaC7eFo/nq9xZtGJqsu3PEuseadl8a8LJzOfNO0NP+4p8o0fMV4oeKSItZUIu88CvinvGd3bp1FWlVItDsGwjo6qOTxCg2ov6p7cAbTudEA5AwSjDlHm/BX08JN4XN7kDKtBQhHoWRbeI3suqZmtLPrSu5NCfgVu2jJpMiwOEhVV9W+fBUica345sIp94qIVVwrVbDnuLC0QDSXKxD+GRhcqdtA54QmUodqxv/bEUlRy1wVUty7Umucxl3B6MYBVSXR7PRzcf2U3vvqbJDJAT5dhFTRI1gK1YcXLzpT1T3wluMsyMPFpEWYMe/QEDAn0UwJ55pZt2pKohioiLJ3amWfNUhzoDkmXXZhAOM71e8gUVdrtAVcnl30MTDjHlIWIOBWrVMshunM5Wfmr4H4BAV+8of6xGz5AhoodWNVE+/x+XifO6h9l+Plwq24Jp8SbiCF3ZFQVe20ijsfDqK6SFAveL4vcVz7sEGLTZXLNLycgeGQmcvkb7Mmmoir/9UwNCWFWBbWXZfsEbNfSLhInw+k53FLb5I+axJPhEDSE5Iqmu+cuvoZfLy+bOailVgQN/jX6vZSL3ihhJwsP7t58urN34tKP+sjOpIBWv2bV2OnntaAqbc24tmc0wjWkaw5IwqKDGowY6sQGFB40kmsXmxihug/yKwcMK/pg5xFknPFO56P6BzErLmt1hcpNF4QBQzh/sdFi7Y/EOh9NqU/XFdFeJLp6KgaxUASLSW/k6ee+RzhbW0aSJb9GYi7tZdArcjg4YaQ6hdXdCFXiYWbNyIMs2MH8APT5jFDnwpbqSnlO2Ao64XY12cm2tMWVH+KTUyLGICHP1az7kD3/tV9glw9rJB2AOL4iA3TTuK+U2o+pHWEHRQOVh3p4=",
"Expiration" : "2023-05-27T11:09:07Z"
}
## 출력된 정보는 AWS API를 사용할 수 있는 어느곳에서든지 Expiration 되기전까지 사용 가능
# 파드에서 나오기
exit
awscli 파드에 kubeconfig (mapRoles) 정보 생성 및 확인
# node 의 IAM Role ARN을 변수로 지정
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
NODE_ROLE=<각자 자신의 노드 Role 이름>
NODE_ROLE=eksctl-myeks-nodegroup-ng1-NodeInstanceRole-1DC6Y2GRDAJHK
# 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
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 : 테라폼
...
위와 같이 설정할 경우 Node 전체에 권한이 들어가게 됨(Instance Profile)
IRSA보다는 Pod Profile을 권장
필요 지식
Service Account Token Volume Projection : '서비스 계정 토큰'의 시크릿 기반 볼륨 대신 'projected volume' 사용
파드가 특정 IAM 역할로 Assume 할때 토큰을 AWS에 전송하고, AWS는 토큰과 EKS IdP를 통해 해당 IAM 역할을 사용할 수 있는지 검증
- 기본 서비스 계정 토큰으로는 사용하기에 부족함이 있음. 토큰을 사용하는 대상(audience), 유효 기간(expiration) 등 토큰의 속성을 지정할 필요가 있기 때문.
- Service Account Token Volume Projection 기능을 사용하면 이러한 부족한 점들을 해결할 수 있음.
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:
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
k8s api 접근 단계
- AuthN → AuthZ → Admisstion Control 권한이 있는 사용자에 한해서 관리자(Admin)가 특정 행동을 제한(validate) 혹은 변경(mutate)
- AuthN & AuthZ - MutatingWebhook - Object schema validation - ValidatingWebhook → etcd
- Admission Control도 Webhook으로 사용자에게 API가 열려있고, 사용자는 자신만의 Admission Controller를 구현할 수 있으며, 이를 Dynamic Admission Controller라고 부르고, 크게 MutatingWebhook 과 ValidatingWebhook 로 나뉨.
- MutatingWebhook은 사용자가 요청한 request에 대해서 관리자가 임의로 값을 변경하는 작업임.
- ValidatingWebhook은 사용자가 요청한 request에 대해서 관리자기 허용을 막는 작업임.
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 역할을 사용할 수 있는지 검증
실습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
실습2
# 파드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
my-sa 이름의 Sub account 부착
# 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>
# 파드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:
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::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-GE2DZKJYWCEN
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-69rh8 (ro)
...
Volumes:
aws-iam-token:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 86400
kube-api-access-sn467:
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::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
- IRSA를 가장 취약하게 사용하는 방법 : 정보 탈취 시 키/토큰 발급 약용 가능
- AWS는 JWT 토큰의 유효성만 확인 하지만 토큰 파일과 서비스 계정에 지정된 실제 역할 간의 일관성을 보장하지는 않음 → Condition 잘못 설정 시, 토큰과 역할 ARN만 있다면 동일 토큰으로 다른 역할을 맡을 수 있음
Pod Identity
기존에는 외부 OIDC를 사용했으나 AWS 내부에서 전부 처리하게 됨
Pod Identity 사용하려면 클러스터에 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"
]
}
]
}
Kyverno
Cloud Native Computing Foundation (CNCF)에서 관리하는 Kubernetes 네이티브 정책 엔진
주요 특징
- YAML 기반 선언적 Kubernetes 리소스 사용
- 새로운 정책 언어 학습 불필요
- 리소스 구성 검증, 변경, 생성 가능
- 이미지 서명 및 증명 검증 지원
기능
- Kubernetes 리소스로서의 정책 관리
- 모든 리소스 검증, 변경, 생성, 제거
- 컨테이너 이미지 및 메타데이터 검증
- 레이블 선택기 및 와일드카드 사용 리소스 매칭
- 네임스페이스 간 구성 동기화
- 비준수 리소스 차단 및 정책 위반 보고
- 셀프 서비스 보고서 및 정책 예외 지원
- CI/CD 파이프라인에서 정책 테스트 가능
Kyverno 작동 방식
- Dynamic Admission Controller로 작동
- Kubernetes API 서버와 통합
- 주요 구성 요소: Webhook 서버, Webhook 컨트롤러
- AdmissionReview 요청 처리 및 정책 적용
# 설치
# 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
Policy and Role : Kyverno Policy는 rules 모음
각 규칙은 match선언, 선택적 exclude선언 및 validate, mutate, generate또는 verifyImages선언 중 하나로 구성됨.
각 규칙에는 단일 validate, mutate, generate또는 verifyImages하위 선언만 포함될 수 있음.
Validation : 파드에 라벨(lables) 검증
# 모니터링
watch -d kubectl get pod -n kyverno
# ClusterPolicy 적용
kubectl apply -f- << EOF
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-labels
spec:
validationFailureAction: Enforce
rules:
- name: check-team
match:
any:
- resources:
kinds:
- Pod
validate:
message: "label 'team' is required"
pattern:
metadata:
labels:
team: "?*"
EOF
# 확인
kubectl get validatingwebhookconfigurations
kubectl get ClusterPolicy
NAME ADMISSION BACKGROUND VALIDATE ACTION READY AGE MESSAGE
require-labels true true Enforce True 12s Ready
# 디플로이먼트 생성 시도
kubectl create deployment nginx --image=nginx
error: failed to create deployment: admission webhook "validate.kyverno.svc-fail" denied the request:
resource Deployment/default/nginx was blocked due to the following policies
require-labels:
autogen-check-team: 'validation error: label ''team'' is required. rule autogen-check-team
failed at path /spec/template/metadata/labels/team/'
# 디플로이먼트 생성 시도
kubectl run nginx --image nginx --labels team=backend
kubectl get pod -l team=backend
# 확인
kubectl get policyreport -o wide
NAME KIND NAME PASS FAIL WARN ERROR SKIP AGE
e1073f10-84ef-4999-9651-9983c49ea76a Pod nginx 1 0 0 0 0 29s
kubectl get policyreport e1073f10-84ef-4999-9651-9983c49ea76a -o yaml
apiVersion: wgpolicyk8s.io/v1alpha2
kind: PolicyReport
metadata:
labels:
app.kubernetes.io/managed-by: kyverno
name: e1073f10-84ef-4999-9651-9983c49ea76a
namespace: default
results:
- message: validation rule 'check-team' passed.
policy: require-labels
result: pass
rule: check-team
scored: true
source: kyverno
timestamp:
nanos: 0
seconds: 1712473900
scope:
apiVersion: v1
kind: Pod
name: nginx
namespace: default
uid: e1073f10-84ef-4999-9651-9983c49ea76a
summary:
error: 0
fail: 0
pass: 1
skip: 0
warn: 0
# 정책 삭제
kubectl delete clusterpolicy require-labels
Mutation : 파드에 라벨(lables) 추가
#
kubectl apply -f- << EOF
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-labels
spec:
rules:
- name: add-team
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
metadata:
labels:
+(team): bravo
EOF
# 확인
kubectl get mutatingwebhookconfigurations
kubectl get ClusterPolicy
NAME ADMISSION BACKGROUND VALIDATE ACTION READY AGE MESSAGE
add-labels true true Audit True 6m41s Ready
# 파드 생성 후 label 확인
kubectl run redis --image redis
kubectl get pod redis --show-labels
# 파드 생성 후 label 확인 : 바로 위와 차이점은?
kubectl run newredis --image redis -l team=alpha
kubectl get pod newredis --show-labels
# 삭제
kubectl delete clusterpolicy add-labels
Kyverno Cli
# Install Kyverno CLI using kubectl krew plugin manager
kubectl krew install kyverno
# test the Kyverno CLI
kubectl kyverno version
kubectl kyverno --help
# 정책 테스트 : --policy-report 옵션은 로컬에서도 리포트 출력을 할 수 있는 기능
kubectl kyverno apply require-probes.yaml --resource nginx.yaml --policy-report
...
'k8s > AWS EKS' 카테고리의 다른 글
[AWS] EKS Upgrade (0) | 2025.04.02 |
---|---|
[AWS] EKS Mode/Nodes (0) | 2025.03.22 |
[AWS] EKS AutoScaling (0) | 2025.03.08 |
[AWS] EKS Observability (0) | 2025.03.01 |
[AWS] EKS Storage (0) | 2025.02.23 |