Agent-almanac manage-kubernetes-secrets
git clone https://github.com/pjt222/agent-almanac
T=$(mktemp -d) && git clone --depth=1 https://github.com/pjt222/agent-almanac "$T" && mkdir -p ~/.claude/skills && cp -r "$T/i18n/zh-CN/skills/manage-kubernetes-secrets" ~/.claude/skills/pjt222-agent-almanac-manage-kubernetes-secrets-9e390b && rm -rf "$T"
i18n/zh-CN/skills/manage-kubernetes-secrets/SKILL.md管理 Kubernetes 密钥
为 Kubernetes 实现生产级密钥管理,支持加密、轮换和与外部密钥存储的集成。
适用场景
- 为 Kubernetes 应用存储敏感配置(API 密钥、密码、令牌)
- 实现密钥需要提交至版本控制的 GitOps 工作流
- 将 Kubernetes 与 AWS Secrets Manager、Azure Key Vault、GCP Secret Manager 集成
- 不停机轮换凭证和证书
- 跨命名空间和团队强制执行密钥访问最小权限
- 从明文 Secret 迁移至加密或外部管理方案
输入
- 必填:具有管理员访问权限的 Kubernetes 集群
- 必填:待管理的密钥(数据库凭证、API 密钥、TLS 证书)
- 可选:云密钥管理器(AWS Secrets Manager、Azure Key Vault、GCP Secret Manager)
- 可选:用于生成 TLS 证书的证书颁发机构
- 可选:用于 SealedSecrets 的 GitOps 仓库
- 可选:用于静态加密的密钥管理服务(KMS)
步骤
完整配置文件和模板请参阅扩展示例。
第 1 步:启用 Kubernetes 密钥静态加密
使用 KMS 或本地加密配置 Secret 的静态加密。
# For AWS EKS, enable secrets encryption with KMS cat > encryption-config.yaml <<EOF apiVersion: apiserver.config.k8s.io/v1 kind: EncryptionConfiguration resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: $(head -c 32 /dev/urandom | base64) - identity: {} EOF # For self-hosted clusters, configure API server # Add to kube-apiserver flags: # --encryption-provider-config=/etc/kubernetes/encryption-config.yaml # Verify encryption status kubectl get secrets -A -o json | jq '.items[] | select(.metadata.name != "default-token") | .metadata.name' # Encrypt existing secrets by reading and rewriting kubectl get secrets --all-namespaces -o json | kubectl replace -f - # Verify a secret is encrypted at rest # Check etcd directly (requires etcd access) ETCDCTL_API=3 etcdctl get /registry/secrets/default/my-secret --print-value-only | hexdump -C
对于云托管 Kubernetes:
# AWS EKS - Create KMS key aws kms create-key --description "EKS secrets encryption" KMS_KEY_ARN=$(aws kms describe-key --key-id alias/eks-secrets --query 'KeyMetadata.Arn' --output text) # Enable encryption on EKS cluster aws eks associate-encryption-config \ --cluster-name my-cluster \ --encryption-config "resources=secrets,provider={keyArn=$KMS_KEY_ARN}" # GKE - Enable application-layer secrets encryption gcloud container clusters update my-cluster \ --database-encryption-key projects/PROJECT_ID/locations/LOCATION/keyRings/RING_NAME/cryptoKeys/KEY_NAME # AKS - Encryption enabled by default with platform-managed keys # Optionally use customer-managed keys az aks update \ --name my-cluster \ --resource-group my-rg \ --enable-azure-keyvault-secrets-provider
预期结果: Secret 在 etcd 中静态加密。hexdump 显示加密数据而非明文。云托管集群已配置 KMS 集成。现有 Secret 的重新加密无错误完成。
失败处理: 对于 API server 启动失败,验证 encryption-config.yaml 语法和密钥格式(必须是 base64 编码的 32 字节密钥)。对于 KMS 错误,检查 IAM 权限是否允许 kms:Decrypt 和 kms:Encrypt。对于 etcd 访问问题,如果加密配置错误,使用备份/恢复程序进行恢复。
第 2 步:安装和配置 SealedSecrets 用于 GitOps
部署 Bitnami Sealed Secrets 控制器以加密密钥用于 Git 存储。
# Install Sealed Secrets controller kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml # Verify controller is running kubectl get pods -n kube-system -l name=sealed-secrets-controller # Install kubeseal CLI wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/kubeseal-0.24.0-linux-amd64.tar.gz tar xfz kubeseal-0.24.0-linux-amd64.tar.gz sudo install -m 755 kubeseal /usr/local/bin/kubeseal # Fetch public key for offline sealing kubeseal --fetch-cert \ --controller-namespace=kube-system \ --controller-name=sealed-secrets-controller \ > pub-cert.pem # Create a regular Secret (NOT applied to cluster yet) kubectl create secret generic mysecret \ --from-literal=username=admin \ --from-literal=password='sup3rs3cr3t!' \ --dry-run=client \ -o yaml > mysecret.yaml # Seal the secret kubeseal --format=yaml --cert=pub-cert.pem < mysecret.yaml > mysealedsecret.yaml # Inspect sealed secret (safe to commit to Git) cat mysealedsecret.yaml
已密封的 Secret 格式如下:
apiVersion: bitnami.com/v1alpha1 kind: SealedSecret metadata: name: mysecret namespace: default spec: encryptedData: username: AgA8V7f3q2... (encrypted data) password: AgBkXp9n1h... (encrypted data) template: metadata: name: mysecret namespace: default
应用并验证:
# Apply sealed secret to cluster kubectl apply -f mysealedsecret.yaml # Verify regular Secret was created automatically kubectl get secret mysecret -o yaml # Decode secret to verify values kubectl get secret mysecret -o jsonpath='{.data.username}' | base64 -d # Commit sealed secret to Git (safe, encrypted) git add mysealedsecret.yaml git commit -m "Add database credentials as sealed secret"
预期结果: Sealed Secrets 控制器在 kube-system 命名空间运行。已获取公共证书。kubeseal 使用公钥加密 Secret。应用至集群的 SealedSecret 自动创建解密后的 Secret。只有控制器才能解密(拥有私钥)。
失败处理: 对于加密错误,验证控制器是否运行,pub-cert.pem 是否有效。对于解密失败,使用
kubectl logs -n kube-system -l name=sealed-secrets-controller 检查控制器日志。对于命名空间不匹配错误,SealedSecret 默认为命名空间范围;对跨命名空间密钥使用 --scope cluster-wide。如果私钥丢失,SealedSecret 将无法解密;使用 kubectl get secret -n kube-system sealed-secrets-key -o yaml > sealed-secrets-backup.yaml 备份控制器密钥。
第 3 步:部署 External Secrets Operator 用于云密钥管理器
将 Kubernetes 与 AWS Secrets Manager、Azure Key Vault 或 GCP Secret Manager 集成。
# Install External Secrets Operator via Helm helm repo add external-secrets https://charts.external-secrets.io helm repo update helm install external-secrets \ external-secrets/external-secrets \ --namespace external-secrets-system \ --create-namespace # Verify operator is running kubectl get pods -n external-secrets-system # Create IAM role for AWS Secrets Manager (EKS with IRSA) cat > trust-policy.json <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/oidc.eks.REGION.amazonaws.com/id/OIDC_ID" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "oidc.eks.REGION.amazonaws.com/id/OIDC_ID:sub": "system:serviceaccount:default:external-secrets-sa" } } } ] } EOF aws iam create-role \ --role-name external-secrets-role \ --assume-role-policy-document file://trust-policy.json aws iam attach-role-policy \ --role-name external-secrets-role \ --policy-arn arn:aws:iam::aws:policy/SecretsManagerReadWrite # Create SecretStore referencing AWS Secrets Manager cat <<EOF | kubectl apply -f - apiVersion: external-secrets.io/v1beta1 kind: SecretStore metadata: name: aws-secretsmanager namespace: default spec: provider: aws: service: SecretsManager region: us-east-1 auth: jwt: serviceAccountRef: name: external-secrets-sa --- apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets-sa namespace: default annotations: eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT_ID:role/external-secrets-role EOF # Create secret in AWS Secrets Manager aws secretsmanager create-secret \ --name myapp/database \ --secret-string '{ "username":"dbadmin", "password":"dbpass123", "endpoint":"db.example.com:5432", "database":"myapp" }' # Create ExternalSecret to sync from AWS cat <<EOF | kubectl apply -f - apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: myapp-database namespace: default spec: refreshInterval: 1h secretStoreRef: name: aws-secretsmanager kind: SecretStore target: name: myapp-db-secret creationPolicy: Owner data: - secretKey: username remoteRef: key: myapp/database property: username - secretKey: password remoteRef: key: myapp/database property: password - secretKey: endpoint remoteRef: key: myapp/database property: endpoint EOF # Verify ExternalSecret synced kubectl get externalsecret myapp-database kubectl get secret myapp-db-secret -o yaml # Check synchronization status kubectl describe externalsecret myapp-database
对于 Azure Key Vault:
apiVersion: external-secrets.io/v1beta1 kind: SecretStore metadata: name: azure-keyvault namespace: default spec: provider: azurekv: authType: ManagedIdentity vaultUrl: "https://my-keyvault.vault.azure.net" tenantId: "tenant-id"
预期结果: External Secrets Operator 运行中。SecretStore 配置了云提供商凭证。ExternalSecret 资源通过从云密钥管理器拉取自动创建 Kubernetes Secret。密钥每小时刷新一次。云密钥管理器中的变更传播至集群。
失败处理: 对于认证错误,验证 IAM 角色/服务账户注解和信任策略是否允许 assume role。对于同步失败,使用
kubectl describe externalsecret 检查 ExternalSecret 状态。对于云中缺少密钥,验证密钥名称和 JSON 属性路径是否匹配。使用 aws secretsmanager get-secret-value --secret-id myapp/database 测试 AWS 凭证。
第 4 步:使用 cert-manager 实现证书管理
使用 cert-manager 自动化 TLS 证书的预置和续期。
# Install cert-manager kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml # Verify installation kubectl get pods -n cert-manager # ... (see EXAMPLES.md for complete configuration)
对于基于 Ingress 注解的证书颁发:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: myapp-ingress annotations: cert-manager.io/cluster-issuer: "letsencrypt-prod" # ... (see EXAMPLES.md for complete configuration)
预期结果: cert-manager 从 Let's Encrypt 获取证书。创建包含有效证书和私钥的 TLS Secret。证书在到期前自动续期。Ingress 使用证书进行 HTTPS 终止。
失败处理: 对于 ACME 挑战失败,验证 DNS 是否指向 http01 的 Ingress LoadBalancer IP,或 dns01 的 Route53 IAM 权限。对于速率限制错误,使用
letsencrypt-staging Issuer 进行测试。对于续期失败,使用 kubectl logs -n cert-manager deployment/cert-manager 检查 cert-manager 日志。使用 curl -v https://myapp.example.com 测试证书。
第 5 步:实现密钥轮换策略
通过版本管理和应用重启自动化密钥轮换。
# Enable automatic Pod restarts on Secret changes with Reloader kubectl apply -f https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml # Annotate Deployment to watch Secrets cat <<EOF | kubectl apply -f - apiVersion: apps/v1 # ... (see EXAMPLES.md for complete configuration)
验证轮换工作流:
# Manually trigger rotation kubectl create job --from=cronjob/secret-rotation manual-rotation-$(date +%s) # Watch for Secret update kubectl get secret myapp-db-secret -w # Verify Reloader triggered Pod restart kubectl get events --sort-by='.lastTimestamp' | grep Reloader # Check new Pods are using updated secret kubectl get pods -l app=myapp kubectl exec -it <pod-name> -- env | grep DB_PASSWORD
预期结果: Reloader 监控 Secret/ConfigMap 并在变更时重启 Pod。密钥轮换更新 AWS Secrets Manager,External Secrets Operator 同步至 Kubernetes,Reloader 触发滚动重启。应用无需手动干预即可获取新凭证。
失败处理: 对于 Reloader 未触发,验证注解语法并用
kubectl get pods -n default -l app=reloader-reloader 确认 Reloader 是否运行。对于 External Secrets 同步延迟,减少 refreshInterval 或使用 kubectl annotate externalsecret myapp-database force-sync="$(date +%s)" --overwrite 手动触发。对于轮换期间应用连接失败,在应用代码中实现优雅的密钥重新加载,或使用带重试逻辑的连接池。
第 6 步:实现密钥访问控制 RBAC
使用最小权限原则的 Kubernetes RBAC 限制密钥访问。
# Create namespace for sensitive workloads apiVersion: v1 kind: Namespace metadata: name: production --- # ... (see EXAMPLES.md for complete configuration)
测试 RBAC:
# Apply RBAC resources kubectl apply -f rbac.yaml # Test as application service account kubectl auth can-i get secret myapp-db-secret --as=system:serviceaccount:production:myapp -n production # Should return "yes" # ... (see EXAMPLES.md for complete configuration)
预期结果: 服务账户通过 resourceNames 对特定密钥具有只读访问权限。开发人员无法查看生产命名空间中的密钥。只有 secret-admins 组才能创建/更新/删除密钥。RBAC 拒绝记录在审计日志中。
失败处理: 对于访问拒绝错误,验证 RoleBinding subjects 是否与 ServiceAccount 名称和命名空间匹配。对于权限过宽的角色,移除通配符动词并添加 resourceNames 限制。对于审计日志缺失,在 API server 级别启用 Kubernetes 审计日志。在部署变更前使用
kubectl auth can-i 测试。
验证清单
- Secret 在 etcd 中静态加密(使用 etcdctl 或 KMS 验证)
- Sealed Secrets 控制器运行,公共证书已获取
- External Secrets Operator 从云密钥管理器同步
- TLS 证书由 cert-manager 颁发并自动续期
- 通过 Reloader 实现密钥轮换自动化和应用重启
- RBAC 策略对密钥强制执行最小权限访问
- Git 仓库或容器镜像中无明文密钥
- sealed-secrets 私钥的备份/恢复程序已测试
- 配置了密钥同步失败和到期的监控警报
常见问题
-
Git 历史中的密钥:提交明文密钥后再删除不会清除 Git 历史。使用 git-filter-repo 或 BFG 重写历史,轮换被泄露的密钥。
-
RBAC 权限过宽:授予命名空间中所有密钥的
权限。使用 resourceNames 将访问限制为特定密钥。get secrets -
无轮换策略:密钥从不轮换,增加了泄露的爆炸半径。使用 External Secrets Operator 或 CronJob 实现自动轮换。
-
缺少静态加密:Secret 在 etcd 中以明文存储。在存储敏感数据前启用加密提供商或 KMS 集成。
-
应用缓存密钥:应用在启动时读取一次密钥,之后不重新加载。实现信号处理(SIGHUP)或密钥文件变更的文件监视器。
-
External Secrets 刷新太慢:默认 1 小时刷新意味着密钥变更需要长达一小时才能传播。对关键密钥降低 refreshInterval,使用 webhook 实现即时更新。
-
无 sealed-secrets 密钥备份:控制器私钥丢失后,所有密封密钥将无法恢复。使用
备份并安全存储。kubectl get secret -n kube-system sealed-secrets-key -o yaml > backup.yaml -
证书续期失败:cert-manager 因 DNS/防火墙变更无法续期。使用 Prometheus 指标和警报监控证书到期。
相关技能
- 在 Deployment 和 StatefulSet 中使用密钥deploy-to-kubernetes
- 密钥访问验证的 OPA 策略enforce-policy-as-code
- 检测应用代码中的硬编码密钥security-audit-codebase
- Ingress 资源中的 TLS 证书使用configure-ingress-networking
- ArgoCD/Flux 流水线中的 SealedSecretsimplement-gitops-workflow