Kubernetes 安全加固与审计 Hands-on
验证:未记录
- 适用版本
- 未记录
- 验证范围
- 尚未登记验证记录。
本文缩写(英文全称与中文含义)
| 缩写 | 英文全称/名称 | 中文含义 |
|---|---|---|
| API | Application Programming Interface | 应用程序编程接口 |
| KMS | Key Management Service | 密钥管理服务 |
| PSA | Pod Security Admission | Kubernetes Pod 安全准入 |
| RBAC | Role-Based Access Control | 基于角色的访问控制 |
| TLS | Transport Layer Security | 传输层安全协议 |
前置:权限篇、预生产环境、三控制面自建集群;托管集群不修改 API Server manifest,使用平台的审计/加密接口。本文将 PSA、审计、静态加密分别验收;Kyverno/Falco 不能替代这些控制。
1. 逐步启用 Pod Security Admission#
在 orders 先 warn/audit,再 enforce。版本固定为当前经验证 minor:
kubectl label ns orders pod-security.kubernetes.io/warn=restricted pod-security.kubernetes.io/warn-version="v$K8S_MINOR" pod-security.kubernetes.io/audit=restricted pod-security.kubernetes.io/audit-version="v$K8S_MINOR" --overwrite
kubectl -n orders get pods -o yaml > "$EVIDENCE/orders-pods.yaml"
# 修正告警后,再启用 enforce。
kubectl label ns orders pod-security.kubernetes.io/enforce=restricted pod-security.kubernetes.io/enforce-version="v$K8S_MINOR" --overwrite用负向测试确认拒绝,server dry-run 不会真的启动特权容器:
kubectl -n orders run forbidden-privileged --image="$DEBUG_IMAGE" --privileged --restart=Never --dry-run=server -o yaml应被 Forbidden 拒绝。已有 Pod 不会因此被驱逐;还需检查控制器下一次创建 Pod 是否合规。系统 Agent 例外按专用 namespace/身份评审,不把所有系统命名空间永久豁免审查。
2. 自建控制面启用审计#
每个控制面 /etc/kubernetes/audit-policy.yaml:
apiVersion: audit.k8s.io/v1
kind: Policy
omitStages: [RequestReceived]
rules:
- level: Metadata
resources:
- group: ''
resources: [secrets, serviceaccounts/token]
- level: Metadata只记 Metadata,避免 RequestResponse 记录 Secret/token 内容;后续按调查需求对非敏感资源细化。先在一台控制面备份 manifest 到 /etc/kubernetes/manifests 之外,添加参数:
--audit-policy-file=/etc/kubernetes/audit-policy.yaml
--audit-log-path=/var/log/kubernetes/audit.log
--audit-log-maxage=7
--audit-log-maxbackup=10
--audit-log-maxsize=100在 kube-apiserver Pod 中添加对应挂载;下面为需合并的 spec 片段:
volumes:
- name: audit-policy
hostPath: {path: /etc/kubernetes/audit-policy.yaml, type: File}
- name: audit-log
hostPath: {path: /var/log/kubernetes, type: DirectoryOrCreate}容器的 volumeMounts 合并以下内容,policy 只读,日志目录可写:
volumeMounts:
- {name: audit-policy, mountPath: /etc/kubernetes/audit-policy.yaml, readOnly: true}
- {name: audit-log, mountPath: /var/log/kubernetes}保存后 kubelet 重建 API Server;该成员 /readyz 成功再滚动下一台。采集日志到企业存储,限制访问并配置保留/防篡改。
kubectl -n orders get pods
sudo tail -n 20 /var/log/kubernetes/audit.log | jq '{auditID,user:.user.username,verb,objectRef,responseStatus}'验收能查到操作者、资源、动词、状态和 auditID,无凭据正文。失败时恢复本机旧 manifest/挂载,其他控制面保持服务。
3. Secret 静态加密与轮换实验#
企业优先 KMS v2;下面用本地 aescbc 演练读写/轮换机制,不是企业 KMS 替代品。先备份 etcd 和所有已有加密配置;已有配置保留全部旧 reader,不能覆盖成本文示例。
全新未加密集群采用两阶段上线:先让所有 API Server 具备读取新格式的能力,再启用加密写入。如果第一台先开始加密,尚未加载密钥的其他成员可能读失败。配置只在一台可信控制面生成,再安全分发;禁止每台各生成一个 key。
sudo install -d -m 700 /etc/kubernetes/encryption
sudo sh -c 'set -eu; set -C; umask 077; python3 - <<"PYKEY" > /etc/kubernetes/encryption/config.yaml
import base64, os, json
print(json.dumps({"apiVersion":"apiserver.config.k8s.io/v1","kind":"EncryptionConfiguration","resources":[{"resources":["secrets"],"providers":[{"identity":{}},{"aescbc":{"keys":[{"name":"key1","secret":base64.b64encode(os.urandom(32)).decode()}]}}]}]}))
PYKEY'通过安全渠道将同一配置分发所有控制面,权限 0600;挂载 /etc/kubernetes/encryption 只读,添加 --encryption-provider-config=/etc/kubernetes/encryption/config.yaml,在 spec.volumes 添加 name: encryption-config、hostPath.path: /etc/kubernetes/encryption、hostPath.type: Directory;容器 volumeMounts 挂载同名目录并设置 readOnly: true。按 证书维护篇逐台重建并检查可读。第一阶段 identity 在前,仍写明文;检查各成员配置摘要一致、进程已重新加载且本地 readyz 成功,全部完成才继续。
第二阶段在每台把同一个 key1 的 aescbc provider 移到第一位,identity 保留在后。不要重新运行密钥生成器;它使用禁止覆盖模式保护已有文件。先在一台生成第二阶段文件,安全分发,再一次一台重启:
sudo python3 - <<'PYSWITCH'
import json, os
from pathlib import Path
p = Path('/etc/kubernetes/encryption/config.yaml')
config = json.loads(p.read_text())
providers = config['resources'][0]['providers']
assert len(providers) == 2 and 'identity' in providers[0] and 'aescbc' in providers[1]
config['resources'][0]['providers'] = [providers[1], providers[0]]
fd = os.open(str(p) + '.next', os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, 'w') as f:
json.dump(config, f)
PYSWITCH
# 审核并备份旧配置后,将 .next 作为第二阶段配置安全分发到所有成员。每台用 sudo mv /etc/kubernetes/encryption/config.yaml.next /etc/kubernetes/encryption/config.yaml 原子替换后重启本机 API Server;其余成员此时已具备 key1 reader。逐台验证后,创建探针,并通过各成员的直接地址读取;identity 在后负责兼容旧明文。已有 Secret 还需受控重写才能全部加密。
kubectl -n orders create secret generic encryption-check --from-literal=marker=non-sensitive-probe
kubectl -n orders get secret encryption-check -o jsonpath='{.metadata.name}{"\n"}'在控制面用 etcd TLS 连接参数读取 /registry/secrets/orders/encryption-check,只对这个无敏感探针检查字节前缀应为 k8s:enc:aescbc:v1:;不要 dump 全库到日志。
轮换顺序:①给所有 API Server 配置加入 key2,key1 仍第一;②确认全员能读 key2 后,逐台将 key2 移第一;③先重写探针并验证,再分批重写其他 Secret;④全部数据与备份恢复流程验证后才删除旧 reader。重写会与业务更新竞争,要处理 resourceVersion 冲突,不能盲目全量 get|replace。
kubectl -n orders get secret encryption-check -o json | kubectl replace -f -
kubectl -n orders delete secret encryption-check旧 etcd 备份可能仍依赖旧 key,按保留期封存密钥;丢失 key 不能靠恢复 manifest 解密。回退时保留所有已写入数据所需的 reader。
4. 上线门禁#
验证 restricted、最小 RBAC、节点 metadata 隔离、NetworkPolicy、审计可查询、Secret 加密恢复、镜像签名;扫描工具发现的问题按适用性处理,不把扫描分数当安全保证。