Skip to main content

rbac 인증

역할 기반 인증

  • API 서버에 접근하기 위해서는 인증작업 필요
  • Role-based access control (RBAC, 역할기반 액세스 제어)
  • 사용자의 역할에 따라 리소스에 대한 접근 권한을 가짐
  • User: 클러스터 외부에서 쿠버네티스를 조작하는 사용자 인증
  • Service Account: 파드가 쿠버네티스 API를 다룰 때 사용하는 계정
  • 인증서, 토큰, 패스워드 인증 가능

인증 절차를 거친 후 수행할 작업에 대한 권한이 있는지 확인 후 권한이 없으면 요청을 반려하고 권한이 있을 경우 해당 요청이 적절한지 확인하여 최종 요청을 승인 및 반려한다.

  1. $ kubectl get pods
  2. .kube/config에 있는 유저네임과 인증서가 일치하는지 확인
  3. 권한 확인
  4. 적절한 요청인지 확인
  5. 요청 승인 및 거부

Service Account 확인

kubectl run podtest --image=nginx
kubectl get pod podtest -o yaml

...
serviceAccount: default # 확인
...

Role & RoleBinding을 이용한 권한 제어

  • 특정 유저나 Service Account가 접근하려는 API에 접근 권한을 설정
  • 권한 있는 User 또는 Service Account만 접근하도록 허용
  • 권한 제어

Role

  • 어떤 API를 이용할 수 있는지 정의
  • 쿠버네티스의 사용권한을 정의
  • 지정된 네임스페이스에서만 유효

RoleBinding

  • 사용자/그룹 또는 Service Account와 role 연결

Role verb

  • create: 새로운 리소스 생성
  • get: 개별 리소스 조회
  • list: 여러 건의 리소스 조회
  • update: 기존 리소스 내용 전체 업데이트
  • patch: 기존 리소스 중 일부 내용 변경
  • delete: 개별 리소스 삭제
  • deletecollection: 여러 리소스 삭제
  • apiGroups: [""] -> all을 의미
  • 파드는 코어 API이기 때문에 apiGroups를 따로 지정하지 않는다.
  • 만약 리소스가 job이라면 apiGroups에 "batch"를 지정
  • resources에는 pods, deployments, services 등 사용할 API 리소스들을 명시
  • verbs에는 단어 그대로 나열된 API 리소스에 허용할 기능을 나열

ClusterRole vs Role

  • Role: 네임스페이스에서만 권한을 가지게 됨
  • ClusterRole: 네임스페이스에 상관없이 자신의 권한만큼 행사하며 클러스터 전체(전체 네임스페이스)에서 유효
  • 따라서 ClusterRole 생성시에 네임스페이스 설정이 포함되지 않는다.

ClusterRoleBinding vs RoleBinding

RoleBinding

  • 특정 네임스페이스 내의 리소스에 대해 특정 작업을 수행할 수 있는 권한을 가진 User 및 Service Account를 정의합니다.
  • RoleBinding은 네임스페이스 범위가 지정되므로 동일한 네임스페이스에 있는 역할만 참조할 수 있습니다.
  • 네임스페이스 수준의 역할을 해당 네임스페이스의 서비스 계정에 바인딩 및 클러스터 수준의 유저에 바인딩 합니다.

ClusterRoleBnding

  • ClusterRoleBinding은 클러스터 역할을 User 또는 Service Account에 바인딩합니다.
  • 클러스터의 모든 네임스페이스에 영향을 줄 수 있습니다.
  • ClusterRoleBinding은 클러스터 전체에 권한을 부여할 수 있는 반면, RoleBinding은 특정 네임스페이스로 제한됩니다.

Service Account - Role

note

