Thứ Tư, 18/03/2026, 17:00 (GMT+0)

[Part 3] PKI, TLS và Cấu hình xác thực cluster

Quay lại Trang chủ Blog
Trên trang này

Sau khi hoàn tất việc thiết lập môi trường ở Part 2, các node trong cluster đã có thể kết nối và giao tiếp với nhau. Tuy nhiên, việc “kết nối được” không đồng nghĩa với “an toàn” hay “đáng tin cậy”. Ở thời điểm này, hệ thống vẫn chưa có cơ chế để xác thực danh tính giữa các thành phần cũng như bảo vệ dữ liệu khi truyền qua network.

Trong Kubernetes, mọi thành phần đều giao tiếp thông qua API và network. Nếu không có cơ chế xác thực và mã hóa, bất kỳ thành phần nào cũng có thể giả mạo hoặc can thiệp vào hệ thống. Vì vậy, Kubernetes sử dụng PKI và TLS để định danh cho từng component và mã hóa toàn bộ traffic trong cluster.

Để làm được điều đó, trước khi bootstrap cluster, cần thiết lập một số thành phần quan trọng:

  • Certificate Authority (CA) để cấp phát và xác thực certificate
  • Certificate cho các component như kube-apiserver, kubelet, etcd
  • Các file kubeconfig để các thành phần và client xác thực với API Server
  • Cấu hình mã hóa dữ liệu (encryption config) để bảo vệ secrets trong etcd

Các thành phần này đóng vai trò đảm bảo:

  • Mỗi component trong cluster có một danh tính rõ ràng
  • Các kết nối giữa các thành phần được mã hóa và xác thực
  • Dữ liệu nhạy cảm không bị lộ khi lưu trữ

Nội dung bên dưới sẽ hướng dẫn thiết lập lần lượt các thành phần trên để xây dựng nền tảng security và identity cho cluster, làm tiền đề cho việc triển khai control plane ở phần tiếp theo.

Khởi tạo Certificate Authority (CA) và tạo TLS certificates

Trong Kubernetes, Certificate Authority (CA) đóng vai trò là “gốc tin cậy” của toàn bộ cluster. Mọi certificate của các component sẽ được CA ký để đảm bảo danh tính và tính hợp lệ trong hệ thống. 

Trong phần này, chúng ta sẽ khởi tạo một CA dạng self-signed bằng OpenSSL. Đây là mô hình đơn giản phù hợp cho lab, giúp mô phỏng cách Kubernetes quản lý trust chain trong thực tế.

Để đơn giản hóa quá trình tạo certificate cho nhiều thành phần, chúng ta sử dụng file cấu hình OpenSSL (ca.conf). File này định nghĩa sẵn các template cho từng loại certificate như node, kube-apiserver, controller-manager, scheduler và service accounts.

[req]

distinguished_name = req_distinguished_name

prompt          = no

x509_extensions = ca_x509_extensions

 

[ca_x509_extensions]

basicConstraints = CA:TRUE

keyUsage      = cRLSign, keyCertSign

 

[req_distinguished_name]

C   = US

ST  = Washington

L   = Seattle

CN  = CA

 

[admin]

distinguished_name = admin_distinguished_name

prompt          = no

req_extensions  = default_req_extensions

 

[admin_distinguished_name]

CN = admin

O  = system:masters

 

# Service Accounts

#

# The Kubernetes Controller Manager leverages a key pair to generate

# and sign service account tokens as described in the

# [managing service accounts](https://kubernetes.io/docs/admin/service-accounts-admin/)

# documentation.

 

[service-accounts]

distinguished_name = service-accounts_distinguished_name

prompt          = no

req_extensions  = default_req_extensions

 

[service-accounts_distinguished_name]

CN = service-accounts

 

# Worker Nodes

#

# Kubernetes uses a [special-purpose authorization mode](https://kubernetes.io/docs/admin/authorization/node/)

# called Node Authorizer, that specifically authorizes API requests made

# by [Kubelets](https://kubernetes.io/docs/concepts/overview/components/#kubelet).

# In order to be authorized by the Node Authorizer, Kubelets must use a credential

# that identifies them as being in the `system:nodes` group, with a username

# of `system:node:<nodeName>`.

 

[node-0]

distinguished_name = node-0_distinguished_name

