콤퓨우터/필기: KodeKloud CKA 강의

146. TLS in Kubernetes

파란화면 2024. 5. 17. 00:30
반응형

쿠버에는 기본적으로 (관리자, 개발자 같은) 사용자라는 개념이 없다. kubectl create user user-name 같은 식으로 사용자를 생성하는 것은 당연히 불가능

  • 다만, 사람이 아닌 entity를 위한 Service Account라는 개념은 존재한다.

하지만 kube-apiserver에 들어오는 API 콜 (kubectl이든 HTTP API endpoint를 통한 직접 접근이든) 을 무조건 다 받아주면 안되는 것은 자명하다. 어떻게든 user authentication을 거친 뒤에 리퀘를 받아줘야 하는 것이다.

Kubernetes 1.19까지는 Client Certificate를 사용하지 않고 인증하는 방식들이 있었다

  • 비밀번호,사용자이름,사용자ID[,그룹] 형식의 CSV파일을 넣은 뒤, 요청 시 ID:PW를 HTTP 헤더에 넣어서 보내는 방법 (HTTP Digest Auth)
  • 토큰,사용자이름,사용자명[,그룹] 형식의 토큰 파일을 만든 뒤, 요청 시 해당 토큰을 HTTP 헤더에 넣어서 보내는 방법 (HTTP Basic Auth)

당연히 별로 안전한 방식은 아니었기 때문에 전부 지원이 삭제되었고, 지금은 TLS의 Client Certificate를 사용하는 방식으로 바뀌었다.

Client & Server Certificates

일반적인 웹에서는 TLS 연결 시 클라이언트가 서버 인증서를 검증하지, 그 역으로는 잘 안 한다. (아무래도 접속할 때마다 사용자에게 인증서를 고르라고 하는 것은 UX가 별로이기 때문에)
하지만 클라이언트 인증서 검증은 엄연히 TLS 스펙상에 존재하는 방식이고, 브라우저들에서도 구현을 하고 있으며, 실제로 일부 사이트들은 (업무용 사이트라든가?) 웹에서도 클라이언트 인증서 검증을 하기도 한다

쿠버네티스에서는 거의 모든 서비스와의 상호작용에 TLS 연결을 사용하고, 또 클라이언트 인증서 검증을 요구한다

  • 이 말은, 그만큼 연관되는 인증서가 많다는 뜻이다

관리자가 kubectl로 kube-apiserver에 접근하거나 kube-scheduler가 kube-apiserver에 접근하는 시나리오 모두에서 마찬가지로 클라/서버 쌍방 인증서 검증이 요구된다

  • 주의할 점은, 같은 엔티티가 상황에 따라 서버가 될 수도 있고, 클라이언트가 될 수도 있다는 점이다
    • 예를 들어 kube-apiserver는 kubectl 입장에서는 서버이지만, etcd에 접속할 때에는 kube-apiserver가 클라이언트이다.
      • 이 때 두 용도 모두에 1개의 인증서-키 쌍을 이용하거나, 아니면 서버일 때 쓰는 인증서, 클라이언트일 때 쓰는 인증서를 나눠서 구성하는 것도 가능하다.

 

물론 이 인증서들은 전부 Self-signed certificate일 수는 없다.

  • 하나의 쿠버 클러스터에는 최소 1개의 CA가 필요하고, 저 인증서들은 모두 해당 CA에서 서명한 것이어야 한다
    • ETCD를 담당하는 CA와 나머지를 담당하는 또 하나의 CA로 분리하여 2CA 구성을 하는 것도 가능하다
      • 이 경우, etcd에 접근하기 위한 클라이언트 인증서는 etcd 담당 CA에서 서명한 것이어야 한다

CA 인증서 생성

  1. CA용 RSA 2048bit Private Key를 생성
    openssl genrsa -out ca.key 2048
  2. CA 인증서 발행을 위한 CSR 생성
    openssl req -new -key ca.key -subj "/CN=CA-NAME" -out ca.csr
  3. CSR을 이용하여 Self-Signed CA Certificate 생성
    openss x509 -req -in ca.csr -signkey -ca.key -out ca.crt

Client Certificate 생성

