[쿠버네티스] 쿠버네티스 접근 제어

황서희·2023년 2월 23일
0
post-thumbnail

쿠버네티스 접근 제어

참조

사용자는 kubectl, 클라이언트 라이브러리 또는 REST 요청을 통해 API에 접근한다. 사용자와 쿠버네티스 서비스 어카운트 모두 API에 접근할 수 있다. 모든 쿠버네티스 클러스터의 진입점은 api-server이다.

쿠버네티스는 사용자 계정과 서비스 계정으로 인증을 요청한다.

  • 사용자 계정
    Normal Account. AWS IAM 등이 속한다. 외부 인증 시스템에 있는 사용자 정보를 연결한다.
  • 서비스 계정
    Service Account. 파드에 위치한 어플리케이션이 사용하는 계정이다.

AWS에서는 IAM 인스턴스 프로파일을 사용하면 EC2 인스턴스에 대한 AWS 자원 액세스 권한을 부여할 수 있다. 이를 통해 EC2 인스턴스 내에서 실행되는 애플리케이션은 AWS 자원에 대한 API 요청을 수행할 때 자신의 보안 자격 증명을 입력하지 않고도 인증 및 권한 부여를 받을 수 있다.

쿠버네티스 클러스터의 API 서버는 쿠버네티스의 리소스를 관리하기 위한 중심화된 컨트롤 플레인이다. API 서버를 통해 클러스터에 대한 상태 정보를 쿼리하고, 새로운 리소스를 생성하거나 기존 리소스를 업데이트하고, 클러스터의 상태를 관리할 수 있다.

파드 내에서 실행되는 어플리케이션에서 쿠버네티스 리소스를 관리하려면 kubectl과 같은 쿠버네티스 CLI 도구를 사용하여 API 서버와 통신해야 한다. 따라서, kubectl 명령어가 있는 이미지를 사용하여 파드를 생성하면, 해당 이미지를 실행하는 컨테이너 내에서 kubectl 명령어를 사용하여 API 서버와 통신하고 쿠버네티스 리소스를 관리할 수 있다.

즉, kubectl 명령어가 설치되어 있는 이미지를 사용하여 쿠버네티스의 API 서버와 통신하여 쿠버네티스의 리소스를 관리한다는 의미이다.

도커의 "bitnami/kubectl" 이미지는 쿠버네티스 클러스터를 관리하기 위한 kubectl 명령어를 실행하기 위한 도구를 제공한다. 따라서, 이 이미지를 사용하면 kubectl 명령어를 쉽게 실행할 수 있다.

bitnami/kubectl

apiVersion: v1
kind: Pod
metadata:
  name: kubectl
spec:
  container:
  - name: kubectl
    image: bitnami/kubectl:1.24.6
kubectl create -f kubectl.yaml #어플리케이션이 정상적으로 종료되고 재시작된다.
sudo docker pull bitnami/kubectl:1.24.6
sudo docker image inspect bitnami/kubectl:1.24.6 #컨테이너의 inspect 정보 확인.

CMD가 --help이고, entrypoint가 kubectl인 것을 확인할 수 있다. run해 보면, kubectl --help 화면이 나오는 것을 확인할 수 있다.

sudo docker run -it bitnami/kubectl:1.24.6 -- sh #help 화면이 나온다.

kubectl은 dettach 모드로 실행할 수 없다. 하지만 kubectl 명령어를 파드 내에서 필요할 때마다 사용하기 위해선 kubectl을 파드 내에 영구적으로 실행시켜야 한다. 다음과 같은 방법을 사용해 영구적으로 kubectl을 파드 내에 실행할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: kubectl
spec:
  #serviceAccountName: myuser1
  containers:
  - name: kubectl
    image: bitnami/kubectl:1.24.6
    command: ['sleep']
    args: ['infinity'] #프로세스가 계속 작동한다. 되는 경우가 있고 안되는 경우가 있음
kubectl create -f kubectl.yaml

계속해서 실행되는 것을 확인할 수 있다. 이와 같이, 기존에 있던 이미지의 엔트리포인트나 CMD를 확인하고 컨테이너를 어떻게 띄우고 커스터마이징 할 것인지 판단할 수 있는 실력을 기르는 것이 중요하다.

kubectl exec -it kubectl -- sh
$ kubectl version
$ kubectl get pods
$ kubectl get nodes #오류가 나온다.
$ kubectl get pods --v=4 #디버깅 모드. 접근이 가능하다

