Azure Pipelines 的其他安全注意事项

Azure DevOps Services | Azure DevOps Server 2022 | Azure DevOps Server 2020

在保护 Azure Pipelines 方面,需要考虑其他几个注意事项,例如保护共享基础结构存储库项目

保护共享基础结构

Azure Pipelines 中的受保护资源是真实基础结构的抽象。 请遵循以下建议来保护底层基础结构。

使用 Microsoft 托管池

Microsoft 托管池为管道的每次运行提供隔离以及干净的虚拟机。 如果可能,请使用 Microsoft 托管池而非自托管池。

每个项目单独使用代理

代理只能与单个池相关联。 可以通过将池与多个项目关联来跨项目共享代理。 实际上,多个项目可能会连续使用同一代理。 虽然经济高效,但此方法可能会带来横向移动风险。

为了缓解横向移动并防止项目之间的交叉污染,请维护单独的代理池,每个池专用于特定项目。

使用低特权帐户运行代理

虽然你可能很受诱惑,但使用直接访问 Azure DevOps 资源的标识运行代理可能会有风险。 此设置在组织中普遍使用 Microsoft Entra ID,但会带来风险。 如果在由 Microsoft Entra ID 支持的标识下运行代理,则它可以直接访问 Azure DevOps API,而无需依赖作业的访问令牌。 为了提高安全性,请考虑使用非特权本地帐户(例如网络服务)运行代理。

重要

在 Azure DevOps 中,有一个名为 Project Collection Service Accounts 的组,这可能会产生误导。 通过继承,项目收集服务帐户的成员也被视为项目集合管理员的成员。 某些客户使用由 Microsoft Entra ID 支持的标识运行其生成代理,这些标识可能是 Project Collection Service 帐户的一部分。 但是,如果攻击者在这些生成代理之一上运行管道,他们可能会控制整个 Azure DevOps 组织。

在某些情况下,自承载代理在高特权帐户下运行。 这些代理通常使用这些特权帐户访问机密或生产环境。 但是,如果攻击者在这些生成代理之一上执行遭到入侵的管道,他们就会获得对这些机密的访问权限。 然后,攻击者可以通过这些帐户访问的其他系统横向移动。

为了增强系统安全性,我们建议使用最低特权帐户来运行自承载代理。 例如,请考虑使用计算机帐户或托管服务标识。 此外,委托 Azure Pipelines 管理对机密和环境的访问权限。

最小化服务连接的范围

确保服务连接仅有权访问所需的资源。 只要可行,请考虑使用工作负荷标识联合来代替 Azure 服务连接的服务主体。 工作负荷标识联合使用 Open ID Connect(OIDC),这是一种行业标准技术,用于在 Azure 和 Azure DevOps 之间促进身份验证,而无需依赖机密。

确保 Azure 服务连接 的范围限定为仅访问必要的资源。 避免向用户授予整个 Azure 订阅的广泛参与者权限。

创建新的 Azure 资源管理器服务连接时,请始终选择特定的资源组。 确保资源组仅包含生成所需的 VM 或资源。 同样,在配置 GitHub 应用时,仅向打算使用 Azure Pipelines 生成的存储库授予访问权限。

保护项目

除了单个资源之外,考虑 Azure DevOps 中的资源组至关重要。 资源由团队项目组织,并了解管道可以根据项目设置和包含访问的内容至关重要。

管道中的每个作业都会收到一个访问令牌,该令牌有权读取打开的资源。 在某些情况下,管道也可能更新这些资源。 这意味着,虽然用户帐户可能无法直接访问管道中运行的特定资源、脚本和任务,但仍可以访问它。 此外,Azure DevOps 的安全模型允许从组织内的其他项目访问这些资源。 如果决定限制对某些资源的管道访问,此决定适用于项目内的所有管道,特定管道无法有选择地授予对开放资源的访问权限。

单独的项目

考虑到开放资源的性质,请考虑在单独的项目中管理每个产品和团队。 这样做可以防止管道从一个产品无意中访问另一个产品的开放资源,从而最大限度地减少横向暴露。 但是,当多个团队或产品共享项目时,其资源的精细隔离变得具有挑战性。

如果在 2019 年 8 月之前创建了 Azure DevOps 组织,则运行可能仍有权跨组织的所有项目打开资源。 组织管理员应查看 Azure Pipelines 中的关键安全设置,以便为管道启用项目隔离。

可以在组织设置>管道>设置中找到此设置,也可以直接找到此设置: 。 https://dev.azure.com/Organization_Name/_settings/pipelinessettings

作业授权范围 UI 的屏幕截图

保护存储库

在版本控制存储库中,可以存储源代码、管道的 YAML 文件和必要的脚本和工具。 为了确保对代码和管道进行安全更改,应用权限和分支策略至关重要。 此外,请考虑 向存储库添加管道权限和检查。

此外,请查看 存储库的默认访问控制设置

