云原生安全避坑:镜像、网络和密钥管理的常见疏忽

📅 2026/7/28 15:37:12 👁️ 阅读次数 📝 编程学习
云原生安全避坑:镜像、网络和密钥管理的常见疏忽

云原生安全避坑:镜像、网络和密钥管理的常见疏忽

基础设施不需要漂亮话。

云原生安全不是装个扫描工具就完事了。过去一年我们做了三次安全审计,发现了 47 个安全问题,其中 31 个是日常运维中的疏忽——不是攻击手法有多高明,而是我们自己没做最基本的防护。这篇文章从镜像、网络和密钥管理三个维度列出最常见的疏忽,每个都附带风险说明和修复方案。

一、背景:云原生安全的特殊性

传统安全关注边界防护——防火墙、WAF、DDoS 防御。云原生环境的安全边界是模糊的:Pod 在集群内可以互相访问,Service 暴露了内部端口,密钥可能散落在 ConfigMap、Secret 和环境变量里。

三个维度的疏忽互相关联:不安全的镜像在不受限的网络里传播更快,明文密钥让入侵者一旦突破边界就能横移。

二、镜像类四个疏忽

疏忽 1:用 latest 标签,部署不可回溯

image: myapp:latest是最常见的偷懒做法。问题是:latest 指向的镜像随时可能变化,你不知道当前线上跑的是哪个版本。回滚时kubectl rollout undo回到的"上一个版本"可能是两天前的 latest,不是你期望的版本。

更严重的问题:某天镜像仓库被污染,latest 被替换成恶意镜像。所有用 latest 的 Pod 下次重启时就会拉取恶意镜像。如果是固定版本号,污染不会自动生效。

修复方案

  • 所有镜像使用固定版本号标签,如myapp:v2.3.1
  • CI/CD 流水线自动生成版本号,不允许手动写 latest。
  • 集群准入控制器(Admission Controller)拒绝 latest 标签的部署。

疏忽 2:镜像没做漏洞扫描就直接上线

Docker 镜像的基础层可能包含已知漏洞。我们审计时发现一个推理服务镜像基于 Ubuntu 20.04,里面有 23 个已知 CVE,其中 3 个是高危。更常见的情况:镜像里装了curlwgetapt等工具,入侵者拿到容器后可以直接用它下载恶意软件。

修复方案

  • CI 流水线集成 Trivy 或 Grype 扫描,高危 CVE 不修复就阻断构建。
  • 使用 distroless 或 scratch 基础镜像,里面只有你的应用二进制,没有 shell 和包管理器。
  • 定期(每周)重新扫描线上镜像,新发现的 CVE 要及时修补。
基础镜像类型大小包含工具安全等级
scratch0最高
distroless~2MB无 shell/apt
alpine~5MBbusybox
ubuntu/debian~70MBapt/curl/wget

疏忽 3:特权容器运行——给入侵者 root 权限

securityContext: privileged: true让容器拥有宿主机 root 的所有能力。GPU 推理服务经常需要这个权限来访问 NVIDIA 驱动,但 privileged 容器的风险极大:入侵者可以从容器逃逸到宿主机,控制整个节点。

修复方案

  • 不要用 privileged。用securityContext.capabilities.add只添加必需的 capability,如CAP_SYS_PTRACECAP_NET_RAW
  • GPU 容器使用 NVIDIA GPU Operator 的默认安全配置,不需要 privileged 就能访问 GPU。
  • 启用 Pod Security Admission(PSA),在 namespace 级别 enforce Restricted 策略,自动拒绝 privileged Pod。

疏忽 4:镜像包含调试工具——给入侵者送武器

开发阶段为了方便调试,镜像里塞了bashpythonstracetcpdump。生产环境直接用这个镜像上线。入侵者拿到容器后,这些工具就是他们的武器。

修复方案

  • 构建两个镜像:debug 镜像(含调试工具,只在开发环境用)和 production 镜像(最小化,不含调试工具)。
  • 用 Docker 多阶段构建,最终阶段只拷贝编译产物,丢弃所有中间层。
# 阶段1:构建 FROM golang:1.21 AS builder COPY . /src RUN go build -o /app/myapp # 阶段2:生产 FROM gcr.io/distroless/static-debian:nonroot COPY --from=builder /app/myapp /myapp ENTRYPOINT ["/myapp"] # 没有 shell、没有 apt、没有 curl

三、网络类三个疏忽

疏忽 5:Pod 间默认全通——入侵后一键横移

Kubernetes 默认的 NetworkPolicy 是"所有 Pod 可以和所有 Pod 通信"。这意味着一旦一个 Pod 被入侵,入侵者可以访问集群内任何其他 Pod——数据库、消息队列、模型服务,全部暴露。

修复方案

  • 每个 namespace 至少有一个 default-deny NetworkPolicy,禁止所有入站和出站流量,然后逐个放通需要的连接。
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all spec: podSelector: {} # 选中所有 Pod policyTypes: - Ingress - Egress # 不定义任何规则 = 拒绝所有流量
  • 然后为每个服务添加具体的 NetworkPolicy,只允许必要的通信路径。