prompt          = no

req_extensions  = node-0_req_extensions

 

[node-0_req_extensions]

basicConstraints  = CA:FALSE

extendedKeyUsage  = clientAuth, serverAuth

keyUsage          = critical, digitalSignature, keyEncipherment

nsCertType        = client

nsComment         = "Node-0 Certificate"

subjectAltName    = DNS:node-0, IP:127.0.0.1

subjectKeyIdentifier = hash

 

[node-0_distinguished_name]

CN = system:node:node-0

O  = system:nodes

C  = US

ST = Washington

L  = Seattle

 

[node-1]

distinguished_name = node-1_distinguished_name

prompt          = no

req_extensions  = node-1_req_extensions

 

[node-1_req_extensions]

basicConstraints  = CA:FALSE

extendedKeyUsage  = clientAuth, serverAuth

keyUsage          = critical, digitalSignature, keyEncipherment

nsCertType        = client

nsComment         = "Node-1 Certificate"

subjectAltName    = DNS:node-1, IP:127.0.0.1

subjectKeyIdentifier = hash

 

[node-1_distinguished_name]

CN = system:node:node-1

O  = system:nodes

C  = US

ST = Washington

L  = Seattle

 

 

# Kube Proxy Section

[kube-proxy]

distinguished_name = kube-proxy_distinguished_name

prompt          = no

req_extensions  = kube-proxy_req_extensions

 

[kube-proxy_req_extensions]

basicConstraints  = CA:FALSE

extendedKeyUsage  = clientAuth, serverAuth

keyUsage          = critical, digitalSignature, keyEncipherment

nsCertType        = client

nsComment         = "Kube Proxy Certificate"

subjectAltName    = DNS:kube-proxy, IP:127.0.0.1

subjectKeyIdentifier = hash

 

[kube-proxy_distinguished_name]

CN = system:kube-proxy

O  = system:node-proxier

C  = US

ST = Washington

L  = Seattle

 

 

# Controller Manager

[kube-controller-manager]

distinguished_name = kube-controller-manager_distinguished_name

prompt          = no

req_extensions  = kube-controller-manager_req_extensions

 

[kube-controller-manager_req_extensions]

basicConstraints  = CA:FALSE

extendedKeyUsage  = clientAuth, serverAuth

keyUsage          = critical, digitalSignature, keyEncipherment

nsCertType        = client

nsComment         = "Kube Controller Manager Certificate"

subjectAltName    = DNS:kube-controller-manager, IP:127.0.0.1

subjectKeyIdentifier = hash

 

[kube-controller-manager_distinguished_name]

CN = system:kube-controller-manager

O  = system:kube-controller-manager

C  = US

ST = Washington

L  = Seattle

 

 

# Scheduler

[kube-scheduler]

distinguished_name = kube-scheduler_distinguished_name

prompt          = no

req_extensions  = kube-scheduler_req_extensions

 

[kube-scheduler_req_extensions]

basicConstraints  = CA:FALSE

extendedKeyUsage  = clientAuth, serverAuth

keyUsage          = critical, digitalSignature, keyEncipherment

nsCertType        = client

nsComment         = "Kube Scheduler Certificate"

subjectAltName    = DNS:kube-scheduler, IP:127.0.0.1

subjectKeyIdentifier = hash

 

[kube-scheduler_distinguished_name]

CN = system:kube-scheduler

O  = system:system:kube-scheduler

C  = US

ST = Washington

L  = Seattle

 

 

# API Server

#

# The Kubernetes API server is automatically assigned the `kubernetes`

# internal dns name, which will be linked to the first IP address (`10.32.0.1`)

# from the address range (`10.32.0.0/24`) reserved for internal cluster

# services.

 

[kube-api-server]

distinguished_name = kube-api-server_distinguished_name

prompt          = no

req_extensions  = kube-api-server_req_extensions

 

[kube-api-server_req_extensions]

basicConstraints  = CA:FALSE

extendedKeyUsage  = clientAuth, serverAuth

keyUsage          = critical, digitalSignature, keyEncipherment

nsCertType        = client, server

nsComment         = "Kube API Server Certificate"

subjectAltName    = @kube-api-server_alt_names

subjectKeyIdentifier = hash

 

[kube-api-server_alt_names]

IP.0  = 127.0.0.1

