在云原生时代,Kubernetes 已成为容器编排的事实标准,但默认配置下的 K8s 并不安全。一次错误的 RBAC 权限配置、一个暴露的 etcd 端口、或者一个特权模式的 Pod,都可能成为攻击者的入口。本文从认证授权、Pod 安全、网络隔离、数据加密四个维度,整理一份可直接落地的加固清单。

一、RBAC:最小权限是铁律

RBAC(基于角色的访问控制)是 K8s 权限管理的核心。很多人图省事,给 ServiceAccount 直接绑定 cluster-admin 角色,这在测试环境可以,但生产环境等于裸奔。

最小权限原则的正确做法是先定义应用实际需要的权限,再精确授予。以下是一个典型前端应用只读场景的 Role 示例:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: frontend-read-only
rules:
  - apiGroups: [""]
    resources: ["configmaps", "secrets"]
    resourceNames: ["app-config", "db-credentials"]  # 精确到具体资源名
    verbs: ["get", "list"]

如果你需要跨命名空间授权,使用 RoleBinding(本命名空间)或 ClusterRoleBinding(集群级别)。ClusterRoleBinding 要格外谨慎,绑定的 cluster-admin 角色要确认 subject 只能是必要的 ServiceAccount,不要用 system:authenticated 这种通配符:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: monitoring-reader
subjects:
  - kind: ServiceAccount
    name: prometheus
    namespace: monitoring
roleRef:
  kind: ClusterRole
  name: cluster-monitoring-view
  apiGroup: rbac.authorization.k8s.io

建议定期用 kubectl auth can-i --list --as=system:serviceaccount:<ns>:<sa> 检查每个 ServiceAccount 的实际权限,清理无用绑定。

二、Pod Security Standards:别让容器拥有不该有的能力

K8s 从 1.25 起将 Pod Security Policy 替换为 Pod Security Standards(PSS),通过命名空间标签定义三个级别的安全策略:privileged(最高权限)、baseline(最低要求)、restricted(生产推荐)。

将命名空间标记为 restricted 级别:

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/warn: restricted
    pod-security.kubernetes.io/warn-version: latest

restricted 策略会强制要求:禁止特权模式、禁止以 root 运行、只允许受控的 capabilities、必须使用只读根文件系统。例如一个合法的 non-root 容器 Pod spec:

securityContext:
  runAsNonRoot: true
  runAsUser: 10000
  seccompProfile:
    type: RuntimeDefault
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true
capabilities:
  drop:
    - ALL

如果你需要在 Pod 中挂载特定 capabilities(比如 CAP_NET_BIND_SERVICE 用于绑定低端口),要逐个声明而非全部放开:

capabilities:
  add:
    - NET_BIND_SERVICE
  drop:
    - ALL

三、网络策略:默认拒绝,按需放行

K8s 网络模型默认是全通行的,同一集群内的 Pod 可以自由互相访问。攻击者一旦拿到某个 Pod,往往能横向渗透到整个集群。解决方案是给每个命名空间设置默认拒绝规则,再按需放行。

默认拒绝所有入站流量:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress

放行 Web 层访问后端服务:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-web-to-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: web-frontend
      ports:
        - protocol: TCP
          port: 8080

需要注意的是,网络策略需要有 CNI 插件支持才能生效,Flannel 默认不支持,推荐使用 Calico、Cilium 或 WeaveNet。

四、Secrets 管理:别把敏感信息明文存放

Kubernetes Secrets 默认只是 Base64 编码,未经加密存储在 etcd 中,任何有 etcd 访问权限的人都能直接读取。最小化处理是开启 Secrets 的加密:

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-32-byte-key>
      - identity: {}  # 回退provider,不能删

然后在 kube-apiserver 启动参数中指定配置:

--encryption-provider-config=/etc/kubernetes/enc/encryption-config.yaml

生产环境中更推荐使用外部 Secrets 管理方案:HashiCorp Vault(通过 CSI 驱动挂载)、AWS Secrets Manager + EKS IAM Roles for Service Accounts、或者 Mozilla SOPS 配合 KMS 加密。这类方案的优势在于 Secrets 不会出现在 K8s API 层,审计日志也更完整。

日常使用中还要注意:不要用 kubectl create secret generic 时直接暴露明文,用 --from-literal--from-file;不要在 Pod 环境变量中引用 Secrets 而是使用 volume 挂载,这样重启才能感知变更。

五、etcd 加密:护住 K8s 的数据心脏

etcd 存储了 K8s 整个集群的状态数据,包括 Secret、RBAC 配置、Pod 定义等。一旦 etcd 被攻破,攻击者就拿到了整个集群。etcd 加密是最后一道防线。

开启 etcd 数据加密(静态加密):

# 生成一个 32 字节的加密 key(注意是原始字节,不是 base64 字符串)
ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)

# 写入 encryption config
cat > /etc/kubernetes/enc/encryption-config.yaml <<EOF
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: ${ENCRYPTION_KEY}
      - identity: {}
EOF

同时确保 etcd 的通信也是 TLS 加密的,启动参数大致如下:

etcd \\
  --cert-file=/etc/kubernetes/pki/etcd/server.crt \\
  --key-file=/etc/kubernetes/pki/etcd/server.key \\
  --client-cert-auth=true \\
  --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt \\
  --listen-client-urls=https://127.0.0.1:2379

etcd 数据目录的磁盘加密也不能忽视,生产环境建议用 LUKS 或云厂商的加密磁盘,防止物理磁盘泄露导致的数据暴露。

加固检查清单

以下是生产级 K8s 集群上线前必须通过的检查项:

  • kube-apiserver 开启 --anonymous-auth=false,禁止匿名访问
  • kube-apiserver 开启 --authorization-mode=RBAC,Node
  • kube-apiserver 关闭不爱用的服务端口(如 kubelet HTTPS API 不暴露在外网)
  • etcd 仅监听本地或通过安全网络访问,不对外网暴露 2379/2380 端口
  • 所有命名空间设置了 Pod Security Standards 标签
  • ServiceAccount 不绑定 cluster-admin,遵循最小权限
  • Secrets 开启静态加密(aescbc 或更好的 KMS 插件)
  • etcd 通信使用 TLS,不使用 insecure 模式
  • 网络策略:至少在关键命名空间(production、data)启用默认拒绝
  • 定期审计日志:kubectl get events --all-namespaces --watch

安全加固不是一次性工程,而是持续运营的过程。建议配合 Falco 做运行时威胁检测,配合 OPA Gatekeeper 做策略即代码(Policy-as-Code),用自动化工具保障每次部署都不违背安全基线。

防守的深度取决于攻击者的投入成本。做好这些基础加固,足以让大多数自动化扫描工具和初级攻击者止步。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