쿠버네티스 클러스터 API 서버에 접근할 수 있지만 권한이 없어 금지당한 것을 확인 가능하다.

kubectl은 별도의 설정이 없는 이상 localhost:8080으로 접근하려고 시도한다. 디버깅 모드에서는 접근이 가능한데, code 403은 기본적으로 http 통신을 한다는 뜻이고 Kubernetes API 서버와의 통신에서 403 오류가 발생한다면, 이는 해당 요청을 처리할 수 있는 권한이 없다는 것을 나타낸다.

system : serviceaccount : default : default 를 확인할 수 있다. 파드가 default라는 서비스 어카운트를 사용한다는 뜻이다. 모든 파드는 따로 설정하지 않으면 default라는 서비스 어카운트를 사용한다.

kubectl get pod kubectl -o jsonpath='{.spec.serviceAccountName}' #서비스 어카운트 확인

서비스 어카운트

참조

서비스 어카운트는 쿠버네티스의 오브젝트 중 하나로 사람이 쓰는 어카운트가 아니다. 파드의 어플리케이션이 사용하는 어카운트이다.

apiVersion: v1
kind: ServiceAccount
metadata:
	name: myuser1
    namespace: default
kubectl create sa myuser1

서비스 계정은 만들면 끝이 아니라 무엇을 할 수 있는지에 대한 권한이 필요하다. 이 때 필요한 것이 바로 RBAC 이다.

인증 (Authentication)

자격증명(Credential) 이라고도 한다. 내가 맞는지를 증명하는 것이다. 주로 X.509 인증서를 사용한다.

TLS가 설정되면 HTTP 요청이 인증 단계로 넘어간다. 클러스터 생성 스크립트 또는 클러스터 관리자는 API 서버가 하나 이상의 인증기 모듈을 실행하도록 구성한다. 인증 모듈은 클라이언트 인증서, 암호 및 일반 토큰, 부트스트랩 토큰, JWT 토큰(서비스 어카운트에 사용됨)을 포함한다. 요즘은 JWT 토큰을 많이 사용한다. AWS에서의 Access Secret Key는 일종의 토큰이라고 할 수 있다.

사람은 인증할 때 인증서를 사용하고, 서비스 계정은 토큰을 이용한다.

사용자 혹은 파드가 인증 -> RBAC를 이용한 권한 인가 -> 어드미션 제어에서 승인 제어 -> 모두 통과한 정보는 etcd에 저장됨.

인가 (Authorization)

권한 부여, 권한 관리라고도 한다.

권한을 부여 받는 것을 의미한다. 특정 사용자로부터 온 요청이 인증된 후에는 사용자가 무엇을 할 수 있는지 인가되어야 한다. 인가는 크게 ABAC와 RBAC 로 나뉜다. RBAC는 역할 기반의 접근 제어, ABAC는 속성 기반의 접근 제어이다. RBAC를 사용한다. ABAC는 수정 후 재시작을 해야 하므로 잘 사용하지 않는다.

RBAC

참조

사용자에게 역할을 부여하여 접근을 제어하는 것이다. 리소스로는 Role, ClusterRole, RoleBinding, ClusterRoleBinding이 있다.

Role

권한을 부여하는 것이다.

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: default
  name: read-pod
rules:
- apiGroups: [""] #그룹, 비워놓는 것은 코어 그룹을 뜻한다.
  resources: ["pods"] #리소스의 종류
  verbs: ["get", "list"] #명령

해당 명령어는 파드 리소스에 대해 get과 list만 가능하다는 뜻이다.

verbs의 종류는 다음과 같다. verbs는 리소스의 권한을 설정한다.

ClusterRole

네임스페이스와 상관이 없고, 클러스터 전체의 역할이다. 클러스터 전체에 영향을 미친다.
메타데이터에 네임스페이스를 지정할 수 없다.

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: read-pod
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get","list"]

사용자가 사용할 수 있는 클러스터 롤은 system을 제외한 cluster-admin, admin, edit, view가 있다. 이 중 cluster-admin은 실습할 때 주로 사용하는 역할이다. 모든 것을 다 할 수 있다. 이것을 FC(Full Control)이라 한다. 모든 것을 할 수 있는 것을 의미한다.

RoleBinding

