管理集群中的 TLS
在使用二进制文件部署 Kubernetes 集群时,TLS 证书配置往往是最容易出错的环节。理解 Kubernetes 集群中 TLS 证书的管理机制,对于构建安全可靠的集群至关重要。
集群 TLS 架构概述
每个 Kubernetes 集群都有一个集群根证书颁发机构(CA),它是整个集群安全通信的基础。集群中的各个组件通过这个 CA 来建立相互信任:
- API Server 验证:集群组件使用 CA 来验证 API Server 的证书
- 客户端验证:API Server 验证 kubelet 等客户端证书
- 证书分发:CA 证书包被分发到集群中的每个节点
- 服务账户集成:CA 证书作为 Secret 自动挂载到默认 Service Account
应用程序可以通过 certificates.k8s.io
API 请求证书签名,这类似于 ACME 协议的工作方式。
在 Pod 中建立 TLS 信任
自动挂载的 CA 证书
Kubernetes 会自动将 CA 证书包挂载到每个 Pod 中:
- 挂载路径:
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
- 适用范围:使用默认 Service Account 的 Pod
- 自动更新:证书轮换时自动更新
在应用程序中使用 CA 证书
以 Go 语言为例,可以这样加载 CA 证书:
package main
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"log"
)
func loadCACert() *x509.CertPool {
caCert, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
return caCertPool
}
func main() {
tlsConfig := &tls.Config{
RootCAs: loadCACert(),
}
// 使用 tlsConfig 进行 HTTPS 通信
}
自定义 Service Account
如果不使用默认 Service Account,需要:
- 创建包含 CA 证书的 ConfigMap
- 将 ConfigMap 挂载到 Pod 中
- 在应用程序中指定正确的证书路径
创建和管理证书签名请求
环境准备
安装必要的工具:
# 安装 cfssl
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssl_1.6.4_linux_amd64 -o cfssl
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssljson_1.6.4_linux_amd64 -o cfssljson
chmod +x cfssl cfssljson
sudo mv cfssl cfssljson /usr/local/bin/
生成私钥和证书签名请求
创建配置文件并生成 CSR:
cat <<EOF | cfssl genkey - | cfssljson -bare server
{
"hosts": [
"my-svc.my-namespace.svc.cluster.local",
"my-pod.my-namespace.pod.cluster.local",
"172.168.0.24",
"10.0.34.2"
],
"CN": "my-pod.my-namespace.pod.cluster.local",
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"C": "CN",
"ST": "Beijing",
"L": "Beijing",
"O": "example",
"OU": "example"
}
]
}
EOF
配置说明:
hosts
:包含服务 DNS 名称、Pod DNS 名称和 IP 地址CN
:通用名称,通常使用主要的 DNS 名称key
:密钥算法和长度names
:证书主体信息
生成成功后会看到类似输出:
2023/10/21 06:48:17 [INFO] generate received request
2023/10/21 06:48:17 [INFO] received CSR
2023/10/21 06:48:17 [INFO] generating key: ecdsa-256
2023/10/21 06:48:17 [INFO] encoded CSR
提交证书签名请求
创建 CSR 资源并提交到 Kubernetes API:
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: my-svc.my-namespace
spec:
request: $(cat server.csr | base64 | tr -d '\n')
signerName: kubernetes.io/kubelet-serving
usages:
- digital signature
- key encipherment
- server auth
EOF
重要变更:
- 在 Kubernetes 1.19+ 版本中,必须指定
signerName
- 常用的 signer 包括:
kubernetes.io/kube-apiserver-client
:客户端证书kubernetes.io/kubelet-serving
:服务端证书kubernetes.io/legacy-unknown
:兼容性 signer
查看证书签名请求状态
kubectl get csr my-svc.my-namespace
kubectl describe csr my-svc.my-namespace
输出示例:
Name: my-svc.my-namespace
Labels: <none>
Annotations: <none>
CreationTimestamp: Tue, 21 Oct 2023 07:03:51 +0800
Requesting User: system:node:worker-1
Requested Signers: kubernetes.io/kubelet-serving
Status: Pending
Subject:
Common Name: my-pod.my-namespace.pod.cluster.local
Serial Number:
Subject Alternative Names:
DNS Names: my-svc.my-namespace.svc.cluster.local
my-pod.my-namespace.pod.cluster.local
IP Addresses: 172.168.0.24
10.0.34.2
Events: <none>
证书批准和使用
手动批准证书
具有适当权限的管理员可以手动批准或拒绝 CSR:
# 批准证书
kubectl certificate approve my-svc.my-namespace
# 拒绝证书
kubectl certificate deny my-svc.my-namespace
获取签名证书
证书批准后,可以提取签名证书:
kubectl get csr my-svc.my-namespace -o jsonpath='{.status.certificate}' | base64 -d > server.crt
验证证书
验证生成的证书内容:
openssl x509 -in server.crt -text -noout
使用证书
现在可以使用 server.crt
和 server-key.pem
启动 HTTPS 服务:
# 启动简单的 HTTPS 服务器
openssl s_server -cert server.crt -key server-key.pem -port 8443
自动化证书管理
自动批准策略
Kubernetes 提供了几种自动批准机制:
内置批准器:
csrapproving
controller 自动批准符合条件的 CSR- 主要用于 kubelet 客户端证书
自定义批准器:
- 基于策略的自动批准
- 集成外部 CA 系统
CSR 批准最佳实践
批准 CSR 时需要验证两个关键要求:
私钥控制验证:
- 确认请求者拥有对应的私钥
- 防止第三方伪造请求
授权验证:
- 确认请求者有权获取该证书
- 验证证书用途的合法性
示例:自动批准脚本
#!/bin/bash
# 简单的 CSR 批准脚本
CSR_NAME=$1
if [ -z "$CSR_NAME" ]; then
echo "Usage: $0 <csr-name>"
exit 1
fi
# 检查 CSR 状态
STATUS=$(kubectl get csr $CSR_NAME -o jsonpath='{.status.conditions[0].type}' 2>/dev/null)
if [ "$STATUS" = "Pending" ]; then
echo "Approving CSR: $CSR_NAME"
kubectl certificate approve $CSR_NAME
else
echo "CSR $CSR_NAME is not in Pending state: $STATUS"
fi
集群管理员配置
Controller Manager 配置
要启用内置的证书签名功能,需要配置 Controller Manager:
apiVersion: v1
kind: Pod
metadata:
name: kube-controller-manager
spec:
containers:
- name: kube-controller-manager
image: k8s.gcr.io/kube-controller-manager:v1.28.0
command:
- kube-controller-manager
- --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
- --cluster-signing-key-file=/etc/kubernetes/pki/ca.key
- --cluster-signing-duration=8760h # 1 年有效期
volumeMounts:
- name: ca-certs
mountPath: /etc/kubernetes/pki
readOnly: true
volumes:
- name: ca-certs
hostPath:
path: /etc/kubernetes/pki
证书轮换策略
建议配置合理的证书轮换策略:
- 证书有效期:通常设置为 1 年
- 轮换时间:在证书到期前 30 天开始轮换
- 自动化程度:尽可能实现自动化轮换
故障排查
常见问题
CSR 长时间处于 Pending 状态:
- 检查 Controller Manager 配置
- 验证 CA 证书和私钥路径
证书验证失败:
- 检查 SAN(Subject Alternative Names)配置
- 确认 DNS 名称和 IP 地址正确
权限问题:
- 确认用户有创建 CSR 的权限
- 检查 RBAC 配置
调试命令
# 查看 CSR 详细信息
kubectl describe csr <csr-name>
# 查看 Controller Manager 日志
kubectl logs -n kube-system kube-controller-manager-<node-name>
# 验证证书链
openssl verify -CAfile /etc/kubernetes/pki/ca.crt server.crt
通过合理配置和管理 TLS 证书,可以确保 Kubernetes 集群的安全通信,为应用程序提供可靠的加密基础。