Language:Chinese VersionEnglish Version

容器安全是一个团队认为自己正在做的事情与实际正在做的事情之间存在巨大差距的领域。到2026年,容器将成为大多数组织的默认部署单元,从在Fly.io上发布产品的初创公司到运行千节点Kubernetes集群的企业。然而,围绕容器的安全实践并没有跟上其采用的步伐。

在过去几年中,我审计了数十个团队的容器部署,注意到相同的失败模式在不断重复。这些错误很少是异乎寻常的。它们是平凡的、可预见的,而且几乎总是可以预防的。本文涵盖了团队在容器安全方面最容易出错的常见领域,并提供了一个你可以立即应用的实用清单。

镜像安全:大多数团队忽视的基础

你的容器镜像是你安全态势的基础。镜像中的每个漏洞、每个不必要的二进制文件、每个硬编码的秘密都成为你攻击面的一部分。然而,整个行业镜像卫生状况仍然出奇地糟糕。

基础镜像问题

团队犯的第一个错误是选择臃肿的基础镜像。使用完整的Ubuntu或Debian镜像作为基础会引入数百个你不需要的包:编译器、包管理器、shell、网络工具。每一个都是潜在的攻击向量。如果攻击者在你的容器内获得执行权限,这些工具就会成为他们的工具包。

解决方案很简单,但需要自律。使用最小化的基础镜像,如Alpine、Google的distroless镜像或Chainguard的加固镜像。例如,一个Go二进制文件的distroless镜像只包含二进制文件本身和它需要的最小系统库。没有shell,没有包管理器,没有攻击者可用于横向移动的任何东西。

我经常遇到这样的论点:开发人员需要完整的镜像进行调试。这在开发期间是合理的,但你的生产镜像应该被精简到绝对最小。使用多阶段构建在完整环境中编译,然后将最终工件复制到最小化的运行时镜像中。这不是什么新实践,但我仍然惊讶于有多少生产部署仍然包含构建工具。

镜像扫描是必要的,但还不够

大多数团队已经采用某种形式的镜像扫描,通常通过Trivy、Grype等工具或其云提供商的内置扫描器。这很好,但许多团队将扫描视为一个复选框,而不是一个过程。

常见的失败模式是在构建时扫描镜像,之后就不再扫描。漏洞是持续被发现的。三个月前构建时干净的镜像今天可能有多个关键CVE。你需要对注册表中的镜像进行持续扫描,理想情况下,对正在运行的容器也是如此。

另一个差距是扫描范围。大多数扫描器专注于操作系统级别的包和常见的语言特定依赖项。但您的应用程序的传递依赖项、供应商库和静态链接的二进制文件可能无法得到完全覆盖。确保您的扫描策略涵盖完整的依赖树,而不仅仅是顶层包。

我推荐的一种实用方法:在 CI 管道中的构建时进行扫描,按每日计划扫描注册表中的镜像,并对当前部署的镜像中新的严重和高危 CVE 发出警报。这种三层方法无论漏洞何时被发现都能捕获它们。

镜像签名和来源证明

针对容器镜像的供应链攻击已从理论走向实践。Sigstore 项目及其 Cosign 工具使镜像签名变得易于使用,到 2026 年,不签名生产镜像已没有借口。

镜像签名建立了来源证明:这个镜像是由这个 CI 管道从这个源提交构建的。结合像 Kyverno 或 OPA Gatekeeper 这样强制执行签名验证的准入控制器,您可以确保只有由您信任的管道构建的镜像在您的集群中运行。

没有签名和验证,任何有权推送到您注册表的人都可以部署任意代码。这包括受损的 CI 凭据、恶意团队成员或获得您注册表访问权限的攻击者。签名并不能阻止镜像的构建,但验证可以阻止它运行。

运行时安全:容器启动后发生了什么

镜像扫描告诉您部署前已知的漏洞信息。运行时安全告诉您容器启动后内部实际发生的情况。这种差异至关重要,而运行时保护是大多数团队最大的盲点。

以 Root 身份运行:不应有的默认设置

我遇到的最常见的错误配置是容器以 root 用户身份运行。许多官方 Docker 镜像默认以 root 身份运行其主进程,团队在不加质疑的情况下继承了这一默认设置。

在容器内部以 root 身份运行意味着,如果攻击者利用了您应用程序中的漏洞,他们立即在容器命名空间中获得 root 权限。虽然容器隔离应该防止逃逸到主机,但针对容器运行时的权限提升漏洞定期被发现。以非 root 用户身份运行是一项关键的纵深防御措施。

解决方法是在您的 Dockerfile 中添加一个 USER 指令,在入口点之前切换到非 root 用户。在 Kubernetes 中,通过在 pod 或容器级别设置 securityContextrunAsNonRoot: true 来强制执行这一点。更好的方法是使用策略引擎在整个集群范围内强制执行。