역할(Role)과 사용자를 묶어준다. 사용자는 SA가 될 수도 있고, 사람이 될 수도 있다. 롤 바인딩과 롤을 네임스페이스에 만들면 해당 네임스페이스에서 해당되는 사용자가 권한을 부여받게 된다.

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: myuser1-read-pod
  namespace: default
subjects: #사용자
- kind: ServiceAccount
  name: myuser1
  apiGroup: "" #선택사항, 코어그룹
  namespace: default
roleRef: #역할 지정
  kind: Role
  name: read-pod
  apiGroup: rbac.authorization.k8s.io #필수

read-pod라는 역할과 myuser1이라는 사용자를 연결시켜준다.

myuser1이라는 서비스 어카운트는 default NS, Pod Resource에 Get/List를 할 수 있는 Verb를 부여받는다.

ClusterRoleBinding

클러스터 역할과 사용자를 연결시켜준다. 네임스페이스 기반의 리소스가 아니므로 클러스터 전체에 영향을 미친다.

다이나믹 프로비저닝을 하게 되면 nfs-client에게 PV 생성을 요청한다. PV 생성은 nfs-client-provisioner가 하게 되는데, 이 때 nfs-client에게 권한이 부여되어야 생성, 삭제, 수정 등을 할 수 있다. 이 때 권한을 부여하는 것이 클러스터롤과 클러스터 롤 바인딩이다.

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: myuser2-read-pod
subjects:
- kind: ServiceAccount
  name: myuser2
  namespace: default
  apiGroup: ""
roleRef:
  kind: ClusterRole
  name: read-pod
  apiGroup: "rbac.authorization.k8s.io"

read-pod 라는 역할과 myuser2이라는 사용자를 연결시켜준다.
myuser2는 클러스터 전체의 Pod 리소스에 대해 Get/List를 할 수 있는 Verb를 가진다.

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: myadmin-admin
subjects:
- kind: ServiceAccount
  name: myadmin
  namespace: default
  apiGroup: ""
roleRef:
  kind: ClusterRole
  name: admin
  apiGroup: "rbac.authorization.k8s.io"

myadmin은 클러스터 전체의 admin 권한을 가진다.

admin은 기본으로 제공되는 클러스터롤이다. admin은 quota 혹은 namespace, EndpointSlices(or Endpoint), ClusterRole, ClusterRoleBinding이 안 되는 등 몇 가지 제한이 있다.

이와 같이 SA 이름을 바꿔 권한을 확인할 수 있다.

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: myedit
  namespace: default
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: myview
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: myedit-edit
subjects:
- kind: ServiceAccount
  name: myedit
  apiGroup: ""
  namespace: default
roleRef:
  kind: ClusterRole
  name: edit
  apiGroup: "rbac.authorization.k8s.io"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: myedit-view
subjects:
- kind: ServiceAccount
  name: myview
  apiGroup: ""
  namespace: default
roleRef:
  kind: ClusterRole
  name: view
  apiGroup: "rbac.authorization.k8s.io"

kubectl.yaml 파일의 SA를 수정함으로서 해당 명령어를 통해 만들어진 권한을 확인할 수 있다.

view - edit - admin - cluster-admin 순으로 상위 권한이며, 상위 권한은 하위 권한의 명령을 모두 사용 가능하다. 예를 들어, SA를 admin으로 설정하면 cluster와 관련된 것 외에는 모두 가능하다.

metrics-server 권한 확인을 예로 들자면, pod와 node에 관련된 get, list, watch Verb가 설정된 것을 확인 가능하다. cadvisor로 사용량을 측정하여 metrics를 가져온다.

이와 같이 쿠버네티스는 파드 내부의 어플리케이션과 상호작용하는 경우가 많다. 이럴 때 사용하는 것이 바로 서비스 어카운트(SA)이다.

어드미션 제어 (Admission Control)

요청에 대한 사용자나 yaml파일에 대해 유효성 검사를 한다. 어드미션 제어 모듈은 요청을 수정하거나 거부할 수 있는 소프트웨어 모듈이다. 인가 모듈에서 사용할 수 있는 속성 외에도 어드미션 제어 모듈은 생성되거나 수정된 오브젝트 내용에 접근할 수 있다. 인증 및 인가 모듈과 달리, 어드미션 제어 모듈이 거부되면 요청은 즉시 거부된다. 지정하지 않은 값의 기본 값을 세팅하는 역할도 어드미션 제어에서 맡는다.