관리자용 Client Certificate를 생성한다고 가정하자

  1. RSA 2048bit Private Key를 생성
    openssl genrsa -out admin-name.key 2048
  2. CA 인증서 발행을 위한 CSR 생성
    openssl req -new -key admin-name.key -subj "/CN=사용자-이름/O=system:masters" -out admin-name.csr
    • system:masters: Admin Group
  3. CSR을 이용하여 관리자용 Client Certificate 생성, CA의 인증서와 keypair로 서명
    openss x509 -req -in admin-name.csr -CA ca.csr -CAKey ca.key -out admin-name.crt

비슷한 과정을 kube-scheduler, kube-controller-manager, kube-proxy 등에 대해서도 진행한다

주의사항

CA의 인증서는 모든 클라이언트와 서버에 배포되어 있어야 한다

etcd에서의 인증서

etcd-server이름으로 생성
HA 구성에서는 etcd가 여러 개 있을 수도 있다, 이 경우 여러 peer certificate이 필요

구성의 상상도:

이후 etcd.yaml을 다음과 같이 설정하여주어야한다.

- etcd
...
    - --key-file=/path/to/etcdserver.key
    - --cert-file=/path/to/etcdserver.pem
    - --peer-cert-file=/path/to/etcdpeer1.pem
    - --peer-client-cert-auth=true
    - --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
    - --peer-trusted-ca-file/etc/kubernetes/pki/etcd/ca.crt
    - --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
...

kube-apiserver에서의 인증서

kube-apiserver는 쿠버에 있어서 매우 중요한 컴포넌트이고, 이 때문에 이명도 많다

  • kubernetes
  • kubernetes.default
  • kubernetes.default.svc
  • kubernetes.default.svc.cluster.local
  • 클러스터 IP 주소
  • ...

이 모든 것이 인증서에 SAN(Subect Alternative Name)으로 박혀있어야한다

OpenSSL 1.1.1 출시 전까지는 SAN을 넣으려면 우선 OpenSSL conf 파일을 만들고, 이것을 파라미터로 openssl req -new -key kubeapiserver.key -subj "/CN=kube-apiserver" -out apiserver.csr -config config.cnf 처럼 넘겨줘야 했다

[req]
req_extensions = v3_req
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation,
subjectAltName = @alt_names
[alt_names]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
...
IP.1 = 10.96.0.1
...

OpenSSL 1.1.1부터는, 다음처럼 그냥 파라미터로 넘겨줄 수 있다
openssl req -new -key apiserver.key -subj="/CN=kube-apiserver" -out apiserver.csr -addext -extension 'subjectAltName = DNS:kubernetes, DNS:kubernetes.default, IP:10.96.0.1

아무튼, 이렇게 CSR 파일을 만들었고, 이제 CA 인증서로 새 인증서를 서명하여 발급했다고 해 보자

적용하기

systemd service로 구동 중인 경우,

ExecStart=/usr/local/bin/kube-apiserver \\
...
  --client-ca-file=/var/lib/kubernetes/ca.pem \\
  --tls-cert-file=/var/lib/kubernetes/apiserver.crt \\
  --tls-private-key=/var/lib/kubernetes/apiserver.key \\
...
  --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
...
  --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem
  --kubelet-client-certificate=/var/lib/kubernetes/apiserver-kubelet-client.crt
  --kubelet-cient-key=/var/lib/kubernetes/apiserver-kubelet-client.key
...
  • kube-apiserver가 kubelet에 접속하는 경우에 사용하는 client cert/keypair도 존재
    • kube-apiserve는 노드 모니터링, pod 스케줄링 정보 전달 등을 위해 kubelet에 접속하는 일이 있음 (outbound)

Kubelet에서의 인증서

Kubelet의 서버 인증서

이름이 kubelet이 아니라 노드이름이어야 한다
만들어진 인증서는 kubelet config file에 넣어준다

kind:KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  x509:
     clientCAFile: "/var/lib/kubernetes/ca.pem"
...
tlsCertFile: "/var/lib/kubelet/kubelet-node01.crt"
tlsPrivateKeyFile: "/var/lib/kubelet/kubelet-node01.key"

Kubelet의 클라이언트 인증서

kube-scheduler가 그렇듯 노드는 시스템 컴포넌트이므로, 클라이언트 인증서들은 system:node:노드이름의 포맷을 따라야한다

또한, kube-apiserver가 kubelet에 적절한 권한을 부여할 수 있도록, Organisation이 system:nodes로 설정되어야 한다

반응형