IP.1  = 10.32.0.1

DNS.0 = kubernetes

DNS.1 = kubernetes.default

DNS.2 = kubernetes.default.svc

DNS.3 = kubernetes.default.svc.cluster

DNS.4 = kubernetes.svc.cluster.local

DNS.5 = server.kubernetes.local

DNS.6 = api-server.kubernetes.local

 

[kube-api-server_distinguished_name]

CN = kubernetes

C  = US

ST = Washington

L  = Seattle

 

 

[default_req_extensions]

basicConstraints  = CA:FALSE

extendedKeyUsage  = clientAuth

keyUsage          = critical, digitalSignature, keyEncipherment

nsCertType        = client

nsComment         = "Admin Client Certificate"

subjectKeyIdentifier = hash

Mọi CA đều gồm private key và root certificate. Trong phần này, chúng ta tạo một CA self-signed bằng OpenSSL để làm gốc tin cậy cho cluster. Cách này phù hợp cho lab nhưng không dùng trong production. Tiếp theo, chúng ta sẽ tiến hành khởi tạo CA. Kết quả đầu ra gồm hai file: ca.crt và ca.key

openssl genrsa -out ca.key 4096

  openssl req -x509 -new -sha512 -noenc \

-key ca.key -days 3653 \

-config ca.conf \

-out ca.crt

Tạo chứng chỉ (Cert) Client và Server

Tiếp theo, chúng ta sẽ tạo certificate cho từng component trong Kubernetes, bao gồm cả client certificate cho user admin để truy cập cluster.

Quá trình này gồm 3 bước lặp lại cho mỗi thành phần: sinh private key, tạo CSR (Certificate Signing Request) và ký CSR bằng CA đã khởi tạo trước đó.

certs=(

  "admin" "node-0" "node-1"

  "kube-proxy" "kube-scheduler"

  "kube-controller-manager"

  "kube-api-server"

  "service-accounts"

)
for i in ${certs[*]}; do

  openssl genrsa -out "${i}.key" 4096

 

  openssl req -new -key "${i}.key" -sha256 \

-config "ca.conf" -section ${i} \

-out "${i}.csr"

 

  openssl x509 -req -days 3653 -in "${i}.csr" \

-copy_extensions copyall \

-sha256 -CA "ca.crt" \

-CAkey "ca.key" \

-CAcreateserial \

-out "${i}.crt"

done

Sau khi thực thi hai khối lệnh trên, hệ thống sẽ tạo ra private key, Certificate Signing Request (CSR) và certificate đã được ký cho từng thành phần trong Kubernetes. Có thể kiểm tra các file được tạo bằng lệnh sau:

ls -1 *.crt *.key *.csr

Danh sách các file tạo ra sẽ bao gồm:

admin.crt

admin.csr

admin.key

ca.crt

ca.key

kube-api-server.crt

kube-api-server.csr

kube-api-server.key

kube-controller-manager.crt

kube-controller-manager.csr

kube-controller-manager.key

kube-proxy.crt

kube-proxy.csr

kube-proxy.key

kube-scheduler.crt

kube-scheduler.csr

kube-scheduler.key

node-0.crt

node-0.csr

node-0.key

node-1.crt

node-1.csr

node-1.key

service-accounts.crt

service-accounts.csr

service-accounts.key

Phân phối chứng chỉ Client và Server

Trong bước này, các chứng chỉ đã tạo sẽ được phân phối đến từng node tại đúng vị trí mà các thành phần Kubernetes sử dụng để xác thực. Trong thực tế, các file này cần được bảo vệ nghiêm ngặt vì chúng đóng vai trò như thông tin định danh giữa các thành phần trong cluster.

Trước tiên, chúng ta sẽ sao chép certificate và private key tương ứng đến các máy node-0node-1:

for host in node-0 node-1; do

  ssh root@${host} mkdir /var/lib/kubelet/

 

  scp ca.crt root@${host}:/var/lib/kubelet/

 

  scp ${host}.crt \

root@${host}:/var/lib/kubelet/kubelet.crt

 

  scp ${host}.key \

root@${host}:/var/lib/kubelet/kubelet.key

done

Sao chép các certificate và private key cần thiết đến máy chủ

scp \

  ca.key ca.crt \

  kube-api-server.key kube-api-server.crt \

  service-accounts.key service-accounts.crt \

  root@server:~/