疏忽 6:Service 暴露内部端口到公网

推理服务用type: LoadBalancer暴露到公网。但 LoadBalancer Service 默认暴露所有声明的端口,包括管理端口(如 metrics 的 9090、debug 的 8080)。管理端口没有认证,任何人都能访问。

修复方案

  • 生产 Service 只暴露业务端口,管理端口通过内部 Service(ClusterIP)暴露,只允许集群内访问。
  • 使用 Ingress 而不是 LoadBalancer 直接暴露,Ingress 可以统一加认证和限流。
  • 监控端口用单独的 Service 和 ServiceMonitor,不走公网。

疏忽 7:mTLS 没有全员部署——内部流量被嗅探

集群内 Pod 间通信默认是明文的。即使外部流量用了 TLS,内部流量依然是 HTTP。如果有人能在集群内嗅探流量(比如入侵了一个 Pod),就能看到所有内部 API 调用的内容,包括密钥、用户数据。

修复方案

  • 部署服务网格(Istio 或 Cilium),自动为所有 Pod 间通信加 mTLS。
  • 如果不想用服务网格的完整功能,至少启用 mTLS 模式。Istio 可以只做 mTLS 不做流量管理。
  • 不用服务网格的话,在应用层自己实现 mTLS:每个服务有自己的 TLS 证书,通过 cert-manager 自动管理。

四、密钥管理类三个疏忽

疏忽 8:Secret 明文存 ETCD——集群数据库就是密钥库

Kubernetes Secret 默认在 ETCD 中以 Base64 编码存储,不是加密。任何人有 ETCD 访问权限就能读取所有 Secret。审计时我们发现 ETCD 中有 12 个数据库密码、8 个 API Key、5 个 TLS 私钥,全是 Base64 "编码"的。

Base64 编码不是加密。编码是可逆的,任何人都能base64 -d解码。

修复方案

  • 启用 ETCD 静态加密:在 kube-apiserver 配置--encryption-provider-config,指定加密算法(推荐 AES-CBC 或 AES-GCM)。
  • 使用外部密钥管理:Vault 或 AWS KMS,Secret 只存引用,不存值。
  • 定期审计 ETCD 中的 Secret 内容,确保没有新增明文密钥。
# encryption-provider-config 示例 apiVersion: apiserver.config.k8s.io/v1 kind: EncryptionConfiguration resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: <32-byte-base64-encoded-key> - identity: {} # 回退:未加密的 Secret 也能读取

疏忽 9:密钥写进环境变量——进程信息泄露密钥

env.valueFrom.secretKeyRef把密钥注入环境变量。问题是:ps aux/proc/<pid>/environ能看到任何进程的环境变量。如果容器被入侵,入侵者一条命令就能获取所有密钥。

更隐蔽的风险:容器崩溃时,错误日志可能打印环境变量(某些框架默认行为),密钥被写进日志系统。

修复方案

  • 密钥用文件挂载(secret.volumeMounts)而不是环境变量。文件挂载后密钥在/etc/secrets/目录下,不会出现在环境变量或进程信息中。
  • 如果必须用环境变量(某些框架只支持环境变量),至少确保容器内没有 shell(用 distroless 镜像),入侵者无法执行ps命令。

疏忽 10:密钥轮换没有自动化——过期密钥在线上跑半年

密钥应该定期轮换,但手动轮换的工作量太大,没人愿意做。我们审计时发现一个数据库密码已经在线上跑了一年没改过,一个 TLS 证书过期了 3 个月没人发现。

过期 TLS 证书的后果比想象严重:证书过期后,mTLS 验证失败,Pod 间通信中断,集群功能部分停止。不是安全风险,是可用性风险。

修复方案

  • 用 cert-manager 自动管理 TLS 证书生命周期,过期前自动续签。
  • 数据库密码等非证书密钥,用 Vault 的动态密钥功能:每次请求生成短期密码,过期自动失效。
  • 建立密钥过期监控:所有 Secret 的metadata.annotations记录过期时间,Prometheus 定期扫描即将过期的密钥并告警。

五、安全疏忽全景图

入侵链路:不安全的镜像(多余工具 + 特权)→ 容器被入侵 → Pod 全通网络让入侵者横移 → 环境变量中的密钥被窃取 → ETCD 中更多明文密钥暴露。

每一层的疏忽都在为入侵链路开绿灯。

六、总结:云原生安全的三个基本动作

  1. 镜像最小化:distroless 基础镜像 + 固定版本号 + CI 扫描漏洞。给入侵者一个空容器,比装防火墙更有效。
  2. 网络隔离化:default-deny NetworkPolicy + mTLS + 端口最小暴露。不是所有 Pod 都需要互相访问。
  3. 密钥专业化:ETCD 加密 + 文件挂载 + 自动轮换。密钥是安全的核心,不是运维的边角。

这三件事做完,能挡住 80% 的常见入侵。不需要买昂贵的安全产品,只需要把该做的基本动作做到位。

基础设施不需要漂亮话,安全漏洞不会因为你没注意到就不存在。