X.509를 통한 외부 사용자 추가

일반 유저(Normal Account)는 외부의 사용자를 의미한다. AWS로 예를 들면 IAM과 같은 인증 서비스와 연동하게 된다. 일반적으로 기업은 LDAP 서버를 이용한다. 하지만 보통 이런 서버를 구축하긴 어려우므로 이런 상황에서 사용할 수 있는 유일하게 안전한 방법은 X.509 표준 인증서를 생성하여 인증하는 것이다.

  • LDAP
    경량 디렉터리 액세스 프로토콜(영어: Lightweight Directory Access Protocol; LDAP)은 TCP/IP 위에서 디렉터리 서비스를 조회하고 수정하는 응용 프로토콜이다. LDAP는 인증을 위한 다른 서비스에 의해 자주 사용된다. FreeIPA가 대표적인 인증 서버이다. LDAP 서버 중 가장 유명한 것은 MS의 Active Directory(AD) 이다. AWS에도 AWS Directory Service라는 서비스가 있다.
cd /etc/kubernetes/pki

kubespray를 사용하여 쿠버네티스를 설치하면 쿠버네티스의 모든 컴포넌트도 쿠버네티스 API 서버의 인증을 받아야 한다. 따라서 쿠버네티스는 자체적인 PKI 구조를 가지고 있다. ca.crt와 ca.key 파일을 확인할 수 있다.

openssl x509 -in ca.crt -text -noout #ca.crt 인증서를 텍스트로 보여준다.

sha256해쉬와 RSA 방식을 사용했고, CN(발급자의 이름)은 kubernetes이다. Subject는 발급 대상으로, 발급 대상 역시 kubernetes이다. 발급자와 발급 대상이 같으므로, 이 인증서는 자체 서명 인증서이다. Root CA이므로 자체 서명 인증서일수밖에 없다.

cd ~/.kube
cat config

client-certificate-data가 사용자에게 발급된 인증서이다. client-key-data는 keyd이다.

echo client-certificate-data | base64 -d > ~/my.crt
openssl x509 -in my.crt -text -noout

파일의 CN을 확인하면 사용자의 계정 이름을 확인할 수 있다. 발급자는 kubernetes이므로 자체 서명 인증서가 아니다. 이 구성에서는 자체 서명 인증서를 만들어 테스트 할 수 없다. 따라서 CA가 서명해줘야 하는 인증서를 만들어야 한다.

  • 새로운 사용자 생성 절차
    X509 인증서 키 생성 -> csr(인증 서명 요청) 생성 -> csr을 Root CA에게 전송 -> RootCA는 키와 인증서를 가지고 있으므로 key와 crt(인증서), csr을 합쳐 인증서를 생성 -> 인증서를 가지고 인증

CSR

openssl genrsa -out myuser3.key 2048 #key 생성
openssl req -new -key myuser3.key -out myuser3.csr -subj "/CN=myuser3" #csr 생성 

csr 리소스를 만들어야 한다.

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
	name: myuser3
spec:
	request: <BASE64 ENCODED VALUE> #csr 파일의 인코딩된 내용
    signerName: kubernetes.io/kube-apiserver-client #api서버가 서명
    usages:
    - client auth #클라이언트 인증용
base64 myuser3.csr -w 0 #한줄로 만들어야 한다.

주석에서도 설명했듯, csr은 request에 base64로 인코딩하여 붙여넣어야 한다.

kubectl create -f myuser3-csr.yaml

Pending 상태에서 멈춰있는 것을 볼 수 있다. 서명할 것을 승인하지 않았기 때문이다.

kubectl certificate approve myuser3 

문제가 없다고 판단되면, 해당 명령어를 사용해 승인할 것을 명령한다.

승인한 후 다시 확인하면 발급되고 승인된 것을 확인할 수 있다.

kubectl get csr myuser3 -o jsonpath='{.status.certificate}' | base64 -d > myuser3.crt

myuser3를 디코딩하여 myuser3.crt라는 인증서로 만든다. 이제 인증서와 키로 암호 통신을 할 수 있다. 또한 인증서에 서명이 되었으므로 해당 인증서로 인증을 할 수 있다.

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: default
  name: admin-pod
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["create", "get", "list", "update", "delete"]

SA가 아닌 사용자, 외부 계정에게 바인딩한다. subjects의 kind가 User인 것을 확인할 수 있다.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: myuser3-admin-pod
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: admin-pod
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: myuser3