애플리케이션 운영 중 특정 namespace의 Pod들을 모니터할 수 있는 서비스가 요청되었습니다. api-access 네임스페이스의 모든 파드를 view할 수 있도록 다음의 작업을 진행하세요.

  • api-access라는 새로운 namespace에 pod-viewer이라는 이름의 Service Account를 생성
  • podreader-role이라는 이름의 role과 podreader-rolebinding이라는 이름의 RoleBinding을 생성
  • 앞서 생성한 ServiceAccount를 API resource Pod에 대하여 watch, list, get을 허용하도록 매핑

api-access namespace 생성

# context 확인
kubectl config current-context

# 기존 namspace 존재 확인
kubectl get namespaces api-access

# namespace 생성
kubectl create namespace api-access

pod-viewer Service Account 생성 --namespace=api-access

# api-access 네임스페이스에 pod-viewer 서비스 계정 생성
kubectl create serviceaccount pod-viewer --namespace api-access

# 서비스 계정 생성 확인
kubectl get serviceaccounts --namespace=api-access
NAME SECRETS AGE
default 0 58s
pod-viewer 0 11s

podreader-role Role 생성

  • Pod: watch, list, get
  • namespace: api-access
# yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: api-access
name: podreader-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]

# command
# example1: role 생성
kubectl create role podreader-role --verb=get --verb=list --verb=watch --resource=pods --namespace api-access
# example2: role 생성
kubectl create role podreader-role --verb=get,list,watch --resource=pods --namespace api-access

# 역할 생성 확인
kubectl get role --namespace=api-access

# 역할 describe
kubectl describe role podreader-role --namespace=api-access
...
Name: podreader-role
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
pods [] [] [get list watch]

podreader-rolebinding RoleBinding 생성

  • role: podreader-role
  • service account: pod-viewer
# example
kubectl create rolebinding [롤바인딩명] --role=[롤명] --serviceaccount=[네임스페이스]:[serviceaccount] --namespace=[네임스페이스]

# 롤바인딩 생성
kubectl create rolebinding podreader-rolebinding --role=podreader-role --serviceaccount=api-access:pod-viewer --namespace=api-access

# 롤바인딩 생성 확인
kubectl get rolebindings --namespace=api-access

# 상세 확인
kubectl describe rolebindings podreader-rolebinding --namespace=api-access
...
Name: podreader-rolebinding
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: podreader-role
Subjects:
Kind Name Namespace
---- ---- ---------
ServiceAccount pod-viewer api-access

# validate
# https://kubernetes.io/docs/reference/kubectl/generated/kubectl_auth/kubectl_auth_can-i/

$ k get sa -n api-access
NAME SECRETS AGE
default 0 9m29s
pod-viewer 0 9m18s
$ k describe role -n api-access podreader-role
Name: podreader-role
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
pods [] [] [get watch list]


# Check to see if service account "pod-viewer" of namespace "api-access"
# can list pods in the namespace "api-access"

$ k auth can-i list pods --as=system:serviceaccount:api-access:pod-viewer -n api-access
yes
$ k auth can-i get pods --as=system:serviceaccount:api-access:pod-viewer -n api-access
yes
$ k auth can-i watch pods --as=system:serviceaccount:api-access:pod-viewer -n api-access
yes
$ k auth can-i create pods --as=system:serviceaccount:api-access:pod-viewer -n api-access
no

Service Account - ClusterRole

note

작업 Context에서 애플리케이션 배포를 위해 새로운 ClusterRole을 생성하고 특정 namespace의 Service Account를 바인드 하세요.

  • 다음의 resource type에서만 Create가 허용된 ClusterRole deployment-clusterrole을 생성합니다. Reosurce Type: Deployment, StatefulSet, DaemonSet
  • 미리 생성된 namespace api-access에 cicd-token이라는 새로운 Service Account를 생성합니다.
  • ClusterRole deployment-clusterrole을 namespace api-access로 제한된 새 Service Account cicd-token에 바인딩합니다.

deployment-clusterrole ClusterRole 생성

deployment, statefulset, daemonset

# 클러스터롤 생성
kubectl create clusterrole deployment-clusterrole --verb=create --resource=deployment,statefulset,daemonset

