云上 Kubernetes 安全加固
适用场景
- 在生产环境部署新的托管 Kubernetes 集群并有安全要求时
- 在安全审计或渗透测试发现后,对现有 EKS、AKS 或 GKE 集群进行加固时
- 实施工作负载身份(Workload Identity)以消除 Pod 中静态云凭证时
- 跨命名空间强制执行 Pod 安全策略以防止容器逃逸时
- 集成运行时安全监控以检测容器级威胁时
不适用于:非 Kubernetes 容器部署(如 ECS Fargate 或 Azure Container Instances)、容器内的应用层安全(参见 securing-serverless-functions),或 CI/CD 流水线安全(参见 implementing-cloud-devsecops)。
前置条件
- 已在 EKS、AKS 或 GKE 上预配托管 Kubernetes 集群并具备管理员访问权限
- kubectl 已配置集群管理员凭据
- 熟悉 Kubernetes RBAC、命名空间和安全上下文
- 支持网络策略的容器网络接口插件(Calico、Cilium)
工作流程
步骤 1:强制执行 Pod 安全标准(Pod Security Standards)
在命名空间级别应用 Pod 安全准入(Pod Security Admission)标签,在生产命名空间中强制执行 Restricted 配置文件。Pod 安全策略(Pod Security Policies)已在 Kubernetes v1.25 中移除,由 Pod 安全准入取代。
# 生产命名空间,应用 Restricted Pod 安全标准
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
---
# 预演命名空间,应用 Baseline 强制执行
apiVersion: v1
kind: Namespace
metadata:
name: staging
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
# 符合 Restricted 配置文件的 Pod 规范
apiVersion: v1
kind: Pod
metadata:
name: secure-app
namespace: production
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: company/app:v2.1@sha256:abc123...
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
resources:
limits:
cpu: "500m"
memory: "256Mi"
requests:
cpu: "100m"
memory: "128Mi"
步骤 2:配置云原生工作负载身份
通过将 Kubernetes 服务账户绑定到云 IAM 角色,消除 Pod 中的静态云凭证。
# EKS:IAM Roles for Service Accounts (IRSA)
eksctl create iamserviceaccount \
--cluster production-cluster \
--namespace production \
--name web-app-sa \
--attach-policy-arn arn:aws:iam::123456789012:policy/WebAppS3ReadOnly \
--approve
# GKE:工作负载身份(Workload Identity)
gcloud iam service-accounts create web-app-sa \
--project=my-gcp-project
gcloud iam service-accounts add-iam-policy-binding \
web-app-sa@my-gcp-project.iam.gserviceaccount.com \
--role roles/storage.objectViewer \
--member "serviceAccount:my-gcp-project.svc.id.goog[production/web-app-sa]"
kubectl annotate serviceaccount web-app-sa \
--namespace production \
iam.gke.io/gcp-service-account=web-app-sa@my-gcp-project.iam.gserviceaccount.com
# AKS:Azure AD 工作负载身份
az identity create --name web-app-identity --resource-group production-rg
az identity federated-credential create \
--name web-app-federation \
--identity-name web-app-identity \
--resource-group production-rg \
--issuer "$(az aks show -n production-cluster -g production-rg --query oidcIssuerProfile.issuerUrl -o tsv)" \
--subject system:serviceaccount:production:web-app-sa
步骤 3:实施网络策略(Network Policy)
部署网络策略,遵循最小权限原则限制 Pod 间通信。默认情况下,Kubernetes 允许所有 Pod 相互通信。
# 生产命名空间中默认拒绝所有入站和出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# 仅允许 web-app 接收来自 ingress controller 的流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-to-web
namespace: production
spec:
podSelector:
matchLabels:
app: web-app
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
---
# 仅允许 web-app 连接到数据库
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-to-db
namespace: production
spec:
podSelector:
matchLabels:
app: web-app
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
步骤 4:按最小权限配置 RBAC
将 Kubernetes RBAC 角色限定在特定命名空间和资源范围内。非管理员用户应避免使用 ClusterRoleBindings。
# 开发者角色,限定在特定命名空间范围内
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer-role
namespace: staging
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "update", "patch"]
# 明确拒绝对 Secrets 的访问
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developer-binding
namespace: staging
subjects:
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer-role
apiGroup: rbac.authorization.k8s.io
步骤 5:部署镜像准入控制
使用准入控制器(Admission Controller)强制只允许部署来自受信任仓库的已签名镜像。使用 OPA/Gatekeeper 或 Kyverno 进行策略执行。
# Kyverno 策略:要求镜像来自已批准的仓库
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-image-registries
spec:
validationFailureAction: Enforce
rules:
- name: validate-registries
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "镜像必须来自已批准的仓库"
pattern:
spec:
containers:
- image: "123456789012.dkr.ecr.us-east-1.amazonaws.com/* | gcr.io/my-gcp-project/*"
---
# Kyverno 策略:要求使用镜像摘要(禁止使用可变标签)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-image-digest
spec:
validationFailureAction: Enforce
rules:
- name: require-digest
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "镜像必须使用摘要引用,而非标签"
pattern:
spec:
containers:
- image: "*@sha256:*"
步骤 6:启用运行时安全监控
部署运行时安全工具,检测容器内的异常行为,包括进程执行、文件系统修改和网络连接。
# 部署 Falco 进行运行时威胁检测
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
--namespace falco-system --create-namespace \
--set falcosidekick.enabled=true \
--set falcosidekick.config.slack.webhookurl="https://hooks.slack.com/services/xxx"
# 运行 kube-bench 进行 CIS Kubernetes 基准评估
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job-eks.yaml
kubectl logs -l app=kube-bench
核心概念
| 术语 | 定义 |
|---|---|
| Pod 安全标准(Pod Security Standards) | 三种配置文件(Privileged、Baseline、Restricted),通过 Pod 安全准入强制执行,控制 Pod 安全上下文能力 |
| 工作负载身份(Workload Identity) | 将 Kubernetes 服务账户绑定到云 IAM 角色的云原生机制,实现无凭证的云 API 访问(IRSA、GKE WI、AKS MI) |
| 网络策略(Network Policy) | 定义 Pod 间允许的入站和出站流量的 Kubernetes 资源,由 CNI 插件强制执行 |
| 准入控制器(Admission Controller) | 在持久化前拦截 API 请求的 Kubernetes 插件,根据安全策略验证或变更资源 |
| RBAC(基于角色的访问控制) | Kubernetes 中定义哪些身份可以在哪些命名空间中对哪些资源执行哪些操作(动词)的访问控制机制 |
| Seccomp 配置文件(Seccomp Profile) | Linux 内核特性,限制容器进程可以进行的系统调用,减少内核攻击面 |
| 服务网格(Service Mesh) | 为服务间通信提供双向 TLS、流量策略和可观测性的基础设施层(Istio、Linkerd) |
工具与系统
- Falco:使用内核级系统调用监控检测容器内异常行为的开源运行时安全引擎
- Kyverno:用于准入控制、资源变更和生成的 Kubernetes 原生策略引擎,基于安全策略运行
- kube-bench:CIS Kubernetes 基准评估工具,对照安全最佳实践检查集群配置
- Trivy:支持 SBOM 生成的容器镜像、文件系统和 Kubernetes 资源漏洞扫描工具
- Calico/Cilium:提供网络策略执行和高级网络安全功能(包括基于 eBPF 的监控)的 CNI 插件
常见场景
场景:通过被攻陷的容器镜像部署加密矿工
场景背景:GuardDuty 扩展威胁检测(Extended Threat Detection)生成 AttackSequence:EKS/CompromisedCluster 发现。一名开发者拉取了包含嵌入式 XMRig 加密矿工的公共 Docker 镜像,该矿工在容器启动时执行。
方法:
- 通过针对受影响 Pod 标签应用全拒绝网络策略,隔离该 Pod
- 捕获容器镜像摘要,使用 Trivy 扫描识别嵌入的二进制文件
- 审查 Kubernetes 审计日志,确定谁在何时部署了被攻陷的镜像
- 部署 Kyverno ClusterPolicy,仅允许来自已批准私有仓库的镜像
- 启用镜像摘要固定,防止标签变更攻击
- 部署 Falco,配置检测加密挖矿进程特征(/usr/bin/xmrig、stratum+tcp 连接)的规则
常见陷阱:在捕获镜像摘要和审计日志之前删除 Pod 会销毁取证证据。仅封锁特定镜像标签会让攻击者使用不同标签重新推送。
输出格式
Kubernetes 安全评估报告
=======================================
集群: production-cluster (EKS 1.29)
云提供商: AWS (us-east-1)
评估日期: 2025-02-23
工具: kube-bench v0.8.0 + 手动审查
CIS KUBERNETES 基准结果:
总控制项: 124
通过: 98 (79%)
失败: 18 (15%)
警告: 8 (6%)
严重发现:
[K8S-001] 3 个命名空间缺少 Pod 安全标准强制执行
命名空间: monitoring, logging, default
修复建议: 应用 Restricted PSA 标签
[K8S-002] 12 个 Deployment 的默认服务账户令牌自动挂载
风险: 容器被攻陷时存在凭证窃取风险
修复建议: 设置 automountServiceAccountToken: false
[K8S-003] 生产命名空间中无网络策略
风险: 所有 Pod 之间不受限制的横向移动
修复建议: 部署默认拒绝策略并添加明确的允许规则
高危发现:
[K8S-004] 5 个 Pod 以 root 身份运行,并具有特权安全上下文
[K8S-005] 8 个 Deployment 中的镜像使用可变标签(:latest)
[K8S-006] RBAC ClusterRoleBinding 向开发者组授予了 cluster-admin 权限