功能和 Seccomp 配置文件

Linux 能力(capabilities)是对传统超级用户权限的细粒度分解。默认情况下,Docker 会禁用许多能力,但保留一组比大多数应用程序所需更宽松的能力。在某些配置中,Kubernetes 的默认设置甚至更加宽松。

最佳实践是禁用所有能力,然后仅添加应用程序明确需要的能力。大多数 Web 应用程序根本不需要任何特殊能力。需要绑定到 1024 以下特权端口的应用程序可以仅授予 NET_BIND_SERVICE,而不是使用完整的默认能力集运行。

Seccomp 配置文件限制了容器可以进行的系统调用。默认的 Docker seccomp 配置文件会阻止 Linux 上可用的 300 多个系统调用中的大约 44 个,这是一个合理的基础。但针对您的应用程序定制的自定义 seccomp 配置文件可以显著减少攻击面。像 strace 或基于 eBPF 的分析器这样的工具可以帮助您确定您的应用程序实际使用的系统调用,从而允许您构建一个白名单配置文件。

网络策略:被遗忘的防火墙

在默认的 Kubernetes 安装中,每个 pod 都可以与其他每个 pod 通信。集群内没有网络边界。这意味着如果攻击者攻破了一个容器,他们就可以访问集群中的每个服务,包括您的数据库、内部 API 和控制平面组件。

网络策略是 Kubernetes 中相当于防火墙规则的功能。它们让您可以指定哪些 pod 可以与其他哪些 pod 通信,使用哪些端口,以及通信方向。尽管这是一种基本的安全控制措施,但我发现我审计的大多数集群要么没有网络策略,要么只有部分覆盖。

从所有命名空间的默认拒绝策略开始,然后明确允许您的应用程序所需的流量模式。这会将安全模型从默认允许转变为默认拒绝,这是任何生产系统应有的正确姿态。

只读文件系统

另一个经常被忽视的安全加固措施是使用只读根文件系统运行容器。如果攻击者无法写入文件系统,他们就无法植入恶意软件、修改配置文件或建立持久性。如果您为应用程序需要的临时目录提供可写的 tmpfs 挂载点,大多数应用程序都可以使用只读文件系统运行。

在 Kubernetes 中,在容器的安全上下文中设置 readOnlyRootFilesystem: true。然后为 /tmp/var/run 或您的应用程序写入的任何其他目录添加 emptyDir 卷。这个简单的更改消除了整个类别的后利用技术。

供应链安全:信任您的依赖项

容器供应链安全不仅限于您自己的镜像。每个基础镜像、每个包、每个您拉入容器的组件都是信任链中的一环。到2026年,供应链攻击将成为破坏容器化应用的最常见途径之一。

固定版本与可重现性

为基础镜像使用 latest 标签是一种众所周知的反模式,但它仍然存在。当您使用 FROM node:latest 时,您的构建是不可重现的。您今天获取的镜像可能与明天获取的不同,而且您无法保证未来的版本没有被破坏或不会引入破坏性更改。

将基础镜像固定到特定的摘要(digest),而不仅仅是标签。标签可以被移动指向不同的镜像,但摘要是一个唯一标识镜像的加密哈希。这保证了可重现性,并防止了标签操纵攻击。

同样的原则也适用于包安装。锁定您的依赖文件,固定版本,并在可能时验证校验和。每个未固定的依赖都是一个供应链风险。

私有注册表与访问控制

您的容器注册表是关键的基础设施组件。如果攻击者能够将恶意镜像推送到您的注册表,他们就有可能将其部署到您的生产集群中。确保您的注册表具有强大的访问控制、审计日志和防止标签覆盖的镜像不可变性策略。

对于敏感环境,考虑使用镜像推广工作流程运行私有注册表。镜像在暂存注册表中构建和扫描,只有在通过所有安全检查后才被推广到生产注册表。这创建了清晰的审计轨迹,并防止未经审核的镜像到达生产环境。

密钥管理:停止将凭据放入镜像中

硬编码在容器镜像中的密钥是一个持续存在的问题。API密钥、数据库密码和烘焙在Docker镜像中的TLS证书最终会出现在注册表缓存、构建日志和层历史中。即使您在后续的Dockerfile层中删除了密钥,它仍然在镜像的层历史中可访问。

正确的方法是通过编排器的密钥管理在运行时注入密钥。Kubernetes Secrets默认是base64编码而非加密,但可以由外部密钥存储(如HashiCorp Vault、AWS Secrets Manager或External Secrets Operator)支持。关键原则是密钥永远不应该接触您的镜像构建过程。

在Kubernetes中一个常见错误是将密钥作为环境变量挂载,而不是作为文件。环境变量可能会通过进程列表、崩溃转储和日志框架泄露。将密钥作为文件挂载到tmpfs卷中更安全,并且允许在无需重启pod的情况下进行密钥轮换。

