Kubernetes/Architecture

Kubernetes와 TLS

Operation CWAL 2021. 4. 15. 22:38

Kubernetes Components

아래 그림과 같이 Kubernetes에 존재하는 모든 Component 간 통신은 HTTPS를 기반으로 이루어지며, 모든 트래픽이 암호화되므로 데이터의 신뢰성과 보안을 보장할 수 있다.

 

일반적으로 K8s Component 사이에서 이루어지는 작업은 다음과 같이 요약할 수 있다.

  • kube-apiserver -> etcd: kube-apiserver는 etcd에 접근할 수 있는 유일한 Component로, K8s 클러스터의 상태를 Key-Value 형식으로 etcd 저장소에 관리한다.
  • kubelet -> kube-apiserver: Worker에 배치된 kubelet은 자신이 생성해야할 Pod 정보를 kube-apiserver로부터 가져온다.
  • kube-apiserver -> kubelet: kubelet은 Server 역할을 수행하며, 자신이 위치한 Worker 및 Pod의 상태를 kube-apiserver에 알려줄 수 있다.
  • kubectl -> kube-apiserver: 사용자가 입력한 Command 및 Manifest 파일을 JSON 형식으로 가공하여, kube-apiserver에 전달한다.
  • kube-scheduler -> kube-apiserver: kube-apiserver로부터 Node가 배정되지 않은 Pod(unscheduled) 정보를 가져와서 적절한 Node를 배정한 뒤, kube-apiserver에 결과를 알려준다.
  • kube-controller-manager -> kube-apiserver: K8s 클러스터에 존재하는 Object의 Desired State와 Actual State를 kube-apiserver로부터 가져와, Sync 작업을 수행한다.
  • kube-proxy -> kube-apiserver: K8s Service와 Endpoint 정보를 가져와서 자신이 위치한 Node의 Netfilter Rule(iptables, ipvs)를 업데이트한다.

Kubernetes PKI

다들 알다시피, HTTPS 프로토콜을 사용하기 위해선 Server와 Client 양측 모두 SSL/TLS 인증서(Certificate)가 필요하며, 모든 인증서는 신뢰할 수 있는 Root CA(Certifiacte Authority)에 의해 서명되어야 한다.

Kubernetes 역시 마찬가지로 각각의 Component마다 Certificate을 요구하며, 아래와 같이 독자적인 PKI(Public Key Infrastructure)를 구성한다.

 

Kubernetes PKI

CA는 'KUBERNETES-CA'라는 Common Name을 갖고 있으며, etcd를 제외한 모든 Component의 Certificate 서명에 사용된다.  Master Node의 '/etc/kubernetes/pki' 디렉토리를 확인하면 위 그림에서 표현한 대부분의 파일이 존재하는 것을 확인할 수 있다.

다만, 일부 Component의 인증서는 파일 형태로 존재하는 대신, 각각의 Config 파일 안에 인코딩되는 방식으로 위치한다. 예를 들어 kube-scheduler의 경우, /etc/kubernetes/scheduler.conf 파일을 통해 설정되는데, 이 파일을 확인하면 다음과 같이 Certificate 정보가 base64로 인코딩된 값으로 존재하는 것을 알 수 있다.

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: <base64-encoded-value>
    server: <kube-apiserver URL>
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: system:kube-scheduler
  name: system:kube-scheduler@kubernetes
current-context: system:kube-scheduler@kubernetes
kind: Config
preferences: {}
users:
- name: system:kube-scheduler
  user:
    client-certificate-data: <base64-encoded-value>
    client-key-data: <base64-encoded-value>

 

kube-apiserver는 Client Certificate 내용 중 Common Name을 통해 Client를 구분할 수 있으며, 특히 kubelet은 자신이 위치한 Node의 hostname으로 세분화된다. 다시 말해서 각각의 kubelet은 자신만의 유니크한 인증서를 갖는다는 의미이다. 이를 통해 kubelet은 자신이 속한 Node에 배정된 Pod 이외의 정보를 읽거나 쓸 수 없도록 제한된다.

