K8s 인증/인가 실습
쿠버네티스에서 dev-k8s 및 infra-k8s 서비스 어카운트를 생성하여, dev-team 네임스페이스 내에서 각각 다른 권한을 부여하고 테스트해보도록 한다.
SA, ns 생성
kubectl create namespace dev-team
namespace/dev-team created
kubectl create ns infra-team
namespace/infra-team created
kubectl get ns
NAME STATUS AGE
default Active 72m
dev-team Active 6s
infra-team Active 5s
kube-node-lease Active 72m
kube-public Active 72m
kube-system Active 72m
local-path-storage Active 72m
kubectl create sa dev-k8s -n dev-team
serviceaccount/dev-k8s created
kubectl create sa infra-k8s -n infra-team
serviceaccount/infra-k8s created
서비스 어카운트와 네임스페이스를 생성한다.
pod deploy
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 -o dev-kubectl -n dev-team -o yaml |
grep service
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
serviceAccount: dev-k8s
serviceAccountName: dev-k8s
- serviceAccountToken:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
kubectl get pod -o infra-kubectl -n infra-team -o yaml | grep service
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
serviceAccount: infra-k8s
serviceAccountName: infra-k8s
- serviceAccountToken:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
권한 확인
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
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:dev-team:dev-k8s" cannot list resource "pods" in API group "" in the namespace "dev-team"
k2 get pods
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:infra-team:infra-k8s" cannot list resource "pods" in API group "" in the namespace "infra-team"
k1 auth can-i get pods
no
현재 dev-k8s 및 infra-k8s 서비스 어카운트는 Kubernetes 리소스 조회 권한이 없어서 에러가 발생한다.
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
role.rbac.authorization.k8s.io/role-dev-team created
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
role.rbac.authorization.k8s.io/role-infra-team created
kubectl get roles -n dev-team
NAME CREATED AT
role-dev-team 2025-03-15T18:56:26Z
kubectl get roles -n infra-team
NAME CREATED AT
role-infra-team 2025-03-15T18:56:28Z
특정 네임스페이스에서만 리소스에 대한 접근 권한을 정의하는 객체인 롤을 생성한다.
롤은 네임스페이스 내에서만 적용되며, 클러스터 전체에 적용하려면 ClusterRole을 사용해야 한다.
apiGroups, resources, 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
rolebinding.rbac.authorization.k8s.io/roleB-dev-team created
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
rolebinding.rbac.authorization.k8s.io/roleB-infra-team created
특정 네임스페이스에서 Role을 특정 사용자 또는 서비스 어카운트(ServiceAccount)에 연결하는 객체인 롤 바인딩을 생성한다.
Role을 단독으로 생성해도 아무 의미가 없으며 RoleBinding을 통해 서비스 어카운트와 연결해야 롤이 적용된다.
kubectl describe rolebindings roleB-dev-team -n dev-team
Name: roleB-dev-team
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: role-dev-team
Subjects:
Kind Name Namespace
---- ---- ---------
ServiceAccount dev-k8s dev-team
서비스 어카운트에 제대로 권한이 할당되었다
k1 get pods
NAME READY STATUS RESTARTS AGE
dev-kubectl 1/1 Running 0 8m31s
k2 get pods
NAME READY STATUS RESTARTS AGE
infra-kubectl 1/1 Running 0 8m24s
k1 auth can-i get pods
yes
k2 auth can-i get pods
yes
RBAC 권한 확인
kubectl krew install access-matrix rbac-tool rbac-view rolesum whoami
실습을 위한 도구를 설치한다.
- access-matrix: 사용자의 RBAC 권한을 매트릭스로 확인
- rbac-tool: RBAC 역할(Role)과 바인딩 정보를 조회
- rbac-view: 웹 UI에서 RBAC 역할을 시각적으로 분석
- rolesum: RBAC 요약 정보를 제공
- whoami: 현재 컨텍스트에서의 인증된 사용자 확인
kubectl rolesum aws-node -n kube-system
ServiceAccount: kube-system/aws-node
Secrets:
Policies:
• [CRB] */aws-node ⟶ [CR] */aws-node
Resource Name Exclude Verbs G L W C U P D DC
cninodes.vpcresources.k8s.aws [*] [-] [-] ✔ ✔ ✔ ✖ ✖ ✔ ✖ ✖
eniconfigs.crd.k8s.amazonaws.com [*] [-] [-] ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖
events.[,events.k8s.io] [*] [-] [-] ✖ ✔ ✖ ✔ ✖ ✔ ✖ ✖
namespaces [*] [-] [-] ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖
nodes [*] [-] [-] ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖
pods [*] [-] [-] ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖
policyendpoints.networking.k8s.aws [*] [-] [-] ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖
policyendpoints.networking.k8s.aws/status [*] [-] [-] ✔ ✖ ✖ ✖ ✖ ✖ ✖ ✖
aws-node SA는 AWS VPC CNI 플러그인이 네트워크 관련 리소스를 조작할 수 있도록 필요한 권한을 가지고 있다.
- cninodes.vpcresources.k8s.aws VPC CNI 관련 노드 관리
- eniconfigs.crd.k8s.amazonaws.com AWS ENI(Elastic Network Interface) 설정 관리
- nodes 클러스터 내 노드 정보 조회 가능
- pods Pod 정보 조회 가능
- policyendpoints.networking.k8s.aws 네트워크 정책 적용
kubectl rolesum -k User system:kube-proxy
User: system:kube-proxy
Policies:
• [CRB] */system:node-proxier ⟶ [CR] */system:node-proxier
Resource Name Exclude Verbs G L W C U P D DC
endpoints [*] [-] [-] ✖ ✔ ✔ ✖ ✖ ✖ ✖ ✖
endpointslices.discovery.k8s.io [*] [-] [-] ✖ ✔ ✔ ✖ ✖ ✖ ✖ ✖
events.[,events.k8s.io] [*] [-] [-] ✖ ✖ ✖ ✔ ✔ ✔ ✖ ✖
nodes [*] [-] [-] ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖
services [*] [-] [-] ✖ ✔ ✔ ✖ ✖ ✖ ✖ ✖
Kube-Proxy는 클러스터 네트워킹을 관리하기 위해 서비스 및 엔드포인트 정보를 가진다.
- endpoints 클러스터 내 서비스의 엔드포인트 정보 조회 가능
- endpointslices.discovery.k8s.io 서비스의 엔드포인트 슬라이스 정보 조회 가능
- events.[,events.k8s.io] 이벤트 관련 정보 조회 가능
- nodes 클러스터 내 노드 정보 조회 가능
- services 서비스 정보 조회 가능
kubectl rolesum -k Group system:masters
Group: system:masters
Policies:
• [CRB] */cluster-admin ⟶ [CR] */cluster-admin
Resource Name Exclude Verbs G L W C U P D DC
*.* [*] [-] [-] ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔
클러스터 관리자 그룹으로 쿠버네티스 최고 권한인 cluster-admin 역할을 부여받아 모든 리소스를 수정 가능하다.
kubectl rolesum -k Group system:nodes
Group: system:nodes
Policies:
• [CRB] */eks:node-bootstrapper ⟶ [CR] */eks:node-bootstrapper
Resource Name Exclude Verbs G L W C U P D DC
certificatesigningrequests.certificates.k8s.io/selfnodeserver [*] [-] [-] ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖
EKS(또는 Kubernetes 클러스터)에서 새로운 노드가 클러스터에 추가될 때 필요한 권한은 eks:node-bootstrapper로 부터 주어지는데,
노드(Kubelet)는 자신의 인증서를 요청(Certificate Signing Request, CSR)하고 관리자로부터 승인받아야 API 서버와 통신이 가능하게 된다.
노드는 API 서버와 안전하게 통신하기 위해 자체 인증서 요청을 생성할 수 있다.
kubectl rolesum -k Group system:authenticated
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
API 서버에 인증된 모든 사용자가 자동으로 포함되는 그룹으로 RBAC 정책을 통해 특정 리소스에 접근할 수 있다.
EKS 인증
EKS에서 액세스 제어는 AWS Identity and Access Management(IAM)과 쿠버네티스의 역할 기반 액세스 제어(RBAC)를 결합하여 이루어진다. IAM은 클러스터에 대한 인증을 처리하고 Kubernetes RBAC는 클러스터 내 리소스에 대한 권한을 관리한다.
- IAM 사용자 및 역할의 쿠버네티스 API 액세스 부여: IAM 사용자 또는 역할이 Kubernetes API에 접근하려면 액세스 항목을 사용하여 Kubernetes RBAC 권한을 해당 IAM 엔터티와 연결하여 개발자나 애플리케이션이 클러스터와 상호 작용 가능하다.
- AWS Management Console에서 Kubernetes 리소스 보기: AWS Management Console을 통해 클러스터의 네임스페이스, 노드, 파드와 같은 Kubernetes 리소스를 시각화하기 위해 콘솔이 Amazon EKS 클러스터와 통신하도록 구성해야 한다.
- kubectl을 EKS 클러스터에 연결: AWS CLI를 사용하여 kubeconfig 파일을 생성하거나 업데이트함으로써 kubectl cli 도구가 Amazon EKS 클러스터의 API 서버와 통신할 수 있도록 설정한다.
- Kubernetes 서비스 어카운트를 통한 AWS API 액세스: Kubernetes 워크로드가 AWS API를 호출할 수 있도록 하려면 서비스 어카운트와 IAM 역할을 설정하여 파드가 AWS 리소스에 안전하게 접근할 수 있다.
사용 가능한 롤 확인
kubectl describe clusterrolebindings.rbac.authorization.k8s.io cluster-admin
Name: cluster-admin
Labels: kubernetes.io/bootstrapping=rbac-defaults
Annotations: rbac.authorization.kubernetes.io/autoupdate: true
Role:
Kind: ClusterRole
Name: cluster-admin
Subjects:
Kind Name Namespace
---- ---- ---------
Group system:masters
kubectl describe clusterrole cluster-admin
Name: cluster-admin
Labels: kubernetes.io/bootstrapping=rbac-defaults
Annotations: rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
*.* [] [] [*]
[*] [] [*]
cluster-admin은 Kubernetes 클러스터에서 모든 리소스를 관리할 수 있는 최고 권한을 가진 역할이다.
system:masters 그룹에 cluster-admin 역할을 부여하는데 system:masters 그룹에 속한 사용자는 클러스터의 모든 리소스를 관리 가능하다.
cluster-admin 역할을 가진 사용자는 쿠버네티스 클러스터 내 모든 리소스와 API 엔드포인트에 대해 제한 없이 접근 가능하다.
aws auth config map
aws iam create-user --user-name testuser
aws iam create-access-key --user-name testuser
aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --user-name testuser
실습을 위한 테스트 유저를 생성하고 어드민 액세스 권한을 부여한다.
kubectl get node -v6
I0316 05:40:16.553046 2768 round_trippers.go:553] GET http://localhost:8080/api?timeout=32s in 0 milliseconds
E0316 05:40:16.553210 2768 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp 127.0.0.1:8080: connect: connection refused"
I0316 05:40:16.554763 2768 cached_discovery.go:120] skipped caching discovery info due to Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
I0316 05:40:16.555075 2768 round_trippers.go:553] GET http://localhost:8080/api?timeout=32s in 0 milliseconds
E0316 05:40:16.555135 2768 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp 127.0.0.1:8080: connect: connection refused"
I0316 05:40:16.558345 2768 cached_discovery.go:120] skipped caching discovery info due to Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
I0316 05:40:16.559675 2768 shortcut.go:103] Error loading discovery information: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
I0316 05:40:16.560892 2768 round_trippers.go:553] GET http://localhost:8080/api?timeout=32s in 0 milliseconds
E0316 05:40:16.560975 2768 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp 127.0.0.1:8080: connect: connection refused"
I0316 05:40:16.562193 2768 cached_discovery.go:120] skipped caching discovery info due to Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
I0316 05:40:16.562483 2768 round_trippers.go:553] GET http://localhost:8080/api?timeout=32s in 0 milliseconds
E0316 05:40:16.562535 2768 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp 127.0.0.1:8080: connect: connection refused"
I0316 05:40:16.563622 2768 cached_discovery.go:120] skipped caching discovery info due to Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
I0316 05:40:16.563803 2768 round_trippers.go:553] GET http://localhost:8080/api?timeout=32s in 0 milliseconds
E0316 05:40:16.563844 2768 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp 127.0.0.1:8080: connect: connection refused"
I0316 05:40:16.564946 2768 cached_discovery.go:120] skipped caching discovery info due to Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
I0316 05:40:16.565005 2768 helpers.go:264] Connection error: Get http://localhost:8080/api?timeout=32s: dial tcp 127.0.0.1:8080: connect: connection refused
The connection to the server localhost:8080 was refused - did you specify the right host or port?
AWS 자격 증명은 문제 없지만 Kubernetes RBAC 및 인증 설정을 확인해야 한다.
iam 매핑
# 1번 배스천 서버
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
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
ARN USERNAME GROUPS ACCOUNT
...
arn:aws:iam::390844768149:user/testuser testuser system:masters
# 2번 배스천 서버
aws eks update-kubeconfig --name $CLUSTER_NAME --user-alias testuser
Updated context testuser in /root/.kube/config
cat ~/.kube/config
...
- name: testuser
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- --region
- ap-northeast-2
- eks
- get-token
- --cluster-name
- myeks
- --output
- json
command: aws
kubectl ns default
Context "testuser" modified.
Active namespace is "default".
(testuser:default) [root@operator-host-2 ~]# kubectl get node -v6
I0316 06:04:22.045436 3159 loader.go:395] Config loaded from file: /root/.kube/config
I0316 06:04:23.069865 3159 round_trippers.go:553] GET https://~~~.yl4.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500 200 OK in 1015 milliseconds
NAME STATUS ROLES AGE VERSION
ip-192-168-1-31.ap-northeast-2.compute.internal Ready <none> 5h53m v1.31.5-eks-5d632ec
ip-192-168-2-143.ap-northeast-2.compute.internal Ready <none> 5h53m v1.31.5-eks-5d632ec
ip-192-168-3-145.ap-northeast-2.compute.internal Ready <none> 5h53m v1.31.5-eks-5d632ec
testuser의 그룹 권한을 변경하면?
kubectl edit cm -n kube-system aws-auth
...
- groups:
- system:authenticated
userarn: arn:aws:iam::390844768149:user/testuser
username: testuser
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
arn:aws:iam::390844768149:user/testuser testuser system:authenticated
kubectl get node -v6
I0316 06:07:56.767482 3307 loader.go:395] Config loaded from file: /root/.kube/config
I0316 06:07:57.923161 3307 round_trippers.go:553] GET https://~~.yl4.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500 403 Forbidden in 1147 milliseconds
I0316 06:07:57.923812 3307 helpers.go:246] server response object: [{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "nodes is forbidden: User \"testuser\" cannot list resource \"nodes\" in API group \"\" at the cluster scope",
"reason": "Forbidden",
"details": {
"kind": "nodes"
},
"code": 403
}]
Error from server (Forbidden): nodes is forbidden: User "testuser" cannot list resource "nodes" in API group "" at the cluster scope
testuser가 system:authenticated 그룹에 속해 있고, nodes 리소스에 대한 RBAC 권한이 없기 때문에 정상적으로 접근이 차단된다.
testuser IAM 매핑 삭제
eksctl delete iamidentitymapping --cluster $CLUSTER_NAME --arn arn:aws:iam::$ACCOUNT_ID:user/testuser
2025-03-16 06:09:54 [ℹ] removing identity "arn:aws:iam::390844768149:user/testuser" from auth ConfigMap (username = "testuser", groups = ["system:authenticated"])
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
kubectl get cm -n kube-system aws-auth -o yaml
# 2번 배스천 서버
kubectl get node -v6
I0316 06:10:54.403658 3413 loader.go:395] Config loaded from file: /root/.kube/config
I0316 06:10:56.447285 3413 round_trippers.go:553] GET https://~~~.yl4.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500 401 Unauthorized in 2035 milliseconds
I0316 06:10:56.447752 3413 helpers.go:246] server response object: [{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "Unauthorized",
"reason": "Unauthorized",
"code": 401
}]
error: You must be logged in to the server (Unauthorized)
기존에 testuser는 aws-auth ConfigMap에 등록되어 있었는데 IAM 매핑을 제거한 후, kubectl get nodes를 실행하면 401 Unauthorized 오류 발생하게 된다.