Các chứng chỉ client của kube-proxy, kube-controller-manager, kube-scheduler và kubelet sẽ được sử dụng để tạo các file kubeconfig phục vụ xác thực giữa các thành phần trong phần tiếp theo

Tạo file cấu hình Kubernetes (kubeconfig) cho cơ chế xác thực

Phần này tập trung vào việc tạo các file kubeconfig cho các client trong Kubernetes. Đây là các cấu hình dùng để thiết lập kết nối và xác thực với Kubernetes API Server. 

Cấu hình xác thực cho client

Trong bước này, chúng ta sẽ tạo kubeconfig cho kubelet và user admin.

File cấu hình Kubernetes cho kubelet

Khi tạo kubeconfig cho kubelet, cần sử dụng client certificate tương ứng với từng node. Điều này đảm bảo kubelet được nhận diện đúng bởi Node Authorizer và được cấp quyền phù hợp trong cluster.

Ghi chú: Các lệnh dưới đây cần được thực thi trong cùng thư mục đã sử dụng để tạo TLS certificates ở phần trước (mục III.5 – Cấp phát CA và tạo chứng chỉ TLS).

Tiếp theo, chúng ta sẽ tạo kubeconfig cho các worker node node-0 và node-1:

for host in node-0 node-1; do

  kubectl config set-cluster kubernetes-the-hard-way \

--certificate-authority=ca.crt \

--embed-certs=true \

    --server=https://server.kubernetes.local:6443 \

--kubeconfig=${host}.kubeconfig

 

  kubectl config set-credentials system:node:${host} \

--client-certificate=${host}.crt \

--client-key=${host}.key \

--embed-certs=true \

--kubeconfig=${host}.kubeconfig

 

  kubectl config set-context default \

--cluster=kubernetes-the-hard-way \

--user=system:node:${host} \

--kubeconfig=${host}.kubeconfig

 

  kubectl config use-context default \

--kubeconfig=${host}.kubeconfig

done

Kết quả sẽ có 2 file

node-0.kubeconfig

node-1.kubeconfig

File cấu hình Kubernetes cho kube-proxy

Tạo file kubeconfig cho dịch vụ kube-proxy:

  kubectl config set-cluster kubernetes-the-hard-way \

--certificate-authority=ca.crt \

--embed-certs=true \

    --server=https://server.kubernetes.local:6443 \

--kubeconfig=kube-proxy.kubeconfig

 

  kubectl config set-credentials system:kube-proxy \

--client-certificate=kube-proxy.crt \

--client-key=kube-proxy.key \

--embed-certs=true \

--kubeconfig=kube-proxy.kubeconfig

 

  kubectl config set-context default \

--cluster=kubernetes-the-hard-way \

--user=system:kube-proxy \

--kubeconfig=kube-proxy.kubeconfig

 

  kubectl config use-context default \

--kubeconfig=kube-proxy.kubeconfig

Kết quả sẽ được file

kube-proxy.kubeconfig

File cấu hình Kubernetes cho kube-controller-manager

Tạo file kubeconfig cho dịch vụ kube-controller-manager:

  kubectl config set-cluster kubernetes-the-hard-way \

--certificate-authority=ca.crt \

--embed-certs=true \

    --server=https://server.kubernetes.local:6443 \

    --kubeconfig=kube-controller-manager.kubeconfig

 

  kubectl config set-credentials system:kube-controller-manager \

    --client-certificate=kube-controller-manager.crt \

--client-key=kube-controller-manager.key \

--embed-certs=true \

    --kubeconfig=kube-controller-manager.kubeconfig

 

  kubectl config set-context default \

--cluster=kubernetes-the-hard-way \

--user=system:kube-controller-manager \

    --kubeconfig=kube-controller-manager.kubeconfig

 

  kubectl config use-context default \

    --kubeconfig=kube-controller-manager.kubeconfig

Kết quả thu được file

kube-controller-manager.kubeconfig

File cấu hình Kubernetes cho kube-scheduler

Tạo file kubeconfig cho dịch vụ kube-scheduler:

  kubectl config set-cluster kubernetes-the-hard-way \

--certificate-authority=ca.crt \

--embed-certs=true \

    --server=https://server.kubernetes.local:6443 \