Component Common Name Organizations
Controller Manager system:kube-controller-manager  
Scheduler system:kube-scheduler  
kube-proxy system:kube-proxy  
kubelet system:node:${hostname} system:nodes

 

인증서 발급 API

매번 K8s 관리자가 Client의 인증서를 수동으로 발급할 수는 없으므로, API 서버에 해당 작업을 위임할 수 있으며 아래와 같은 순서로 이루어진다.

 

1. Client 측에서 Private Key로부터 CSR(Certificate Signing Request)를 생성하고, 이를 API 서버에 전달한다.

2. 해당 요청은 K8s 상에서 CertificateSigningRequest라는 별도의 리소스(로 취급되며, 관리자의 승인이 있을 때까지 Pending 상태로 남는다.

3. 관리자 승인시, Client 인증서를 발급하며 해당 Certificate으로 K8s 접근이 가능하다.

 

자세한 사용 예시는 Kubernetes 공식 문서를 참고하길 바란다.

 

클러스터에서 TLS 인증서 관리

쿠버네티스는 사용자가 제어하는 ​​인증 기관 (CA)에서 서명한 TLS 인증서를 프로비저닝 할 수 있는 certificates.k8s.io API를 제공한다. 이러한 CA 및 인증서는 워크로드 간의 신뢰 관계를 구성하는

kubernetes.io

 

사용자 생성

K8s에서 프로그래밍 방식으로 접근하는 ServiceAccount와는 달리 사용자(User)라는 개념이 명시적으로 존재하지 않으며, 클러스터 외부의 독립적인 서비스로부터 사용자를 제공받는 것이 일반적이다. 다른 Component와 마찬가지로 kube-apiserver 접근시 Authentication은 Client Ceritificate의 'Common Name(CN)' 필드값을 통해 이루어진다. 다만 클러스터 CA가 서명한 인증서만으로는 kube-apiserver 접근만 가능할 뿐, 어떠한 리소스에도 사용권한이 없기 때문에 RBAC을 통한 Authorization이 필요하다.

 

다음은 On-premise 환경의 Kubernetes에서 새로운 사용자 'dev1'을 추가하는 예시이다.

 

1. SSL Key 파일 및 CSR 파일 생성

openssl genrsa -out dev1.key 2048
openssl req -new -key dev1.key -out dev1.csr

CSR 파일 생성시, Common Name에 새로 추가할 사용자의 이름을 입력하며 그 외 항목은 생략한다. 이 과정에서 dev1.key, dev1.csr 두 개의 파일이 생성된다.

 

2. K8s CertificateSigningRequest 리소스 생성

우선 다음 명령어로 CSR 파일의 내용을 base64로 인코딩할 필요가 있다. 

cat dev1.csr | base64 -w 0

아래와 같은 CSR Manifest 파일을 작성하되, request 필드에 위에서 얻은 인코딩 텍스트를 입력한다.

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: dev1
spec:
  groups:
  - system:authenticated
  request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ21UQ0NBWUVDQVFBd1ZERUxNQWtHQTFVRUJoTUNRVlV4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeApJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpERU5NQXNHQTFVRUF3d0VaR1YyCk1UQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUpiVGNJd2JOcStmRnNsZGg2NnMKbWpoMFZLTldiaElkTjFWV0hrQkV6Vm84M0hhNXZIUXM2UUtwazJMUlBmaERxbHMrU3AySVRkemxHcHFiUmNhWQpXeHRsa1NNOFcrZlBUc2VUZDFTTXZMU1hGSjFpUVc0ZzcwUDVobmsrL0NTUWlPUTJmQzU5MnBhV1lWRWZMUE5PCld0aXdxTC9iczNhWjl0VktXZUkrSXB0eExCQTdhRTlKUzVTZ1BndlEzcHZVOFIzNnltbUdOVE1JeEI3ZVVMMUQKN2tvMFk0Y0RlTzQrK3YrRm5xYU04dTBOTXJONkhSTkZ3V05abStkK1l4c1lGTGk2REFwbW9ZVyt2Z2NGS0oybgpETEtuNmJSVE44U3JoTkF5a0xQRnNwN0lCT3A5RnJkaDltU21RQWdmcEVUWVZmdzJscjJBcmVuV0w5SEEySTJ1Cmw0RUNBd0VBQWFBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQk5yMkRPVXlTUE9jb1Zsa0dsM0tCaVJKeEgKanhkSEVwaFVFY3VpWllEZmJybldoQmZ1UEV5bXpVMDRHTmZmcHBCbEtUTDdOYWJZOURGNmpHQ3dqcDJ0L0xyMApsM1FhaGVrRWQyZ2FVcjhxMWcwZkdHTGlqc0RuMnI4TUxaSDVVaS9kZ2Q2NXRlb1d5YW10ZkxWQ2dEUjJSaVN0CitTeG0yVXh2R3lqVVUvekEya2xSZWNKWVV0aXNBRVY3VHpYUDFVZDloOWlWMFNuSHNhUVpxN04rdWh6NjRObTUKQmRyRlVsOGsvdEtSNGJzN2JRMDgxQXcrM3NIQndOZFpleHRncnRXSVVhNTJUUHF4T3MrYmwvbGFpRm12LzNpUQo2cXZ4RzROL3dwalhtOXVPYlp3YXBhM08rMWY4cnY1dHJWKzAxZ2t2bk1qVnhxS2JoWXNweHJ3VFVkNmwKLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVU
  signerName: kubernetes.io/kube-apiserver-client
  usages:
  - client auth

아래 명령어로 K8s CSR 리소스를 생성하여, 인증서 발급을 요청한다.

kubectl apply -f csr.yaml

 

 

3. CSR 확인 및 Approve

아래 명령어로 현재 존재하는 K8s CSR 리소스를 확인할 수 있으며, 현재 dev1이라는 이름의 CSR이 승인 대기중인 것으로 나온다.

kubectl get csr

다음 명령어로 해당 CSR을 승인하여, K8s CA가 서명한 인증서를 얻을 수 있다.

kubectl certificate approve dev1

 

4. Client Certificate 파일 생성

승인 완료시, 해당 CSR 리소스에 CA가 서명한 인증서 데이터가 추가되며, 다음 명령어로 이를 확인할 수 있다. 

kubectl get csr dev1 -o yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"certificates.k8s.io/v1","kind":"CertificateSigningRequest","metadata":{"annotations":{},"name":"dev1"},"spec":{"groups":["system:authenticated"],"request":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ21UQ0NBWUVDQVFBd1ZERUxNQWtHQTFVRUJoTUNRVlV4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeApJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpERU5NQXNHQTFVRUF3d0VaR1YyCk1UQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUpiVGNJd2JOcStmRnNsZGg2NnMKbWpoMFZLTldiaElkTjFWV0hrQkV6Vm84M0hhNXZIUXM2UUtwazJMUlBmaERxbHMrU3AySVRkemxHcHFiUmNhWQpXeHRsa1NNOFcrZlBUc2VUZDFTTXZMU1hGSjFpUVc0ZzcwUDVobmsrL0NTUWlPUTJmQzU5MnBhV1lWRWZMUE5PCld0aXdxTC9iczNhWjl0VktXZUkrSXB0eExCQTdhRTlKUzVTZ1BndlEzcHZVOFIzNnltbUdOVE1JeEI3ZVVMMUQKN2tvMFk0Y0RlTzQrK3YrRm5xYU04dTBOTXJONkhSTkZ3V05abStkK1l4c1lGTGk2REFwbW9ZVyt2Z2NGS0oybgpETEtuNmJSVE44U3JoTkF5a0xQRnNwN0lCT3A5RnJkaDltU21RQWdmcEVUWVZmdzJscjJBcmVuV0w5SEEySTJ1Cmw0RUNBd0VBQWFBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQk5yMkRPVXlTUE9jb1Zsa0dsM0tCaVJKeEgKanhkSEVwaFVFY3VpWllEZmJybldoQmZ1UEV5bXpVMDRHTmZmcHBCbEtUTDdOYWJZOURGNmpHQ3dqcDJ0L0xyMApsM1FhaGVrRWQyZ2FVcjhxMWcwZkdHTGlqc0RuMnI4TUxaSDVVaS9kZ2Q2NXRlb1d5YW10ZkxWQ2dEUjJSaVN0CitTeG0yVXh2R3lqVVUvekEya2xSZWNKWVV0aXNBRVY3VHpYUDFVZDloOWlWMFNuSHNhUVpxN04rdWh6NjRObTUKQmRyRlVsOGsvdEtSNGJzN2JRMDgxQXcrM3NIQndOZFpleHRncnRXSVVhNTJUUHF4T3MrYmwvbGFpRm12LzNpUQo2cXZ4RzROL3dwalhtOXVPYlp3YXBhM08rMWY4cnY1dHJWKzAxZ2t2bk1qVnhxS2JoWXNweHJ3VFVkNmwKLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tCg==","signerName":"kubernetes.io/kube-apiserver-client","usages":["client auth"]}}
  creationTimestamp: "2021-04-15T13:13:53Z"
  managedFields:
  - apiVersion: certificates.k8s.io/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          .: {}
          f:kubectl.kubernetes.io/last-applied-configuration: {}
      f:spec:
        f:groups: {}
        f:request: {}
        f:signerName: {}
        f:usages: {}
    manager: kubectl-client-side-apply
    operation: Update
    time: "2021-04-15T13:13:53Z"
  - apiVersion: certificates.k8s.io/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:status:
        f:certificate: {}
    manager: kube-controller-manager
    operation: Update
    time: "2021-04-15T13:17:18Z"
  - apiVersion: certificates.k8s.io/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:status:
        f:conditions:
          .: {}
          k:{"type":"Approved"}:
            .: {}
            f:lastTransitionTime: {}
            f:lastUpdateTime: {}
            f:message: {}
            f:reason: {}
            f:status: {}
            f:type: {}
    manager: kubectl
    operation: Update
    time: "2021-04-15T13:17:18Z"
  name: dev1
  resourceVersion: "861571"
  uid: 725f36ff-f730-4aaa-9d02-7b0575b45045
spec:
  groups:
  - system:masters
  - system:authenticated
  request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ21UQ0NBWUVDQVFBd1ZERUxNQWtHQTFVRUJoTUNRVlV4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeApJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpERU5NQXNHQTFVRUF3d0VaR1YyCk1UQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUpiVGNJd2JOcStmRnNsZGg2NnMKbWpoMFZLTldiaElkTjFWV0hrQkV6Vm84M0hhNXZIUXM2UUtwazJMUlBmaERxbHMrU3AySVRkemxHcHFiUmNhWQpXeHRsa1NNOFcrZlBUc2VUZDFTTXZMU1hGSjFpUVc0ZzcwUDVobmsrL0NTUWlPUTJmQzU5MnBhV1lWRWZMUE5PCld0aXdxTC9iczNhWjl0VktXZUkrSXB0eExCQTdhRTlKUzVTZ1BndlEzcHZVOFIzNnltbUdOVE1JeEI3ZVVMMUQKN2tvMFk0Y0RlTzQrK3YrRm5xYU04dTBOTXJONkhSTkZ3V05abStkK1l4c1lGTGk2REFwbW9ZVyt2Z2NGS0oybgpETEtuNmJSVE44U3JoTkF5a0xQRnNwN0lCT3A5RnJkaDltU21RQWdmcEVUWVZmdzJscjJBcmVuV0w5SEEySTJ1Cmw0RUNBd0VBQWFBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQk5yMkRPVXlTUE9jb1Zsa0dsM0tCaVJKeEgKanhkSEVwaFVFY3VpWllEZmJybldoQmZ1UEV5bXpVMDRHTmZmcHBCbEtUTDdOYWJZOURGNmpHQ3dqcDJ0L0xyMApsM1FhaGVrRWQyZ2FVcjhxMWcwZkdHTGlqc0RuMnI4TUxaSDVVaS9kZ2Q2NXRlb1d5YW10ZkxWQ2dEUjJSaVN0CitTeG0yVXh2R3lqVVUvekEya2xSZWNKWVV0aXNBRVY3VHpYUDFVZDloOWlWMFNuSHNhUVpxN04rdWh6NjRObTUKQmRyRlVsOGsvdEtSNGJzN2JRMDgxQXcrM3NIQndOZFpleHRncnRXSVVhNTJUUHF4T3MrYmwvbGFpRm12LzNpUQo2cXZ4RzROL3dwalhtOXVPYlp3YXBhM08rMWY4cnY1dHJWKzAxZ2t2bk1qVnhxS2JoWXNweHJ3VFVkNmwKLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tCg==
  signerName: kubernetes.io/kube-apiserver-client
  usages:
  - client auth
  username: kubernetes-admin
status:
  certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURPakNDQWlLZ0F3SUJBZ0lSQUsycytPc1Q1WjVvTnoxcVgwZDhEQll3RFFZSktvWklodmNOQVFFTEJRQXcKRlRFVE1CRUdBMVVFQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TVRBME1UVXhNekV5TVRoYUZ3MHlNakEwTVRVeApNekV5TVRoYU1GUXhDekFKQmdOVkJBWVRBa0ZWTVJNd0VRWURWUVFJRXdwVGIyMWxMVk4wWVhSbE1TRXdId1lEClZRUUtFeGhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXhEVEFMQmdOVkJBTVRCR1JsZGpFd2dnRWkKTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDVzAzQ01HemF2bnhiSlhZZXVySm80ZEZTagpWbTRTSFRkVlZoNUFSTTFhUE54MnVieDBMT2tDcVpOaTBUMzRRNnBiUGtxZGlFM2M1UnFhbTBYR21Gc2JaWkVqClBGdm56MDdIazNkVWpMeTBseFNkWWtGdUlPOUQrWVo1UHZ3a2tJamtObnd1ZmRxV2xtRlJIeXp6VGxyWXNLaS8KMjdOMm1mYlZTbG5pUGlLYmNTd1FPMmhQU1V1VW9ENEwwTjZiMVBFZCtzcHBoalV6Q01RZTNsQzlRKzVLTkdPSApBM2p1UHZyL2haNm1qUEx0RFRLemVoMFRSY0ZqV1p2bmZtTWJHQlM0dWd3S1pxR0Z2cjRIQlNpZHB3eXlwK20wClV6ZkVxNFRRTXBDenhiS2V5QVRxZlJhM1lmWmtwa0FJSDZSRTJGWDhOcGE5Z0szcDFpL1J3TmlOcnBlQkFnTUIKQUFHalJqQkVNQk1HQTFVZEpRUU1NQW9HQ0NzR0FRVUZCd01DTUF3R0ExVWRFd0VCL3dRQ01BQXdId1lEVlIwagpCQmd3Rm9BVTZETmt5THFSNmJaYnV6K2N4S2tOTWZROWJjRXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQmkxCi83MFR0NDkxcHBydmVlQXZQU0p6bzlic2xnSHY4emdhdlRUeXRzN1REamIremNQUGViMlRoSnZXR1k3YTBYeXYKdjNkblB5NzBLSnFpTWhldWJnc29LUHo3cDlDckVheGNIWWNjakh2QnZqbjhMd3EwNGd6VldsMHR5K3NKdDB0QQpiTHBSWXZqdVZ5K0tYeWpLYnZqMVlXZXFDc3llK04xNjltcEpPbVZQblBVMGdxOFRtL1B4RXdWWTUyQ1RQcDg1ClNIZE9COWIwRzNXZWR6b2hDZmo0cjRONDJvYTNtYllWYlJQR2IxZjNCUDU4TDh6TW9YY3dZNXpMYklDeWZuOG8KNnF6ZG9DZllYZVNoUFU2aGNtN3F6TnplOGNZYmlqaEJ0dTl2UWJ2UnlDTGdDeHJmbjY0dnVTVXpxUkp6ODZuMgpMMmlXRGdUS0tpeFBIOUVSM2dRPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
  conditions:
  - lastTransitionTime: "2021-04-15T13:17:18Z"
    lastUpdateTime: "2021-04-15T13:17:18Z"
    message: This CSR was approved by kubectl certificate approve.
    reason: KubectlApprove
    status: "True"
    type: Approved

.status.certificate 항목에 base64로 인코딩된 인증서가 위치하며, 아래 명령어로 원본 데이터를 얻을 수 있다.

echo "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURPakNDQWlLZ0F3SUJBZ0lSQUsycytPc1Q1WjVvTnoxcVgwZDhEQll3RFFZSktvWklodmNOQVFFTEJRQXcKRlRFVE1CRUdBMVVFQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TVRBME1UVXhNekV5TVRoYUZ3MHlNakEwTVRVeApNekV5TVRoYU1GUXhDekFKQmdOVkJBWVRBa0ZWTVJNd0VRWURWUVFJRXdwVGIyMWxMVk4wWVhSbE1TRXdId1lEClZRUUtFeGhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXhEVEFMQmdOVkJBTVRCR1JsZGpFd2dnRWkKTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDVzAzQ01HemF2bnhiSlhZZXVySm80ZEZTagpWbTRTSFRkVlZoNUFSTTFhUE54MnVieDBMT2tDcVpOaTBUMzRRNnBiUGtxZGlFM2M1UnFhbTBYR21Gc2JaWkVqClBGdm56MDdIazNkVWpMeTBseFNkWWtGdUlPOUQrWVo1UHZ3a2tJamtObnd1ZmRxV2xtRlJIeXp6VGxyWXNLaS8KMjdOMm1mYlZTbG5pUGlLYmNTd1FPMmhQU1V1VW9ENEwwTjZiMVBFZCtzcHBoalV6Q01RZTNsQzlRKzVLTkdPSApBM2p1UHZyL2haNm1qUEx0RFRLemVoMFRSY0ZqV1p2bmZtTWJHQlM0dWd3S1pxR0Z2cjRIQlNpZHB3eXlwK20wClV6ZkVxNFRRTXBDenhiS2V5QVRxZlJhM1lmWmtwa0FJSDZSRTJGWDhOcGE5Z0szcDFpL1J3TmlOcnBlQkFnTUIKQUFHalJqQkVNQk1HQTFVZEpRUU1NQW9HQ0NzR0FRVUZCd01DTUF3R0ExVWRFd0VCL3dRQ01BQXdId1lEVlIwagpCQmd3Rm9BVTZETmt5THFSNmJaYnV6K2N4S2tOTWZROWJjRXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQmkxCi83MFR0NDkxcHBydmVlQXZQU0p6bzlic2xnSHY4emdhdlRUeXRzN1REamIremNQUGViMlRoSnZXR1k3YTBYeXYKdjNkblB5NzBLSnFpTWhldWJnc29LUHo3cDlDckVheGNIWWNjakh2QnZqbjhMd3EwNGd6VldsMHR5K3NKdDB0QQpiTHBSWXZqdVZ5K0tYeWpLYnZqMVlXZXFDc3llK04xNjltcEpPbVZQblBVMGdxOFRtL1B4RXdWWTUyQ1RQcDg1ClNIZE9COWIwRzNXZWR6b2hDZmo0cjRONDJvYTNtYllWYlJQR2IxZjNCUDU4TDh6TW9YY3dZNXpMYklDeWZuOG8KNnF6ZG9DZllYZVNoUFU2aGNtN3F6TnplOGNZYmlqaEJ0dTl2UWJ2UnlDTGdDeHJmbjY0dnVTVXpxUkp6ODZuMgpMMmlXRGdUS0tpeFBIOUVSM2dRPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" | base64 -d > dev1.crt

 이제 dev1.key, dev1.crt 두 개의 파일을 사용하여 kube-apiserver 접근이 가능하다. 물론 어떠한 RBAC 설정도 되지 않은 계정이기 때문에 아직 할 수 있는 동작은 없다. 이는 해당 계정에 부여할 Role과 RoleBinding을 통해 해결 가능하지만 이번 과정은 사용자 추가가 목적이므로 생략하도록 한다.

 

5. kubeconfig 설정

새로 추가한 사용자 계정으로 kubectl을 사용하기 위해선 kubeconfig에 해당 정보가 있어야 한다. 아래 명령어로 추가해보자.

kubectl config set-credentials dev1 --client-key=dev1.key --client-certificate=dev1.crt --embed-certs

현재 kubeconfig 설정은 다음 명령어로 확인 가능하다.

kubectl config view

이제 K8s 클러스터와 kubectl 사용자를 매칭하는 context를 정의할 차례다.

kubectl config set-context dev1 --user=dev1 --cluster=kubernetes

context 목록은 다음 명령어로 확인 가능하다.

kubectl config get-contexts

다음 명령어로 kubectl에서 사용할 context를 선택할 수 있다.

kubectl config use-context dev1

 

여기까지 진행했으면, 새로 추가한 사용자 'dev1'으로 기존 클러스터 'kubernetes'에 접근할 수 있다. 다만 RBAC 설정을 하지 않은 상태이므로, 대부분의 명령어는 실패한다.

 

사용자 추가시 주의할 사항은 인증서가 의도치않게 외부로 유출된 경우, 유효기한이 만료할 때까지 이를 무효화할 방법이 없다는 점이다. 다만 기존에 부여한 RoleBinding 또는 ClusterRoleBinding을 제거함으로써 리소스에 접근하는 것은 막을 수 있다. 

kubelet Cert Bootstrapping

kubelet도 마찬가지로 API 서버에 접근 가능한 Client 인증서 및 자체 Server 역할을 위한 인증서를 필요로 한다. 다행히 관리자가 직접 인증서를 생성하거나 승인하는 대신, kubelet이 클러스터에 Join하는 시점에 임시 토큰값(Bootstrap token)을 통해 자동으로 발급된다. 특히 Controller Manager에선 CSR을 요청한 주체가 kubelet일 경우, 자동으로 승인 및 발급이 이루어지도록 설계되어 있다. 이를 Sequence Diagram으로 표현하면 다음과 같다.

 

 

Kubernetes 인증서 유효기간

Public Cloud의 Managed 서비스를 사용할 경우, K8s 인증서를 신경쓰지 않아도 아무런 문제가 없다. 다만 on-premise 환경에서 kubeadm을 통해 클러스터를 구축한 경우, CA를 제외한 모든 Certificate의 유효기간은 1년이며, 만료시 클러스터를 사용할 수 없기 때문에 반드시 주기적인 갱신이 필요하다. 유효 기간은 아래 명령어를 통해 이를 확인할 수 있다.

kubeadm certs check-expiration

인증서를 연장하는 방법은 사실 간단한데, 일반적으로 K8s 버전 업그레이드 과정에서 자동으로 갱신된다. 다만 업데이트 하기 어려운 환경이라면, 'kubeadm certs renew' 명령어를 통해 일괄 갱신이 가능하다.

 

참고

'Kubernetes > Architecture' 카테고리의 다른 글

Kubernetes Custom Resource와 Controller  (0) 2021.07.21
Pod Lifecycle  (0) 2021.03.07
Calico Components  (0) 2021.02.11
Linux Namespaces  (0) 2021.02.02
CNI - Spec  (0) 2021.01.24