常见的Kubernetes错误配置

除了容器级别的安全问题外,Kubernetes 本身也引入了大量的配置面,其中包含许多需要关注的安全相关默认设置。

RBAC 权限过度配置

Kubernetes 中的基于角色的访问控制功能强大但复杂。最常见的错误是授予过于宽泛的权限,因为这比确定最小权限集更容易。具有 cluster-admin 特权的服务账户、通配符资源权限以及未使用的角色绑定会像技术债务一样不断累积。

定期审核您的 RBAC 策略。使用 rbac-lookupkubectl auth can-i --list 等工具来了解每个服务账户实际可以执行的操作。严格遵循最小权限原则:每个服务账户应只拥有其所需的权限,并且权限范围应限制在尽可能窄的命名空间内。

默认服务账户

每个 Kubernetes 命名空间都有一个默认服务账户,除非您明确选择退出,否则该账户会自动挂载到每个 pod 中。这个服务账户通常拥有比实际需要的更多权限,并且其令牌可被 pod 中运行的任何进程访问。

通过在服务账户或 pod 规范中设置 automountServiceAccountToken: false 来禁用自动服务账户令牌挂载。为确实需要 Kubernetes API 访问的 pod 创建专门的服务账户,并只授予它们所需的特定权限。

Etcd 加密

Kubernetes 将所有集群状态(包括 Secrets)存储在 etcd 中。默认情况下,Secrets 以 base64 编码形式存储,但在静态状态下并未加密。任何能够访问 etcd 数据目录的人都可以读取您集群中的所有秘密。

为 etcd 启用静态加密。大多数托管 Kubernetes 服务会自动处理这一点,但如果您运行的是自管理集群,这是一个经常被忽视的关键配置。

小型团队实用安全检查清单

安全指导往往令人不知所措,特别是对于没有专职安全工程师的小型团队。以下是一个优先级检查清单,您可以按影响与努力的比例逐步完成。

  • 使用最小基础镜像(Alpine 或 distroless)和多阶段构建。工作量:低。影响:高。
  • 以非root用户运行容器。添加 USER 指令并强制执行 runAsNonRoot。工作量:低。影响:高。
  • 在 CI 中启用镜像扫描,使用 Trivy 或 Grype。阻止包含严重 CVE 的部署。工作量:低。影响:中。
  • 通过摘要固定基础镜像并锁定依赖版本。工作量:低。影响:中。
  • 在每个命名空间中实施默认拒绝网络策略。工作量:中。影响:高。
  • 禁用所有 Linux 能力并仅添加所需的能力。工作量:中。影响:中。
  • 使用只读根文件系统并设置有针对性的可写挂载。工作量:中。影响:中。
  • 使用 Cosign 签名镜像并通过准入控制器强制验证。工作量:中。影响:高。
  • 通过密钥管理器外部化所有密钥。切勿将密钥构建到镜像中。工作量:中。影响:高。
  • 每季度审核 RBAC 策略。移除未使用的绑定并缩小权限范围。工作量:中。影响:中。
  • 禁用默认服务账户令牌挂载。创建特定用途的服务账户。工作量:低。影响:中。
  • 启用每日计划的持续注册表扫描。对已部署镜像中的新 CVE 发出警报。工作量:低。影响:中。

您不需要一次性实施所有措施。从前四项开始,它们需要的工作量最小且能立即提升安全性。然后根据团队的能力逐步实施其余措施。

文化维度

容器安全最难的部分不是技术层面的,而是文化层面的。减慢开发速度的安全措施会被绕过。复杂的配置会被错误配置。不被理解的政策会被忽视。

我曾合作过的最有效的安全团队将安全融入开发工作流程,而不是将其作为关卡附加。CI 管道中的自动扫描、带有清晰错误信息的策略即代码、默认安全的黄金路径模板,以及对事件和未遂事件的定期安全回顾。

如果您的开发者认为安全是别人的问题,那么再多的工具也无法拯救您。如果他们理解这些控制措施存在的原因,并且拥有符合人体工程学的工具来遵守这些措施,您的安全状况将自然改善。

结论

2026年的容器安全并非关乎那些异端的零日防御或前沿技术。而是关于持续应用那些已被充分理解的最佳实践:最小化镜像、非root用户执行、妥善的密钥管理、网络分段以及供应链验证。那些真正做好容器安全的团队,并非预算最多的团队,而是那些将安全视为开发和运维工作流程中不可或缺的一部分,而非事后才考虑的团队。

从检查清单开始,尽可能自动化,并将安全意识融入团队文化。威胁格局会不断演变,但掌握好基础知识可以保护你抵御绝大多数现实世界中的攻击。

By

Leave a Reply

Your email address will not be published. Required fields are marked *

You missed