[CKS] 1-7. k8s TLS 인증서 생성

OpenSSL로 CA, 클라이언트, 서버 인증서를 생성하고 mTLS 통신으로 클러스터 전체를 안전하게 보호하는 방법을 확인합니다.

[CKS] 1-7. k8s TLS 인증서 생성
Photo by Liam Truong / Unsplash

개요

클러스터 인증서를 생성하기 위한 방법을 정리했습니다.

인증서 생성

인증서를 생성하기 위한 방법은 다양합니다. 현재 문서에서는 OpenSSL을 사용하였습니다.

CA 인증서 생성

  1. 인증서 생성을 위한 키 생성
  2. 인증서 서명 요청 생성

3. 자체 서명된 인증서 생성

openssl genrsa -out ca.key 2048
openssl req -new -key ca.key -subj "/CN=KUBERNETES-CA" -out ca.csr
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt

이제 다른 인증서에 대해서는 해당 CA 키 쌍을 사용하여 서명할 수 있습니다.

해당 인증서 사본을 mTLS 구조에서 각 클라이언트와 서버에 저장합니다.


Client 인증서(Admin User 인증서) 생성

  1. 인증서 생성을 위한 키 생성
  2. 인증서 서명 요청 생성

3. CA 인증서와 CA 키 쌍으로 인증서에 서명

openssl genrsa -out admin.key 2048
openssl req -new -key admin.key -subj "/CN=kube-admin" -out admin.csr
openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -out admin.crt

이제 클러스터 내에서 유효한 인증서가 되어 admin 사용자가 k8s 클러스터에 인증할 때 사용합니다.

즉 인증서는 검증된 사용자 ID, 키는 비밀번호와 같습니다.

추가적으로 단순한 사용자 인증서가 아닌 관리자 사용자를 구분하기 위해 그룹 세부정보를 추가하여 생성할 수 있습니다. OU를 SYSTEM:MASTERS 로 지정하여 그룹 세부정보를 추가합니다.

openssl genrsa -out admin.key 2048
openssl req -new -key admin.key -subj "/CN=kube-admin/O=system:masters" -out admin.csr
openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -out admin.crt

서명이 완료되면 admin 사용자에 대한 인증서를 갖게 됩니다. 다른 쿠버네티스 구성 요소(예: Kube 스케줄러, 컨트롤러 관리자, Kube 프록시)에 대한 인증서도 SYSTEM 이라는 접두사를 부쳐 유사한 절차로 생성됩니다.

이러한 인증서는 Kube API 서버와의 보안 인증을 지원하여 아이디/패스워드 방식이 아닌 키, 클라이언트 인증서, CA 인증서를 사용하는 REST API 호출을 허용합니다.

curl https://kube-apiserver:6443/api/v1/pods \
  --key admin.key --cert admin.crt --cacert ca.crt
-----------------------------------------------------
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "selfLink": "/api/v1/pods"
  },
  "items": []
}

해당 과정을 정리하면 다음과 같습니다.

  1. -cacert ca.crt: 클라이언트가 서버를 신뢰할 수 있는지 확인합니다.
    • 클라이언트는 접속 시도시 ca.crt에 지정된 CA 인증서를 기준으로 서버가 제시하는 인증서가 유효하고 신뢰할 수 있는지를 검증합니다. 즉, "내가 통신하려는 이 서버가 ca.crt라는 내가 믿는 인증기관(CA)에 의해 보증된 서버인가?"를 확인하는 것입니다.
  2. -cert admin.crt: 서버가 클라이언트를 신뢰할 수 있는지 확인 (1단계 - 인증서 자체 검증).
    • 클라이언트는 admin.crt라는 자신의 인증서를 서버에 제시합니다.
    • 서버는 이 admin.crt가 자신이 신뢰하는 CA로부터 발급된 유효한 인증서인지 확인합니다. 즉, "이 접속 요청을 한 클라이언트의 인증서(admin.crt)는 내가 믿는 인증기관(CA)이 발급해 준 것이 맞는가?"를 확인합니다.
  3. -key admin.key: 서버가 클라이언트를 신뢰할 수 있는지 확인 (2단계 - 개인키 소유 증명).
    • 서버는 admin.crt를 제시한 클라이언트가 실제로 그 인증서에 명시된 개인키(admin.key)의 소유자인지 암호학적인 방법(챌린지-응답)으로 확인합니다. 즉, "이 클라이언트가 제시한 인증서(admin.crt)의 실제 주인이 맞는가?"를 최종적으로 확인하는 것입니다. 이 개인키 자체는 전송되지 않습니다.

대부분의 Kubernetes 클라이언트는 이러한 매개변수를 KubeConfig라는 구성 파일에 통합합니다. 이 파일에는 API 서버 엔드포인트와 해당 인증서에 대한 세부 정보가 들어 있습니다.


서버용 인증서 생성

고가용성 배포에 필수적인 etcd 서버의 인증서 생성 프로세스는 클라이언트 인증서 생성 프로세스와 유사합니다. etcd 서버는 안전한 클러스터 간 통신을 위해 추가 피어 인증서가 필요하게 됩니다.

