跳到主要内容

Kubeconfig制作实践

生成 Namespace 级别的 Kubeconfig

需求描述:希望为一个特定的Namespace创建一个Kubeconfig文件,使得持有该Kubeconfig的用户只能访问该Namespace中的资源,而无法访问其他Namespace。这种做法通常用于多租户环境,确保不同团队或用户之间的资源通过Namespace进行隔离。

以下操作流程使用 X.509 客户端证书 + Kubernetes CSR + RBAC,为一个人创建只在指定 Namespace 内生效的 kubeconfig。适合多租户、个人开发空间等场景。

目标与边界

以用户 zhangsan 为例,最终得到:

  • Kubernetes 用户:zhangsan
  • Kubernetes 组:dev
  • Namespace:zhangsan
  • 权限:该 Namespace 中全部 API Group、全部资源、全部动作
  • kubeconfig:内嵌集群 CA、客户端证书和客户端私钥;默认 context 不指定 Namespace

apiGroups: ["*"]resources: ["*"]verbs: ["*"] 是 Namespace 内管理员权限,包含读取 Secret,以及创建或修改该 Namespace 内的 Role 和 RoleBinding。生产环境优先按最小权限替换为具体资源和动作。

该 Role 不会授权 Node、Namespace、ClusterRole、ClusterRoleBinding 等集群级资源,也不会授权其他 Namespace。

原理

Kubernetes 对客户端证书 Subject 的常规映射如下:

CN=zhangsan -> User/zhangsan
O=dev -> Group/dev

system:authenticated 不需要写入证书;它由 API Server 为所有成功认证的请求自动添加。

前置条件

准备一个具备集群管理权限的 kubeconfig,并确认当前 context 正确:

kubectl config current-context
kubectl auth whoami

最少需要以下 Kubernetes API 权限:

  • getcreate Namespace;
  • creategetdelete certificatesigningrequests.certificates.k8s.io
  • update certificatesigningrequests/approval.certificates.k8s.io
  • 在目标 Namespace 对 Role、RoleBinding 的 getcreateupdate
  • 目标集群启用 kubernetes.io/kube-apiserver-client CSR signer。

可用下面的命令逐项验证:

kubectl auth can-i create namespaces
kubectl auth can-i create certificatesigningrequests.certificates.k8s.io
kubectl auth can-i update certificatesigningrequests/approval.certificates.k8s.io
kubectl auth can-i create roles.rbac.authorization.k8s.io -n zhangsan
kubectl auth can-i create rolebindings.rbac.authorization.k8s.io -n zhangsan

步骤

确定用户、组和 Namespace

USER_NAME='zhangsan'
USER_GROUP='dev'
TARGET_NAMESPACE="$USER_NAME"
VALID_DAYS=365 # 证书有效期
CSR_NAME="${USER_NAME}-csr"
WORK_DIR="$(mktemp -d)"

创建 Namespace

kubectl get namespace "$TARGET_NAMESPACE" >/dev/null 2>&1 || \
kubectl create namespace "$TARGET_NAMESPACE"

生成私钥与 CSR

umask 077
openssl genrsa -out "$WORK_DIR/${USER_NAME}.key" 2048
openssl req -new \
-key "$WORK_DIR/${USER_NAME}.key" \
-out "$WORK_DIR/${USER_NAME}.csr" \
-subj "/CN=${USER_NAME}/O=${USER_GROUP}"

提交并批准 CSR

创建 CSR,指定客户端证书签发器、client auth 用途和一年有效期:

CSR_REQUEST="$(base64 < "$WORK_DIR/${USER_NAME}.csr" | tr -d '\n')"

cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: ${CSR_NAME}
labels:
user: ${USER_NAME}
spec:
request: ${CSR_REQUEST}
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 31536000
usages:
- client auth
EOF

kubectl certificate approve "$CSR_NAME"

等待签发并导出证书:

until CERTIFICATE="$(kubectl get csr "$CSR_NAME" -o jsonpath='{.status.certificate}')" && [ -n "$CERTIFICATE" ]; do
sleep 1
done

printf '%s' "$CERTIFICATE" | base64 -d > "$WORK_DIR/${USER_NAME}.crt"
openssl x509 -in "$WORK_DIR/${USER_NAME}.crt" -noout -subject -issuer -dates