--kubeconfig=kube-scheduler.kubeconfig

 

  kubectl config set-credentials system:kube-scheduler \

--client-certificate=kube-scheduler.crt \

--client-key=kube-scheduler.key \

--embed-certs=true \

--kubeconfig=kube-scheduler.kubeconfig

 

  kubectl config set-context default \

--cluster=kubernetes-the-hard-way \

--user=system:kube-scheduler \

--kubeconfig=kube-scheduler.kubeconfig

 

  kubectl config use-context default \

--kubeconfig=kube-scheduler.kubeconfig

Kết quả thu được file

kube-scheduler.kubeconfig

File cấu hình Kubernetes cho admin

Tạo file kubeconfig cho user admin:

  kubectl config set-cluster kubernetes-the-hard-way \

--certificate-authority=ca.crt \

--embed-certs=true \

--server=https://127.0.0.1:6443 \

--kubeconfig=admin.kubeconfig

 

  kubectl config set-credentials admin \

--client-certificate=admin.crt \

--client-key=admin.key \

--embed-certs=true \

--kubeconfig=admin.kubeconfig

 

  kubectl config set-context default \

--cluster=kubernetes-the-hard-way \

--user=admin \

--kubeconfig=admin.kubeconfig

 

  kubectl config use-context default \

--kubeconfig=admin.kubeconfig

Kết quả thu được file

admin.kubeconfig

Tiếp theo, thực hiện copy kubeconfig của kube-controller-manager và kube-scheduler sang máy server: 

for host in node-0 node-1; do

  ssh root@${host} "mkdir -p /var/lib/{kube-proxy,kubelet}"

 

  scp kube-proxy.kubeconfig \

root@${host}:/var/lib/kube-proxy/kubeconfig \

 

  scp ${host}.kubeconfig \

root@${host}:/var/lib/kubelet/kubeconfig

done

Copy các file kubeconfig tương ứng của kube-controller-manager và kube-scheduler sang máy server: 

scp admin.kubeconfig \

  kube-controller-manager.kubeconfig \

  kube-scheduler.kubeconfig \

Tạo cấu hình và khóa mã hóa dữ liệu (Data Encryption Config & Key)

Kubernetes lưu trữ nhiều loại dữ liệu trong etcd, bao gồm trạng thái cluster, cấu hình và các thông tin nhạy cảm như Secrets. Để bảo vệ dữ liệu này, Kubernetes hỗ trợ cơ chế mã hóa ở mức lưu trữ (encryption at rest).

Trong phần này, chúng ta sẽ tạo encryption key và file cấu hình mã hóa để sử dụng cho Kubernetes Secrets.

Tạo một khóa mã hóa:

export ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)

Lệnh trên sinh một chuỗi ngẫu nhiên dài 32 byte và mã hóa dưới dạng base64, được sử dụng làm khóa mã hóa. 

Tiếp tục thực hiện tạo Tạo file cấu hình encryption-config.yaml: 

envsubst < configs/encryption-config.yaml \

  > encryption-config.yaml

Lệnh envsubst sẽ thay thế các biến môi trường (ví dụ ENCRYPTION_KEY) trong file mẫu bằng giá trị thực và tạo ra file cấu hình hoàn chỉnh. 

Cuối cùng, thực hiện copy file cấu hình mã hóa encryption-config.yaml tới các instance controller (server node) :

scp encryption-config.yaml root@server:~/

Sau khi hoàn thành bước này, các thành phần cần thiết cho cơ chế xác thực và mã hóa dữ liệu trong cluster đã được thiết lập đầy đủ.

Trong phần tiếp theo, chúng ta sẽ bắt đầu bootstrap control plane và đưa Kubernetes cluster vào trạng thái sẵn sàng vận hành.

#CloudWave Radar
#CloudWave Radar
Sovereign Cloud không chỉ là đặt máy chủ trong nước. Với bối cảnh pháp lý dữ liệu mới tại Việt Nam, đây đang trở thành bài toán hạ tầng quan trọng cho doanh nghiệp Việt và doanh nghiệp nước ngoài hoạt động tại Việt Nam
Sovereign Cloud - Đám mây chủ quyền là gì? Và vì sao doanh nghiệp hoạt động tại Việt Nam nên quan tâm từ bây giờ?
Tiếp tục đọc