本文档介绍了保护 build 的最佳实践。构建代码可以指代不同类型的操作,例如:
- 优化或混淆代码:例如,Google 开源工具 Closure Compiler 会解析和分析 JavaScript,移除无效代码,并重写和缩减剩余代码。它还会检查代码是否存在常见的 JavaScript 陷阱。
- 将代码编译为中间代码:例如,您可以将 Java 代码编译为 Java 类文件 (
.class
),或将 C++ 代码编译为对象文件 (.obj
)。 - 编译代码和关联,创建库或可执行文件:例如,将 C++ 代码编译为共享库 (
.so
) 或 Windows 可执行文件 (.exe
)。 - 将代码打包为可分发或可部署的格式:例如,从 Java 类文件创建 Java WAR (
.war
) 文件、创建 Docker 映像或创建 Python 构建的分发版 (.whl
)。
根据您使用的编程语言和部署到的环境,您的 build 可能包含这些操作的不同组合。例如,构建过程可能会将 Python 代码打包到构建的分发版中,并将其上传到 Artifact Registry 或 PyPI 等工件存储库,以便您在 Cloud Run 函数中将其用作依赖项。您还可以将 Python 代码容器化,并将容器映像部署到 Cloud Run 或 Google Kubernetes Engine。
本文档中的实践重点是构建代码以进行打包或部署到运行时环境,而不是编译代码。
使用自动化 build
自动化 build 或脚本化 build 会在 build 脚本或 build 配置中定义所有 build 步骤,包括检索源代码的步骤和构建代码的步骤。唯一的手动命令(如果有)是运行 build 的命令。
例如,构建脚本可以是:
- Cloud Build
cloudbuild.yaml
。 - 您使用
make
工具运行的 Makefile。 - 存储在
.github/workflows/
目录中的 YAML 格式的 GitHub Actions 工作流文件。
自动化构建可确保构建步骤的一致性。不过,在一致且可信的环境中运行 build 也很重要。
虽然本地 build 对调试很有用,但通过本地 build 发布软件可能会在构建流程中引入许多安全问题、不一致性和低效率。
- 允许本地构建会为有恶意意图的攻击者提供修改构建流程的方法。
- 开发者本地环境和开发者做法不一致,导致很难重现 build 并诊断 build 问题。
手动构建会利用更多基础架构资源(例如计算、存储和网络),导致该流程效率低下。在 SLSA 框架的要求中,SLSA 级别 1 要求使用自动化构建,SLSA 级别 2 要求使用构建服务(而非开发者环境)进行构建。
Cloud Build 是 Google Cloud 上的托管式构建服务。它使用构建配置文件向 Cloud Build 提供构建步骤。您可以配置 build 以提取依赖项、运行单元测试、静态分析和集成测试,并使用 Docker、Gradle、Maven、Go 和 Python 等构建工具创建工件。Cloud Build 与 Google Cloud 上的其他 CI/CD 服务(例如 Artifact Registry 和 Cloud Deploy)以及 GKE 和 Cloud Run 等运行时环境完全集成。它还可与 GitHub 和 Bitbucket 等主要源代码管理系统集成。
生成 build 出处
构建来源是关于 build 的一组可验证数据。
来源元数据包括已构建映像的摘要、输入源位置、构建工具链和构建时长等详细信息。
生成 build 来源有助于您:
- 验证构建的工件是否是从可信的来源位置使用可信的构建系统创建的。
- 识别从不可信的源位置或构建系统注入的代码。
您可以使用提醒和政策机制主动使用 build 来源数据。例如,您可以创建政策,以仅允许部署从经过验证的来源构建的代码。
对于 SLSA 级别 1,构建来源必须可供构建工件的使用方使用。对于 SLSA 级别 2,构建源数据还必须:
- 由构建服务生成或可直接从构建服务读取。
- 可供消费者验证其真实性和完整性。这应使用创建 build 历史记录数据的服务生成的数字签名来完成。
对于 SLSA 级别 3,来源内容还必须包含:
- 构建定义的入口点。
- 用户控制的所有 build 参数。
Cloud Build 可以为提供 SLSA 3 级 build 保证的容器映像生成 build 来源。如需了解详情,请参阅查看 build 来源。
使用临时构建环境
短暂环境是指仅在单次构建调用期间有效的临时环境。构建完成后,系统会清除或删除环境。临时 build 可确保 build 服务和 build 步骤在临时环境(例如容器或虚拟机)中运行。构建服务不会重复使用现有构建环境,而是为每个 build 预配一个新环境,并在构建流程完成后销毁该环境。
临时环境可确保构建干净无杂,因为之前的构建中没有任何可能干扰构建流程的残留文件或环境设置。非短暂环境为攻击者注入恶意文件和内容提供了机会。临时环境还可以减少维护开销,并减少构建环境中的不一致性。
Cloud Build 会为每个 build 设置一个新的虚拟机环境,并在 build 完成后销毁该环境。
限制对 build 服务的访问
遵循最小权限安全原则,向 build 服务和 build 资源授予所需的最小权限。您还应使用非人类身份代表 build 运行 build 并与其他服务交互。
如果您使用的是 Cloud Build:
- 向组织成员授予所需的最低权限。
- 为代表 Cloud Build 执行操作的服务账号自定义权限,使其仅具有您使用所需的权限。修改默认 Cloud Build 服务账号的权限,或考虑改用自定义服务账号。
- 使用 Cloud Build 允许的集成组织政策来控制允许调用 build 触发器的外部服务。
使用 VPC Service Controls 将 Cloud Build 放置在服务边界中。边界允许边界内的 Google Cloud 服务之间自由通信,但会根据您指定的规则限制跨边界的通信。边界还可降低数据渗漏风险。
Cloud Build 仅支持对在专用池中运行的 build 使用 VPC Service Controls。
保护凭据
build 通常包含与其他系统(例如版本控制系统、工件存储区和部署环境)的连接。保护您在 build 中使用的凭据有助于防止对软件供应链中的系统进行未经授权的访问以及数据渗漏。
避免直接在版本控制系统或 build 配置中存储硬编码凭据。请改为将凭据存储在安全的密钥库中。
在 Google Cloud 中,Secret Manager 可安全存储 API 密钥、密码和其他敏感数据。您可以将 Cloud Build 配置为使用存储在 Secret Manager 中的 Secret。
管理依赖项
应用的完整性取决于您开发的代码和您使用的所有依赖项的完整性。您还需要考虑发布依赖项的位置、有权读取和写入工件代码库的人员,以及针对部署到运行时环境的构建工件可信来源的政策。
如需详细了解依赖项管理,请参阅管理依赖项。
在 Cloud Build 中,您可以使用云构建器运行命令。构建器是安装有常用语言和工具的容器映像。您可以使用 Docker Hub 等公共注册库中的公共容器映像、Cloud Build 提供的构建器、社区贡献的构建器,以及您创建的自定义构建器。您还可以将 buildpacks 用作构建器,包括 Google Cloud 的 buildpack。
查看您在 Cloud Build 构建中使用的构建器,了解其提供方,并确定您是否信任其在软件供应链中的角色。如需更好地控制构建器中的代码,您可以创建自定义构建器,而不是使用公共来源中的构建器。
减少更改 build 的机会
还有许多其他因素可能会影响 build,包括:
- 并发运行且能够相互影响的 build,或持久存在且会影响后续 build。
- 除了 build 入口点和顶级源代码位置之外,还接受用户参数的 build。
- 指定具有范围的依赖项或可变依赖项(例如使用带有
latest
标记的映像)的 build。这些方法会导致 build 使用有缺陷或不需要的依赖项版本的风险。
以下做法有助于降低这些风险:
- 在短暂环境中运行每个 build。
- 避免使用额外参数运行 build,以免用户影响 build 脚本中定义的变量。
- 限制对 build 服务和 build 资源的访问权限。
- 引用不可变的依赖项版本,而不是标记等标识符,因为标记可能会在未来指向工件的其他版本。如需详细了解依赖项,请参阅依赖项管理。
软件供应链安全
Google Cloud 提供了一组模块化功能和工具,可用于改善软件供应链的安全状况。它会显示 Cloud Build 构建的已构建应用的安全数据分析。其中包括:
- SLSA 级别,用于标识软件供应链安全的成熟度级别。
- build 工件的漏洞、软件物料清单 (SBOM) 和漏洞可利用性交流 (VEX) 语句。
- 构建来源,即关于构建的一组可验证元数据。其中包括已构建映像的摘要、输入源位置、构建工具链、构建步骤和构建时长等详细信息。
如需了解如何查看构建的应用的安全数据分析,请参阅构建应用并查看安全数据分析。
后续步骤
- 了解保护源代码的最佳实践。
- 了解保护依赖项的最佳实践。
- 了解保护部署的最佳实践。