kubeconfig 파일을 사용하여 클러스터 접근 구성하기

참고

kubeconfig 파일들을 사용하여 클러스터, 사용자, 네임스페이스 및 인증 메커니즘에 대한 정보를 관리할 수 있다. kubectl 커맨드라인 툴은 kubeconfig 파일을 사용하여 클러스터의 선택과 클러스터의 API 서버와의 통신에 필요한 정보를 찾는다. kubeconfig는 kubectl의 설정 파일이다. 해당 파일이 없으면 클러스터를 찾을 수 없다. 클러스터에 대한 접근을 구성하는 데 사용되는 파일을 kubeconfig 파일 이라 한다. 기본적으로 kubectl은 $HOME/.kube 디렉터리에서 config라는 이름의 파일을 찾는다. KUBECONFIG 환경 변수를 설정하거나 --kubeconfig 플래그를 지정해서 다른 kubeconfig 파일을 사용할 수 있다.

kubectl 치트 시트

cd ~/.kube
ls
cache config kubens #이곳의 config를 kubeconfig라고 한다.
kubectl config view #config 파일을 보기 좋게 확인
apiVersion: v1
clusters:  
- cluster: #list이므로 여러개 설정할 수 있다. 클러스터의 실제 정보
    certificate-authority-data: DATA+OMITTED #Root CA의 인증서
    server: https://127.0.0.1:6443 #API 서버
  name: cluster.local #사용중인 클러스터의 이름
contexts:  
- context: #list이므로 여러개 설정할 수 있다. 클러스터와 사용자를 결합
    cluster: cluster.local #클러스터
    namespace: default #네임스페이스
    user: kubernetes-admin #유저
  name: kubernetes-admin@cluster.local #관습적으로 사용자이름@클러스터이름
current-context: kubernetes-admin@cluster.local
kind: Config #쿠버네티스에 내장된 리소스가 아닌 외부 리소스이다.
preferences: {}
users: #list이므로 여러개 설정할 수 있다. 사용자를 뜻한다.
- name: kubernetes-admin #계정의 이름을 설정한다. CN과 같다.
  user: #사용자 인증 방법
    client-certificate-data: REDACTED  #인증서
    client-key-data: REDACTED #키

계정의 이름은 인증서의 CN에서 기인한다. CN의 이름을 그대로 kubeconfig 파일에 써야 한다.

kubectl config get-users
kubectl config get-clusters
kubectl config get-contexts

kubens는 config의 contexts의 namespace를 바꾸는 것이다. config 파일을 수정하여 간편하게 기본 namespace를 바꿀 수 있다.

사용자를 추가하는 방법은 두 가지이다.

  1. 직접 kubeconfig를 수정
  2. 명령어를 사용

이 중 kubeconfig를 직접 수정하는 것은 여러모로 곤란하므로, 명령어를 사용하여 수정한다.

kubectl config set-credentials myuser3 --client-certificate=myuser3.crt
--client-key=myuser3.key
#credentials 생성. 이름은 인증서의 CN과 똑같이 생성해야 한다.
#위치는 crt와 key파일이 있는 곳에서 진행하는 것이 편하다.
kubectl config get-users #user가 추가된 것을 확인할 수 있다.

kubectl config set-credentials myuser3 --client-certificate=myuser3.crt 
-client-key=myuser3.key --embed-certs #파일의 내용을 내장시킨다.

kubectl config set-context myuser3@cluster.local --cluster=cluster.local
--user=myuser3 --namespace=default
#클러스터와 사용자를 연결, 이름은 관습적으로 유저명@클러스터명
kubectl config use-context myuser3@cluster.local #컨텍스트를 사용

이제 사용자는 myuser3이므로 pod에 대해서만 모든 권한을 가진다. 따라서 Pod가 아닌 다른 명령어를 실행할 수 없다.

kubectl config use-context kubernetes-admin@cluster.local
#다시 사용자를 kubernetes-admin으로 바꾼다.

CKA에서는 각 문제마다 사용하는 클러스터가 다르다. 컨텍스트 전환을 하지 않으면 점수를 얻지 못하므로 꼭 컨텍스트를 전환하는 습관을 들여야 한다.

kubectx 를 통해 현재 사용하는 컨텍스트를 쉽게 변환할 수 있다.

profile
다 아는 건 아니어도 바라는 대로

0개의 댓글