etcd 서버의 키와 인증서를 생성한 후 etcd 설정 파일에서 이를 참조할 수 있습니다.

기본적인 cert-file뿐만 아니라 peer-cert도 확인할 수 있습니다.

cat etcd.yaml
-------------------------------------
etcd:
  --advertise-client-urls=https://127.0.0.1:2379
  --key-file=/path-to-certs/etcdserver.key
  --cert-file=/path-to-certs/etcdserver.crt
  --client-cert-auth=true
  --data-dir=/var/lib/etcd
  --initial-advertise-peer-urls=https://127.0.0.1:2380
  --initial-cluster=master=https://127.0.0.1:2380
  --listen-client-urls=https://127.0.0.1:2379
  --listen-peer-urls=https://127.0.0.1:2380
  --name=master
  --peer-cert-file=/path-to-certs/etcdpeer1.crt
  --peer-client-cert-auth=true
  --peer-key-file=/path/to/etcd/peer.key
  --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
  --snapshot-count=10000
  --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt

kube API 서버는 쿠버네티스 Control Plane의 핵심 구성 요소입니다. 이 서버는 여러 DNS 이름과 IP 주소로 인식되므로 인증서에 모든 대체 이름이 포함되어야 합니다.

우선 키와 CSR을 생성하는 것은 동일합니다.

openssl req -new -key apiserver.key -subj "/CN=kube-apiserver" -out apiserver.csr

다만 openssl.cnf다음 내용으로 OpenSSL 구성 파일을 만들어 SAN(주체 대체 이름)을 작성합니다.

[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation
subjectAltName = @alt_names

[alt_names]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster.local
IP.1 = 10.96.0.1
IP.2 = 172.17.0.87

최종 인증서를 생성합니다.

openssl x509 -req -in apiserver.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out apiserver.crt -extensions v3_req -extfile openssl.cnf -days 1000

모두 생성이 완료되었다면 인증서들의 위치를 kube API 서버의 실행 파일이나 서비스 구성 파일에 전달하여 수행합니다.

ExecStart=/usr/local/bin/kube-apiserver \\
  --advertise-address=${INTERNAL_IP} \\
  --allow-privileged=true \\
  --apiserver-count=3 \\
  --authorization-mode=Node,RBAC \\
  --bind-address=0.0.0.0 \\
  --enable-swagger-ui=true \\
  --etcd-cafile=/var/lib/kubernetes/ca.pem \\
  --etcd-certfile=/var/lib/kubernetes/apiserver-etcd-client.crt \\
  --etcd-keyfile=/var/lib/kubernetes/apiserver-etcd-client.key \\
  --etcd-servers=https://127.0.0.1:2379 \\
  --event-ttl=1h \\
  --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
  --kubelet-client-certificate=/var/lib/kubernetes/apiserver-kubelet-client.crt \\
  --kubelet-client-key=/var/lib/kubernetes/apiserver-kubelet-client.key \\
  --kubelet-https=true \\
  --runtime-config=api/all \\
  --service-account-key-file=/var/lib/kubernetes/service-account.pem \\
  --service-cluster-ip-range=10.32.0.0/24 \\
  --service-node-port-range=30000-32767 \\
  --client-ca-file=/var/lib/kubernetes/ca.pem \\
  --tls-cert-file=/var/lib/kubernetes/apiserver.crt \\
  --tls-private-key-file=/var/lib/kubernetes/apiserver.key \\
  --v=2

각 etcd, kubelet과 소통하기 위한 client 파일도 동일하게 저장합니다.

다음으로 Kubelet 서버 및 클라이언트 인증서를 설치합니다.

각 쿠버네티스 노드는 노드 작업을 관리하는 HTTPS API 서버 역할을 하는 kubelet을 실행합니다. 이를 kube api 서버와 통신하기 위해서 각 노드는 자체 키와 인증서 쌍을 보유해야 하며, 일반적으로 노드의 이름을 따서 명명됩니다(예: node-01, node-02, node-03).

노드별 인증서는 kubelet 구성 파일 내에서 참조됩니다.

각 노드에 대해서 해당 작업을 반복하여 수행하면 됩니다.

kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  x509:
    clientCAFile: "/var/lib/kubernetes/ca.pem"
authorization:
  mode: Webhook
clusterDomain: "cluster.local"
clusterDNS:
  - "10.32.0.10"
podCIDR: "${POD_CIDR}"
resolvConf: "/run/systemd/resolve/resolv.conf"
runtimeRequestTimeout: "15m"
tlsCertFile: "/var/lib/kubelet/kubelet-node01.crt"
tlsPrivateKeyFile: "/var/lib/kubelet/kubelet-node01.key"

노드는 마찬가지로 SYSTEM 구성 요소이기 때문에 각 인증서의 이름에 system:node:... 형식으로 구성되며 OU로 지정하며 SYSTEM:NODES 를 포함합니다.

Reference