# 생성 확인
kubectl get clusterrole deployment-clusterrole

# 상세 확인
kubectl describe clusterrole deployment-clusterrole
...
Name: deployment-clusterrole
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
daemonsets.apps [] [] [create]
deployments.apps [] [] [create]
statefulsets.apps [] [] [create]

cicd-token Service Account 생성

  • namespace: api-access
# Service Account 생성
kubectl create serviceaccount cicd-token --namespace=api-access

# 생성 확인
kubectl get sa --namespace=api-access

deployment-clusterrolebinding ClusterRoleBinding 생성

  • clusterrole: deployment-clusterrole
  • serviceaccount: cicd-token
  • namespace: api-access
# 클러스터롤 바인딩 생성
kubectl create clusterrolebinding deployment-clusterrolebinding --clusterrole=deployment-clusterrole --serviceaccount=api-access:cicd-token

# 생성 확인
kubectl get clusterrolebindings deployment-clusterrolebinding

# 상세 확인
kubectl describe clusterrolebindings deployment-clusterrolebinding
...
Name: deployment-clusterrolebinding
Labels: <none>
Annotations: <none>
Role:
Kind: ClusterRole
Name: deployment-clusterrole
Subjects:
Kind Name Namespace
---- ---- ---------
ServiceAccount cicd-token api-access

User - ClusterRole

note

CSR(Certificate Signing Request)를 통해 app-manager 인증서를 발급 받은 유저 app-manager에게 cluster 내 모든 namespace의 deployment, pod, service 리소스를 create, list, get, update, delete 할 수 있는 권한을 할당하세요.

  • username: app-manager
  • certificate name: app-manager
  • clusterRole name: app-access
  • clusterRoleBinding name: app-access-binding

인증서 생성

# RSA PRIVATE KEY 생성
openssl genrsa -out app-manager.key 2048

# CSR 생성
openssl req -new -key app-manager.key -out app-manager.csr -subj "/CN=app-manager"

# CSR 파일 base64 인코딩
cat app-manager.csr | base64 | tr -d "\n"

# vi app-manager.yaml
# request에 붙여넣기
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: app-manager
spec:
request: [paste]
signerName: kubernetes.io/kube-apiserver-client
usages:
- client auth

kubectl apply -f app-manager.yaml
certificatesigningrequest.certificates.k8s.io/app-manager created

# Pending 상태 확인
kubectl get csr

# 요청 승인
kubectl certificate approve app-manager

# Approved 상태 확인
kubectl get csr

# 인증서 검색
kubectl get csr/app-manager -o yaml

# 인증서 내보내기
kubectl get csr app-manager -o jsonpath='{.status.certificate}'| base64 -d > app-manager.crt

app-access ClusterRole 생성

  • verb: create, list, get, update, delete
  • resource: pod, service, deployment
# 클러스터롤 생성
kubectl create clusterrole app-access --verb=create,list,get,update,delete --resource=pod,service,deployment

# 생성 확인
kubectl get clusterrole app-access

# 상세 확인
kubectl describe clusterrole app-access
...
Name: app-access
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
pods [] [] [create list get update delete]
services [] [] [create list get update delete]
deployments.apps [] [] [create list get update delete]

app-binding-manager ClusterRoleBinding 생성

# 클러스터롤 바인딩 생성
kubectl create clusterrolebinding app-access-binding --clusterrole=app-access --user=app-manager

# 생성 확인
kubectl get clusterrolebinding app-access-binding

# 상세 확인
kubectl describe clusterrolebinding app-access-binding
...
Name: app-access-binding
Labels: <none>
Annotations: <none>
Role:
Kind: ClusterRole
Name: app-access
Subjects:
Kind Name Namespace
---- ---- ---------
User app-manager

kubeconfig에 유저 등록

# 인증서, Key로 유저 등록
kubectl config set-credentials app-manager --client-key=app-manager.key --client-certificate=app-manager.crt --embed-certs=true

