Kubernetes 环境中最关键的 10 个安全风险以及如何缓解这些风险。
OWASP Kubernetes Top 10 确定了 Kubernetes 环境中最重要的安全风险。Kubernetes 已成为容器协调的事实标准,但其灵活性和复杂性带来了众多安全挑战。本指南涵盖工作负载错误配置、RBAC 问题、机密管理、网络分段等内容。
不安全的工作负载配置是最常见的 Kubernetes 安全问题。以 root 身份运行的容器、权限过高、可写文件系统或无资源限制,都会造成严重的攻击。默认配置通常不安全,必须明确加固。
拥有 root 权限和主机访问权限的受攻击容器可以逃出容器沙盒,访问主机文件系统,并转向其他工作负载。通过配置错误的 pod 进行权限升级可能会导致整个群集受损。
apiVersion: v1 kind: Pod metadata: name: insecure-app spec: containers: - name: app image: myapp:latest # Mutable tag! securityContext: privileged: true # Full host access! runAsUser: 0 # Running as root! volumeMounts: - mountPath: /host name: host-fs volumes: - name: host-fs hostPath: path: / # Entire host filesystem!
apiVersion: v1 kind: Pod metadata: name: secure-app spec: securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - name: app image: myapp@sha256:abc123... # Immutable digest securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 1000 capabilities: drop: ["ALL"] resources: limits: cpu: "500m" memory: "256Mi" requests: cpu: "100m" memory: "128Mi"
Kubernetes RBAC(基于角色的访问控制)功能强大,但也很复杂。过度放任的角色,尤其是集群管理员绑定、通配符权限和过多的服务账户权限,会导致对集群资源、机密和工作负载的未经授权访问。
获得超权限服务账户访问权限的攻击者可以列出机密、创建权限 pod、修改部署并升级为集群管理员。通配符 RBAC 规则相当于 Kubernetes 授予 root 访问权限。
# ClusterRoleBinding granting cluster-admin to a service account apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: app-admin-binding subjects: - kind: ServiceAccount name: my-app # App SA with cluster-admin! namespace: default roleRef: kind: ClusterRole name: cluster-admin # Full cluster control! apiGroup: rbac.authorization.k8s.io
# Scoped Role with minimum required permissions apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: app-role namespace: my-app-ns rules: - apiGroups: [""] resources: ["configmaps"] verbs: ["get", "list"] # Read-only, specific resources resourceNames: ["app-config"] # Named resource only --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: app-role-binding namespace: my-app-ns subjects: - kind: ServiceAccount name: my-app namespace: my-app-ns roleRef: kind: Role name: app-role apiGroup: rbac.authorization.k8s.io
Kubernetes Secrets 默认只采用 base64 编码,而非加密。将敏感数据(API 密钥、数据库密码、TLS 证书)存储在纯配置映射、环境变量或未加密的 "秘密 "中,会暴露给拥有 API 访问权限的任何人。如果未启用静态加密,则 Secrets 在 etcd 中也是可见的。
暴露的机密允许攻击者访问数据库、云账户和外部服务。未经加密存储在 etcd 中的秘密可被拥有 etcd 访问权限的任何人读取。环境变量机密在 pod 规格和进程列表中可见。
# Secret in plain ConfigMap — visible to anyone apiVersion: v1 kind: ConfigMap metadata: name: app-config data: DB_PASSWORD: "SuperSecret123!" # Plaintext password! API_KEY: "sk-live-abc123xyz" # API key in ConfigMap! --- apiVersion: v1 kind: Pod metadata: name: app spec: containers: - name: app image: myapp:latest envFrom: - configMapRef: name: app-config # All values as env vars!
# Use External Secrets Operator with a vault backend apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: app-secrets spec: refreshInterval: 1h secretStoreRef: name: vault-backend kind: ClusterSecretStore target: name: app-secrets data: - secretKey: db-password remoteRef: key: secret/myapp/db property: password --- apiVersion: v1 kind: Pod metadata: name: app spec: containers: - name: app image: myapp@sha256:abc123... volumeMounts: - name: secrets mountPath: /etc/secrets readOnly: true # Mount as read-only files volumes: - name: secrets secret: secretName: app-secrets
如果没有集群级策略,就无法防止部署不安全的工作负载。Pod 安全标准 (PSS)、准入控制器和 OPA Gatekeeper 或 Kyverno 等策略引擎对于在所有命名空间执行安全基线至关重要。
如果不执行策略,任何开发人员都可以部署有权限的容器、使用 hostPath 卷或禁用安全控制。一个配置错误的工作负载就可能危及整个群集。手动审查无法涵盖所有违规行为。
#!/bin/bash # No admission control — any pod spec is accepted # No Pod Security Standards configured # Anyone can deploy a privileged container kubectl run hacker-pod --image=alpine \ --overrides='{"spec":{"containers":[{ "name":"hack", "image":"alpine", "securityContext":{"privileged":true}, "command":["nsenter","--target","1","--mount","--uts","--ipc","--net","--pid","--","bash"] }]}}' # No policy blocks this — now has host root access!
# Enforce Pod Security Standards with Kyverno apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: disallow-privileged spec: validationFailureAction: Enforce background: true rules: - name: deny-privileged match: any: - resources: kinds: ["Pod"] validate: message: "Privileged containers are not allowed." pattern: spec: containers: - securityContext: privileged: "false" - name: require-run-as-non-root match: any: - resources: kinds: ["Pod"] validate: message: "Containers must run as non-root." pattern: spec: securityContext: runAsNonRoot: true
默认情况下,Kubernetes 集群中的所有 pod 都可以相互通信,不受任何限制。这种扁平化网络架构意味着,受到攻击的 pod 可以访问任何其他 pod、服务,甚至是 Kubernetes API 服务器。网络策略对实施微分区至关重要。
没有网络分段,横向移动就变得微不足道。被入侵的前端 pod 可以直接访问后端数据库、内部服务和元数据 API(169.254.169.254)。这违反了网络层的最小权限原则。
# No NetworkPolicy — all pods can communicate freely apiVersion: v1 kind: Namespace metadata: name: production # No default deny policy # No network policies at all # Any pod can reach: # - Other pods in any namespace # - Kubernetes API server # - Cloud metadata API (169.254.169.254) # - External internet
# Default deny all ingress and egress apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all namespace: production spec: podSelector: {} policyTypes: ["Ingress", "Egress"] --- # Allow only frontend to backend communication apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend-to-backend namespace: production spec: podSelector: matchLabels: app: backend policyTypes: ["Ingress"] ingress: - from: - podSelector: matchLabels: app: frontend ports: - port: 8080 protocol: TCP
Kubernetes 控制平面组件(API 服务器、etcd、kubelet、仪表盘)和应用服务可能会无意中暴露在互联网或不受信任的网络中。暴露的仪表盘、未经验证的 kubelet 和可公开访问的 API 服务器是常见的攻击载体。
公开的 Kubernetes 组件可直接访问群集管理。未经验证的 kubelet API 允许执行容器。带有默认凭据的公开仪表盘可授予集群管理员访问权限。公共 etcd 访问可公开包括机密在内的所有集群数据。
# Kubernetes Dashboard exposed via LoadBalancer apiVersion: v1 kind: Service metadata: name: kubernetes-dashboard namespace: kubernetes-dashboard spec: type: LoadBalancer # Public internet access! ports: - port: 443 targetPort: 8443 selector: k8s-app: kubernetes-dashboard # Dashboard bound to cluster-admin service account # No authentication required — full cluster access!
# Dashboard accessible only via kubectl proxy apiVersion: v1 kind: Service metadata: name: kubernetes-dashboard namespace: kubernetes-dashboard spec: type: ClusterIP # Internal only ports: - port: 443 targetPort: 8443 selector: k8s-app: kubernetes-dashboard --- # Access via: kubectl proxy, then browse to: # http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ # Or use kubectl port-forward for secure access
Kubernetes 集群由许多组件组成:API 服务器、etcd、kubelet、kube-proxy、CoreDNS 和第三方附加组件(入口控制器、服务网格、监控)。配置错误或未打补丁的组件会带来漏洞。默认配置通常没有加固。
存在漏洞的群集组件可被利用来远程执行代码、权限升级或拒绝服务。kubelet、Ingress 控制器或 CNI 插件中的 CVE 可导致容器逃脱和群集接管。过时组件会积累已知漏洞。
# kubelet with insecure configuration # /var/lib/kubelet/config.yaml apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration authentication: anonymous: enabled: true # Anyone can access kubelet API! webhook: enabled: false # No authorization! authorization: mode: AlwaysAllow # All requests permitted! readOnlyPort: 10255 # Unauthenticated read port open!
# Hardened kubelet configuration apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration authentication: anonymous: enabled: false # Deny anonymous access webhook: enabled: true # Use API server for auth x509: clientCAFile: /etc/kubernetes/pki/ca.crt authorization: mode: Webhook # API server authorizes requests readOnlyPort: 0 # Disable read-only port protectKernelDefaults: true eventRecordQPS: 5
在云环境(AWS、GCP、Azure)中运行的 Kubernetes 群集可以访问云元数据 API 和附加到节点的 IAM 角色。入侵 pod 的攻击者可以利用这些权限从群集升级到云账户,访问 S3 桶、数据库和其他云服务。
节点级 IAM 角色由该节点上的所有 pod 继承。受损的 pod 可以查询元数据 API(169.254.169.254)以获取云凭证,然后访问节点角色允许的任何云资源。这样就能从 Kubernetes 横向移动到更广泛的云环境。
# Node IAM role with excessive permissions resource "aws_iam_role_policy_attachment" "node_admin" { role = aws_iam_role.node_role.name policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" # All pods on this node inherit admin access! } # No IMDS restrictions — any pod can get node credentials # curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Use IAM Roles for Service Accounts (IRSA) resource "aws_iam_role" "app_role" { name = "my-app-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Federated = aws_iam_openid_connect_provider.eks.arn } Action = "sts:AssumeRoleWithWebIdentity" Condition = { StringEquals = { "${aws_iam_openid_connect_provider.eks.url}:sub" = "system:serviceaccount:my-ns:my-app" } } }] }) } # Pod-level scoped IAM via service account annotation # Only this specific pod gets these specific permissions
Kubernetes 支持多种身份验证机制:证书、令牌、OIDC 和 webhook 身份验证。失效的身份验证包括使用静态令牌、共享 kubeconfig 文件、不轮换证书以及未能与身份提供商集成。服务帐户令牌自动挂载会产生不必要的攻击面。
被盗或泄漏的 kubeconfig 文件和服务帐户令牌可提供直接的群集访问权限。永不过期的静态令牌可提供持久访问权限。如果没有集中式身份管理,撤销已离职团队成员的访问权限既困难又容易出错。
# Static token file for API server authentication # /etc/kubernetes/token-auth-file.csv # token,user,uid,"groups" # abc123,admin,1,"system:masters" # Static token, never expires! # Pod with auto-mounted service account token apiVersion: v1 kind: Pod metadata: name: app spec: # automountServiceAccountToken defaults to true! # Token mounted at /var/run/secrets/kubernetes.io/serviceaccount/token containers: - name: app image: myapp:latest # App doesn't need K8s API access but has a token anyway
# Disable auto-mounted token for pods that don't need API access apiVersion: v1 kind: ServiceAccount metadata: name: my-app namespace: my-app-ns automountServiceAccountToken: false # No auto-mount --- # For pods that need API access, use bound tokens with expiry apiVersion: v1 kind: Pod metadata: name: api-consumer spec: serviceAccountName: api-consumer-sa automountServiceAccountToken: false containers: - name: app image: myapp@sha256:abc123... volumeMounts: - name: token mountPath: /var/run/secrets/tokens readOnly: true volumes: - name: token projected: sources: - serviceAccountToken: expirationSeconds: 3600 # 1-hour expiry audience: api-server
Kubernetes 会为 API 服务器请求生成审计事件,但审计日志经常被禁用或配置错误。如果没有适当的日志记录和监控,恶意活动(如未经授权的秘密访问、RBAC 更改和容器逃逸)就不会被发现。容器和节点级别的运行时安全监控至关重要。
没有审计日志,攻击者的行动就不会被发现。他们可以在没有任何记录的情况下创建后门服务账户、提取机密和修改工作负载。如果没有审计跟踪来确定发生了什么、何时发生、由谁发生,事件响应就会受到严重影响。
# API server with no audit logging configured # kube-apiserver flags: # --audit-log-path="" # No audit log! # --audit-policy-file="" # No audit policy! # No runtime security monitoring # No alerting on suspicious activities # Container logs not collected centrally # Default log retention — logs lost on pod restart
# Comprehensive audit policy apiVersion: audit.k8s.io/v1 kind: Policy rules: # Log all secret access at Metadata level - level: Metadata resources: - group: "" resources: ["secrets"] # Log RBAC changes at RequestResponse level - level: RequestResponse resources: - group: rbac.authorization.k8s.io resources: ["clusterroles", "clusterrolebindings", "roles", "rolebindings"] # Log pod exec/attach (potential attacks) - level: Request resources: - group: "" resources: ["pods/exec", "pods/attach", "pods/portforward"] # Default: log at Metadata level - level: Metadata
| 身份证 | 脆弱性 | 严重性 | 关键缓解措施 |
|---|---|---|---|
| K01 | 不安全的工作负载配置 | Critical | 非根目录、只读 FS、删除功能、资源限制 |
| K02 | 过于放任的授权 | Critical | 名称空间范围的 RBAC,无通配符,审计绑定 |
| K03 | 秘密管理失败 | Critical | 外部机密、静态加密、文件挂载 |
| K04 | 政策执行不力 | High | Pod 安全标准,Kyverno/OPA Gatekeeper |
| K05 | 缺失的网络分割 | High | 默认-拒绝网络策略,微分段 |
| K06 | 过度暴露的组件 | High | ClusterIP 服务、kubectl 代理、防火墙规则 |
| K07 | 配置错误的群集组件 | High | CIS 基准、禁用匿名验证、补丁管理 |
| K08 | 云团到云团的横向移动 | Critical | IRSA/Workload Identity、块元数据 API、IMDSv2 |
| K09 | 被破坏的验证机制 | Critical | OIDC 集成、绑定令牌、禁用自动安装 |
| K10 | 日志记录和监控不足 | Medium | 审计日志、Falco/Tetragon、SIEM 集成 |