输出应包含 subject=O=dev, CN=zhangsan,且证书用途为 TLS Client Authentication。

创建 Namespace 内的 RBAC

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: ${USER_NAME}-role
namespace: ${TARGET_NAMESPACE}
labels:
user: ${USER_NAME}
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ${USER_NAME}-binding
namespace: ${TARGET_NAMESPACE}
labels:
user: ${USER_NAME}
subjects:
- kind: User
name: ${USER_NAME}
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: ${USER_NAME}-role
apiGroup: rbac.authorization.k8s.io
EOF

RoleBinding 的主体是 User/zhangsan,不是 Group/devdev 只是证书携带的附加组身份。若要通过组授权,应将 subjects.kind 改为 Group

从管理员 kubeconfig 提取 API Server 与 CA

使用 --flatten 兼容 CA 以外部文件引用的管理员 kubeconfig:

ADMIN_CONTEXT="$(kubectl config current-context)"
API_SERVER="$(kubectl --context "$ADMIN_CONTEXT" config view --raw --flatten --minify \
-o jsonpath='{.clusters[0].cluster.server}')"

kubectl --context "$ADMIN_CONTEXT" config view --raw --flatten --minify \
-o jsonpath='{.clusters[0].cluster.certificate-authority-data}' \
| base64 -d > "$WORK_DIR/ca.crt"

组装用户 kubeconfig

OUTPUT_FILE="$PWD/kubeconfig-${USER_NAME}-$(date +%Y%m%d-%H%M%S).yaml"

KUBECONFIG="$OUTPUT_FILE" kubectl config set-cluster opsis-cluster \
--server="$API_SERVER" \
--certificate-authority="$WORK_DIR/ca.crt" \
--embed-certs=true

KUBECONFIG="$OUTPUT_FILE" kubectl config set-credentials "$USER_NAME" \
--client-certificate="$WORK_DIR/${USER_NAME}.crt" \
--client-key="$WORK_DIR/${USER_NAME}.key" \
--embed-certs=true

KUBECONFIG="$OUTPUT_FILE" kubectl config set-context "${USER_NAME}@opsis-cluster" \
--cluster=opsis-cluster \
--user="$USER_NAME"

KUBECONFIG="$OUTPUT_FILE" kubectl config use-context "${USER_NAME}@opsis-cluster"
chmod 600 "$OUTPUT_FILE"

此处不设置 context 的默认 Namespace,与“用户可显式访问被授权 Namespace”的接口逻辑一致。若希望日常命令默认落到个人空间,可在 set-context 时增加:

--namespace="$TARGET_NAMESPACE"

验证

kubectl --kubeconfig "$OUTPUT_FILE" auth whoami
kubectl --kubeconfig "$OUTPUT_FILE" auth can-i --list -n "$TARGET_NAMESPACE"
kubectl --kubeconfig "$OUTPUT_FILE" auth can-i get pods -n "$TARGET_NAMESPACE"
kubectl --kubeconfig "$OUTPUT_FILE" auth can-i get pods -n default
kubectl --kubeconfig "$OUTPUT_FILE" auth can-i get nodes

预期结果:

  • 身份为 Username: zhangsan,组包含 devsystem:authenticated
  • zhangsan 中出现 *.* / [*]
  • default 的 Pod 访问为 no
  • 集群级 nodes 访问为 no

续期、回收

证书续期

重新执行“生成私钥与 CSR”至“组装用户 kubeconfig”会生成新的密钥和证书,并更新同名 Role/RoleBinding。旧证书在自然过期前仍然有效,因此这是“新增一份凭据”,不是替换或吊销旧凭据。

回收权限

要立即阻止该用户继续操作该 Namespace,删除 RoleBinding:

kubectl -n "$TARGET_NAMESPACE" delete rolebinding "${USER_NAME}-binding"

随后可按需删除 Role、Namespace 和 CSR 记录:

kubectl -n "$TARGET_NAMESPACE" delete role "${USER_NAME}-role"
kubectl delete csr "$CSR_NAME" --ignore-not-found

删除 CSR 不会撤销已经下发的证书;恢复 RoleBinding 后,未过期的旧 kubeconfig 会再次获得权限。若必须在认证层立即使某张证书失效,需要集群 CA 的证书吊销/轮换机制,或采用可撤销的短期 Token/外部身份认证方案。