软件供应链攻击正在把 CI/CD 系统变成高价值目标。攻击者不一定需要入侵生产环境,只要修改一段部署脚本,就可能让构建、制品和发布链路成为恶意代码的传播通道。Google Cloud Secure Source Manager(SSM)近期提供了两项正式可用的能力:阻断未经授权的 CI/CD 系统访问,以及通过 Code Owners 对代码变更实施更细粒度的审批控制。
这两项能力覆盖了供应链安全的两个边界:一边是“谁能触达构建和部署系统”,另一边是“谁能批准关键文件的变更”。将它们结合起来,可以把代码仓库、构建环境、制品存储和部署工具纳入更一致的身份与访问控制体系。
先保护 CI/CD 系统本身
传统网络边界并不能完全阻止 CI/CD 风险。企业网络一旦被攻破,攻击者可能利用已经建立的网络通道访问版本控制、构建系统、制品仓库或部署工具。因此,安全控制不能只停留在防火墙和内网隔离层,还需要在 CI/CD 系统之间验证访问者和访问路径。
SSM 的目标是通过统一的认证与授权机制,阻止未经授权的访问,即使企业网络已经受到影响,也能减少攻击者横向进入软件交付链路的机会。实践中,应将以下对象作为一个整体检查:
- 源代码仓库及其访问身份。
- Cloud Build 使用的构建池和服务账号。
- 构建产物和制品存储。
- 部署工具、运行时以及它们之间的连接权限。
对于位于不同私有网络的 CI/CD 组件,可以使用 Developer Connect 建立连接。摘要中的私有网络蓝图是:Secure Source Manager 连接 Private Service Connect,再连接 Cloud Build;仓库、构建池和制品存储都留在私有网络中,并使用 VPC Service Controls 限制代理端点的访问范围。
Code Owners 把审批从“角色”细化到“文件”
粗粒度的 Approver IAM 角色通常只能回答“这个人能不能审批”,却无法回答“这个人是否应该审批这类文件”。例如,应用团队可以审批业务代码,但不应单独批准生产部署脚本、构建配置或安全策略文件。
SSM 的 Code Owners 功能通过仓库中的 CODEOWNERS 文件定义审批规则,并支持以下控制方式:
- 按路径指定审批人:使用 glob 风格的路径匹配,为不同目录或文件设置不同审批团队。
- 按分支设置治理规则:可以在同一个文件中为
main、dev等分支定义不同的所有者。 - 嵌套多个 CODEOWNERS 文件:子目录可以维护更贴近团队职责的规则,SSM 使用“更本地的规则优先”逻辑;根目录管理员仍可对整个仓库保持否决权。
- 要求多个独立审批组签字:使用
[SectionName][count]语法,让同一个 Pull Request 必须获得不同部门的独立批准。
这种模型特别适合保护会影响交付链路的文件。比如,开发人员可以提交业务代码,但修改 .cloudbuild/、部署清单或安全配置时,还必须经过平台工程或安全团队批准。
一个可改造的 CODEOWNERS 示例
下面是一个示例配置。实际使用前,请将用户名或团队标识替换为组织中真实存在的身份,并根据 SSM 的语法文档确认具体的分支和 section 写法。
# CODEOWNERS
# 默认规则:所有代码至少由应用团队审阅
* @app-team
# 生产部署与构建链路需要平台团队审批
[Platform][1]
/cloudbuild.yaml @platform-team
/.cloudbuild/** @platform-team
/deploy/production/** @platform-team
# 安全敏感文件需要两名安全团队成员独立批准
[Security][2]
/security/** @security-team
/policies/** @security-team
# 开发分支可以由开发团队维护,主分支则要求平台团队参与
[dev]
/deploy/** @app-team
[main]
/deploy/** @platform-team
将配置提交到仓库后,可以用下面的命令检查文件内容,并在本地确认关键路径是否已经覆盖。这个命令不会替代 SSM 的 Pull Request 规则校验,但适合纳入提交前检查或 CI 诊断步骤。
#!/usr/bin/env bash
set -euo pipefail
owners_file="CODEOWNERS"
required_paths=(
".cloudbuild/"
"deploy/production/"
"security/"
"policies/"
)
if [[ ! -f "$owners_file" ]]; then
echo "ERROR: $owners_file is missing" >&2
exit 1
fi
for path in "${required_paths[@]}"; do
if ! grep -Fq "$path" "$owners_file"; then
echo "ERROR: no ownership rule found for $path" >&2
exit 1
fi
done
echo "CODEOWNERS contains rules for all protected paths"
这段检查脚本只验证关键路径是否出现,不能解析所有 glob、分支和独立审批语义。正式流程仍应以 SSM 的服务端规则为准,并通过测试 Pull Request 验证:普通代码变更、构建脚本变更、生产部署变更和安全策略变更是否分别触发了预期审批人。
用私有连接组织一条可验证的交付路径
Developer Connect 的价值不只是“把两个系统连起来”,而是帮助团队把 CI/CD 组件放在更清晰的私有网络边界内。可以按下面的顺序落地:
- 明确 SSM 仓库、Cloud Build、构建池和制品存储所在的 VPC 及项目边界。
- 根据 Private Network Integrations 指南配置 Developer Connect。
- 使用 Private Service Connect 连接 SSM 与 Cloud Build,避免将关键服务暴露到公共网络路径。
- 为构建和部署分别使用最小权限服务账号,不共享高权限身份。
- 使用 VPC Service Controls 限制代理端点可访问的项目和服务范围。
- 测试正常构建、跨网络访问失败、未经授权的 Pull Request 以及构建服务账号权限不足等场景。
一个简化的 Cloud Build 配置可以这样组织。它只是展示构建步骤与制品输出的结构,具体的私有池、项目和区域参数需要按实际环境补充:
steps:
- name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-ceu'
- |
echo "Building from a private CI/CD path"
./scripts/test.sh
./scripts/build.sh
artifacts:
objects:
location: 'gs://PRIVATE_ARTIFACT_BUCKET/releases/$SHORT_SHA'
paths:
- 'dist/**'
options:
logging: CLOUD_LOGGING_ONLY
真正的安全边界来自连接方式、网络策略、服务账号权限和仓库审批规则的组合,而不是某一个 YAML 字段。配置完成后,建议把访问日志、构建日志和 Pull Request 审批记录关联起来,便于在异常变更发生时追踪“谁提交、谁批准、谁构建、谁发布”。
落地时的检查清单
采用这两项能力时,可以用下面的清单减少遗漏:
- 为构建配置、部署清单、制品发布脚本和安全策略建立明确的 Code Owners 规则。
- 为
main与开发分支分别定义审批要求,避免所有分支使用同一套过于宽松的策略。 - 将大型根目录
CODEOWNERS拆分到子目录,让负责团队能够维护本地规则。 - 对需要跨团队把关的文件,使用独立 section 和明确的最少批准人数。
- 通过 Developer Connect 和 Private Service Connect 连接私有 CI/CD 组件。
- 使用 VPC Service Controls 为代理端点增加纵深防御。
- 定期用模拟 Pull Request 验证规则,尤其要覆盖构建脚本被修改和生产分支被修改的场景。
- 保留最小权限服务账号,并审查构建池、制品存储和部署工具之间的权限链路。
安全的 CI/CD 不应只依赖企业网络仍然可信这一前提。SSM 的访问控制负责限制系统之间的连接,Code Owners 负责限制关键代码变更的批准者;两者配合,才能让软件供应链在网络边界失守或普通开发账号被滥用时仍保留额外的防线。