请记住,Git 的设计意味着分支级保护具有限制。 具有存储库推送访问权限的用户通常可以创建新分支。 如果使用的是 GitHub 开源项目,则具有 GitHub 帐户的任何人都可以为存储库分叉并提出贡献。 由于管道与存储库(不是特定分支)相关联,因此必须将代码和 YAML 文件视为可能不受信任的文件。

前叉

从 GitHub 使用公共存储库时,请务必仔细考虑分支生成的方法。 分支源自组织外部,构成特定风险。 若要保护产品免受可能不受信任的参与代码的危害,请考虑到以下建议

注意

这些建议主要适用于从 GitHub 生成公共存储库。

不要向分支生成提供机密

默认情况下,管道配置为生成分支,但机密和受保护的资源不会自动向这些管道中的作业公开。 必须不要禁用此保护来维护安全性。

分支生成保护 UI 的屏幕截图。

注意

启用分支生成以访问机密时,Azure Pipelines 会限制默认使用的访问令牌。 与常规访问令牌相比,此令牌对打开资源的访问权限有限。 若要授予分支生成与常规生成相同的权限,请启用 Make 分支生成具有与常规生成 设置相同的权限。

Azure DevOps Server 2020 及更低版本中分支生成保护 UI 的屏幕截图。

注意

启用分支生成以访问机密时,Azure Pipelines 会限制默认使用的访问令牌。 与普通访问令牌相比,它对开放资源的访问权限更加有限。 无法禁用此保护。

考虑手动触发分支生成

可以关闭自动分叉生成,而是使用拉取请求注释来手动生成这些贡献。 此设置使你有机会在触发生成之前查看代码。

使用 Microsoft 托管的代理进行分支生成

避免在自承载代理上运行分支中的生成。 这样做可以允许外部组织在企业网络中的计算机上执行外部代码。 尽可能使用Microsoft托管代理。 对于自承载代理,请实现网络隔离,并确保代理不会在作业之间保留其状态。

查看代码更改

在对分叉拉取请求运行管道之前,请仔细查看建议的更改,并确保你愿意运行它。

运行的 YAML 管道的版本是拉取请求中的 YAML 管道版本。 因此,请特别注意对 YAML 代码和管道运行时运行的代码的更改,例如命令行脚本或单元测试。

GitHub 令牌范围限制

生成 GitHub 分叉拉取请求时,Azure Pipelines 可确保管道无法更改任何 GitHub 存储库内容。 仅当使用 Azure Pipelines GitHub 应用来与 GitHub 集成时,此限制才适用。 如果使用其他形式的 GitHub 集成(例如 OAuth 应用),则不会强制实施限制。

用户分支

组织中具有适当权限的用户可以创建包含新代码或更新代码的新分支。 此代码可以运行与受保护分支相同的管道。 如果新分支中的 YAML 文件已更改,则更新的 YAML 用于运行管道。 虽然此设计允许极大的灵活性和自助服务,但并非所有更改都是安全的(无论是否恶意)。

如果你的管道使用源代码或者是在 Azure Repos 中定义的,则你必须完全了解 Azure Repos 权限模型。 具体而言,在存储库级别具有“创建分支”权限的用户可以向存储库中引入代码,即使该用户缺少“参与”权限也是如此。

其他安全注意事项

在保护管道时,应考虑以下几个其他事项。

依赖于 PATH

依赖于代理的 PATH 设置是一种危险方法。 它可能不会指出你认为它的位置,因为它可能被以前的脚本或工具更改。 对于安全关键型脚本和二进制文件,请始终使用程序的完全限定路径。

日志机密

Azure Pipelines 会尽可能尝试从日志中清理机密。 此筛选是尽最大努力进行的,无法捕获机密可能泄露的一切方式。 避免将机密回显到控制台、在命令行参数中使用机密或将其记录到文件。

锁定容器

容器有一些系统提供的卷装载,用于映射与主机代理进行通信所需的任务、工作区和外部组件。 你可以将其中任何卷或所有这些卷标记为只读的。

resources:
  containers:
  - container: example
    image: ubuntu:22.04
    mountReadOnly:
      externals: true
      tasks: true
      tools: true
      work: false  # the default; shown here for completeness

通常,大多数人应将前三个目录设置为只读目录,并保留 work 为读写。 如果不在特定作业或步骤中写入 work 目录,也可以随意进行 work 只读操作。 但是,如果管道任务涉及自我修改,则可能需要保留 tasks 为读写。

控制可用的任务

你可以禁用从市场安装和运行任务的功能,从而可以更好地控制管道中执行的代码。 还可以禁用所有现装任务(除了签出,这是代理上的特殊操作)。 建议在大多数情况下不要禁用现成任务。

通过 tfx 直接安装的任务始终可用。 启用这两项功能后,只有这些任务可用。

使用审核服务

审核服务中记录了许多管道事件。 定期查看审核日志,确保不会发生恶意更改。 访问 https://dev.azure.com/ORG-NAME/_settings/audit 以开始操作。

后续步骤