# 유저 등록 확인
kubectl config view

# context 추가
kubectl config set-context app-manager --cluster=kubernetes --user=app-manager

# context 확인
kubectl config view

# context switching
kubectl config use-context app-manager

# 권한 없음
kubectl get pv
kubectl get configmaps
# 권한 있음
kubectl get deployments.apps

# context switching
kubectl config use-context kubernetes-admin@kubernetes

Mixing Role

ClusterRole, Role, ClusterRoleBinding, RoleBinding을 섞어서 사용하면 어떨까요?

  • ClusterRole: 클러스터에 사용가능한 권한과 범위를 정의합니다.
  • Role: 단일 네임스페이스 내에서 사용가능한 권한과 범위를 정의합니다.
  • ClusterRoleBinding: 서비스 계정 및 사용자와 연결하고 전체 클러스터에서 적용됩니다.
  • RoleBinding: 서비스 계정 및 사용자와 연결하고 단일 네임스페이스에서 적용됩니다.
RoleBindingResult
rolerolebinding
clusterroleclusterrolebinding
clusterrolerolebinding
roleclusterrolebinding

비유를 쉽게 하기 위해서 Role과 ClusterRole이 적용되는 주체는 A라는 Service Account로 고정하겠습니다. 여기 A라는 Service Account가 있습니다.

  • 단일 네임스페이스의 Role을 Rolebinding을 통해 A에 바인딩한다면, 단일 네임스페이스 내에서만 권한을 행사할 수 있습니다.
  • 클러스터의 ClusterRole을 ClusterRoleBinding을 통해 A에 바인딩한다면, 해당 클러스터에서 권한을 행사할 수 있습니다.
  • 클러스터의 ClusterRole을 RoleBinding을 통해 A에 바인딩한다면, RoleBinding의 단일 네임스페이스 내에서만 권한을 행사할 수 있습니다.

한마디로 ClusterRole과 RoleBinding을 섞어 쓴다면, 클러스터 범위의 ClusterRole이 RoleBinding의 단일 네임스페이스로 범위가 좁혀지는 것입니다. 이와 같은 방식으로 동작한다면 Role을 ClusterRoleBinding과 섞어 쓰는 것은 동작하지 않을 것입니다. ClusterRoleBinding의 범위는 클러스터 단위인데 Role은 단일 네임스페이스 범위를 가지고 있으므로 권한을 넘는 행위이기 때문입니다. 쉽게 말해, 어떤 종류의 Role(ClusterRole)이 오던지 Binding(ClusterRoleBinding)의 범위가 우선순위를 갖는다고 생각하면 편할 것 같네요.

아래 몇가지 시나리오를 통해 위 동작을 확인해보겠습니다!

Role과 Rolebinding 생성

# 다른 시나리오의 테스트를 위해 네임스페이스 4개를 미리 생성해둡니다.
kubectl create namespace test
kubectl create namespace test2
kubectl create namespace test3
kubectl create namespace test4

# test 네임스페이스 내 myaccount 생성
$ vi sa-myaccount-test.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: myaccount
namespace: test

$ kubectl apply -f sa-myaccount-test.yaml

# test 네임스페이스 내 test-role 생성
$ vi role-test.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: test
name: test-role
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]

$ kubectl apply -f role-test.yaml

$ vi rolebinding-test.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: test-rolebinding
namespace: test
subjects:
- kind: ServiceAccount
name: myaccount
apiGroup: ""
roleRef:
kind: Role
name: test-role
apiGroup: ""

$ kubectl apply -f rolebinding-test.yaml

다른 네임스페이스의 Service Account에 rolebinding

특정 네임스페이스 내 생성하는 rolebinding은 rolebinding과 동일한 네임스페이스 내 role을 할당해야한다.

# test2 네임스페이스에 role-test2 역할 생성
$ vi role-test2.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: test2
name: role-test2
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]

$ kubectl apply -f role-test2.yaml

# test 네임스페이스의 myaccount 계정에 test2의 역할인 role-test2를 바인딩
# rolebinding 야믈 형식 중 roleRef에 namespace 옵션이 없으며
# rolebinding이 생성되는 네임스페이스 내 역할을 참조
$ vi rolebinding-test2.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: rolebinding-test2
namespace: test2
subjects:
- kind: ServiceAccount
name: myaccount
namespace: test
apiGroup: ""
roleRef:
kind: Role
name: role-test2
apiGroup: ""

# 만약 roleRef의 role을 test 네임스페이스에 있는 role-test로 수정한 후에
# 바인딩한다면 아래와 같이 role-test를 찾을 수 없다는 에러 발생
# command:
# $ k auth can-i get pods --as=system:serviceaccount:test:myaccount -n test2
# result:
# no - RBAC: role.rbac.authorization.k8s.io "role-test" not found

$ k apply -f rolebinding-test2.yaml
rolebinding.rbac.authorization.k8s.io/rolebinding-test2 created

$ k describe rolebinding -n test2 rolebinding-test2
Name: rolebinding-test2
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: role-test2
Subjects:
Kind Name Namespace
---- ---- ---------
ServiceAccount myaccount test


# test 네임스페이스 내 myaccount의 test2 네임스페이스 내 작업 권한 확인
ubuntu@k8s-master:~/playground$ k auth can-i list pods --as=system:serviceaccount:test:myaccount -n test2
yes
ubuntu@k8s-master:~/playground$ k auth can-i get pods --as=system:serviceaccount:test:myaccount -n test2
yes

ClusterRole과 RoleBinding

  • ClusterRole은 네임스페이스에 속하지도 않고 단일 네임스페이스에 대한 권한 범위를 지정하지 않음
  • 하지만 ClusterRole이 RoleBinding을 통해 Service Account에 연결되면 클러스터 역할 권한은 RoleBinding이 만들어진 네임스페이스에만 적용
# 클러스터 범위에서 파드를 생성할 수 있는 권한을 갖는 ClusterRole을 생성
$ k create clusterrole cluster-role --verb=create --resource=pods
clusterrole.rbac.authorization.k8s.io/cluster-role created

# ClusterRole을 test 네임스페이스 내 myaccount에 바인딩
$ vi rolebinding-test3.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: rolebinding-test3
namespace: test3
subjects:
- kind: ServiceAccount
name: myaccount
namespace: test
apiGroup: ""
roleRef:
kind: ClusterRole
name: cluster-role
apiGroup: ""

$ k apply -f rolebinding-test3.yaml

$ k get rolebinding -n test3
NAME ROLE AGE
rolebinding-test3 ClusterRole/cluster-role 84s


# ClusterRole을 myaccount에 RoleBinding할 경우 아래와 같이 클러스터 범위에서 적용되는 ClusterRole이 아닌
# RoleBinding의 test3 단일 네임스페이스로 범위가 좁혀진 것을 확인할 수 있습니다.
$ k auth can-i create pods --as=system:serviceaccount:test:myaccount -n test3
yes
$ k auth can-i create pods --as=system:serviceaccount:test:myaccount -n test4
no

ClusterRole과 ClusterRoleBinding

# clusterRole과 ClusterRoleBinding
$ vi cluterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: clusterrolebinding
subjects:
- kind: ServiceAccount
name: myaccount
apiGroup: ""
namespace: test
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: ""

$ kubectl apply -f cluterrolebinding.yaml


# 클러스터 범위에서 권한 확인 (모든 네임스페이스 적용 확인)
$ k auth can-i create pods --as=system:serviceaccount:test:myaccount -n test4
yes
$ k auth can-i create pods --as=system:serviceaccount:test:myaccount -n test3
yes
$ k auth can-i create pods --as=system:serviceaccount:test:myaccount -n test2
yes
$ k auth can-i create pods --as=system:serviceaccount:test:myaccount -n test1
yes