Azure DevOps Services | Azure DevOps Server 2022 | Azure DevOps Server 2020
流水线提供强大的功能,用于执行脚本并将代码部署到生产环境,但至关重要的是要将这种能力与安全性平衡。 你从来不希望管道成为恶意代码的管道。 与开发团队所需的灵活性和能力平衡安全性至关重要。
本文概述了必要的安全相关配置,以保护管道免受威胁和漏洞的影响。
先决条件
类别 | 要求 |
---|---|
Azure DevOps | - 在 确保 Azure DevOps 安全方面实施建议。 - 对 YAML 和 Azure Pipelines 的基本知识。 有关详细信息,请参阅 创建第一个管道。 |
权限 | - 修改管道权限: 项目管理员组的成员。 - 修改组织权限:成为项目集合管理员组的成员。 |
限制项目、存储库和服务连接访问
为了增强安全性,请考虑将项目分开,使用分支策略,并为分叉添加更多安全措施。 最大程度地减少服务连接的范围,并使用最安全的身份验证方法。
- 单独的项目:在单独的项目中管理每个产品和团队。 这样可以防止管道从一个产品无意中访问另一个产品的开放资源,从而最大限度地减少横向暴露。
- 使用项目级标识:对管道使用基于项目的生成标识,而不是使用集合级标识。 项目级标识只能访问其关联项目中的资源,最大限度地减少恶意参与者未经授权访问的风险。 有关详细信息,请参阅 作用域内生成标识 和 作业授权范围。
- 使用分支策略:若要确保对代码和管道的安全更改,请应用权限和分支策略。 此外,请考虑 向存储库添加管道权限和检查。
-
添加额外的分叉安全性:使用 GitHub 中的公共存储库时,请仔细考虑分叉生成的方法。 源自组织外部的分支会带来特定的风险。
- 不要为分支生成提供机密:默认情况下,管道配置为生成分支,但机密和受保护的资源不会自动向这些管道中的作业公开。 必须不要禁用此保护来维护安全性。
- 请考虑手动触发分叉生成:关闭自动分叉生成,并使用拉取请求注释手动生成这些贡献。 此设置使你有机会在触发构建之前查看代码。 有关详细信息,请参阅 关闭自动分叉生成。
- 不要为分叉版本提供机密:默认情况下,与管道关联的机密无法拉取对分叉的请求验证。 不要启用使机密对分叉的构建可用的选项。 有关如何查找和验证此设置的说明,请参阅 分叉中的“贡献”。
- 请考虑手动触发分叉生成:关闭自动分叉生成,并使用拉取请求注释手动生成这些贡献。 此设置使你有机会在触发构建之前查看代码。 有关如何执行此作的说明,请参阅 关闭自动分支生成。
- 使用 Microsoft 托管代理进行 fork 构建:避免在自建代理上运行 fork 中的构建。 这样做可以允许外部组织在企业网络中的计算机上执行外部代码。 尽可能使用Microsoft托管代理。
- 将 Azure Pipelines GitHub 应用用于令牌范围限制:当您生成 GitHub 分叉的拉取请求时,Azure Pipelines 确保管道无法更改任何 GitHub 存储库内容。 仅当使用 Azure Pipelines GitHub 应用与 GitHub 集成时,此限制才适用。
保护服务连接
- 最大程度地减少服务连接的范围:服务连接应仅有权访问必要的资源。 创建新的 Azure 资源管理器服务连接时,请始终选择特定的资源组。 请确保资源组仅包含生成所需的 VM 或资源。 有关如何设置服务连接的说明,请参阅 使用 Azure 资源管理器服务连接。
- 使用工作负荷标识联合身份验证进行身份验证:尽可能使用工作负荷标识联合身份验证,而不是 Azure 服务连接的服务主体。 工作负荷标识联合使用 Open ID Connect(OIDC),这是一种行业标准技术,用于在 Azure 和 Azure DevOps 之间促进身份验证,而无需依赖机密。 有关如何执行此操作的说明,请参阅创建与工作负荷标识联合身份验证的服务连接(自动)。
- 最小化 GitHub 应用访问权限:将 GitHub 应用配置为 Azure DevOps 时,仅向打算使用管道生成的存储库授予访问权限。
使用 YAML 管道而不是经典管道
为了提高安全性并减少意外配置的风险,请使用 YAML 管道而不是经典管道。 此预防措施可防止 YAML 和经典管道共享同一资源(例如服务连接)带来的安全问题。 如果组织使用的是经典管道, 请将管道迁移到 YAML。
- YAML 提供基础结构即代码的优势:将 YAML 管道视为任何其他代码,因为步骤和依赖项是在代码中定义的。 还可以清楚地了解管道配置,并降低意外配置错误的风险。
- YAML 管道可以与增强的安全措施相结合:通过代码评审和拉取请求,使用 分支策略 为拉取请求设置评审过程,以防止合并错误。
-
资源访问管理:资源所有者控制 YAML 管道是否可以访问特定资源。 此安全功能可防止攻击,例如窃取另一个存储库。 使用 审批和检查 为每个管道运行提供访问控制。
- 受保护的分支检查:如果有特定分支的手动代码评审过程,可以将此保护扩展到管道。 对资源的受保护分支检查可防止管道在未经授权的分支上自动运行。
- 手动审批检查:使用手动审批检查阻止管道请求使用受保护的资源,直到指定用户或组手动批准。
- 工作时间检查:使用此检查来确保管道部署在指定的日期和时间范围内启动。
- 禁用创建经典管道:独立禁用经典生成管道和经典发布管道的创建。 禁用两者后,无法通过用户界面或 REST API 创建经典生成管道、经典发布管道、任务组或部署组。 有关详细信息,请参阅 “禁用创建经典管道”。
安全代理
若要保护容器,请将卷标记为只读、设置资源限制、使用受信任的映像、扫描漏洞并强制实施安全策略。
- 使用Microsoft托管代理而不是自承载代理:Microsoft托管的代理为管道的每个运行提供隔离和干净的虚拟机。 使用Microsoft托管代理,而不是自承载代理。 有关详细信息,请参阅 Microsoft托管代理。
- 每个项目的单独代理:为了缓解横向移动并防止项目之间的交叉污染,请维护单独的代理池,每个代理池专用于特定项目。
- 使用低特权帐户运行代理:若要增强系统安全性,请使用最低特权帐户来运行自承载代理。 例如,请考虑使用计算机帐户或托管服务标识。 不要在具有直接访问 Azure DevOps 资源的标识下运行代理。
-
隔离生产项目和敏感代理池:使用不同的代理池来防止安全问题。
- 对生产构件使用一个单独的代理池:使用不同的代理池隔离生产构件,防止意外部署来自非生产分支。
- 细分敏感池: 为敏感和非敏感工作负荷创建单独的池。 仅允许在与相应池关联的生成定义中使用凭据。
- 为自承载代理配置限制性防火墙:将防火墙设置为尽可能限制,同时仍允许代理正常运行、平衡安全性和可用性。
- 定期更新自承载代理池:使用常规更新使自承载代理保持最新状态,以确保易受攻击的代码未运行,从而减少开发风险。
安全地使用变量和参数
通过遵循设置机密的最佳做法,在管道中安全地使用变量和参数。 最佳做法包括限制机密使用、使用队列时间变量,以及启用 shell 任务参数验证来保护管道免受威胁和漏洞的影响。
- 限制对机密的访问:从管道中删除任何机密或密钥。 转向无机密身份验证方法,例如工作负荷联合身份验证,或在 UI、变量组或从 Azure 密钥保管库源的变量组中配置机密。
- 启用 shell 参数验证:启用 “启用 shell 任务”参数验证 设置时,添加了对分号、引号和括号等字符的检查。 在“设置管道>设置”>下,在组织级别或项目级别启用“启用 shell 任务”参数验证。
- 限制可在队列时间设置的变量:通过在组织设置 中的管道设置启用限制可在队列时间设置的变量,防止用户在队列时定义新变量。
-
使用参数而不是变量:与变量不同,正在运行的管道无法修改管道参数。 参数具有数据类型,
number
string
并且可以限制为特定的值子集。 当管道的用户可配置方面只接受预定义列表中的值时,此限制非常有用,确保管道不接受任意数据。 - 引用模板中的机密:与其在管道 YAML 中直接包含带有机密参数的内联脚本,不如使用模板,将敏感信息与主管道分离。 若要实现此方法,请为脚本创建单独的 YAML 文件,然后将该脚本存储在单独的安全存储库中。 然后,可以引用模板,并将 YAML 中的机密变量作为参数传递。 安全变量应来自 Azure 密钥库、变量组或管道 UI。 有关详细信息,请参阅使用模板。
-
使用分支策略和变量组权限限制机密:可以使用变量组权限、条件作业插入和分支策略的组合来确保机密绑定到
main
分支。 有关详细信息,请参阅 保护机密。 -
使用 setvariable 限制设置变量:使用
settableVariables
属性配置管道作者允许在管道中设置的变量。 如果没有此设置,管道作者可以使用日志记录命令声明无限制的新变量setvariable
。 指定空列表with settableVariables
时,禁止设置任何变量。 有关详细信息,请参阅settableVariables
YAML 架构中的属性。
保护机密的最佳方法是首先没有机密。 避免尽可能使用机密,切勿将它们存储在 YAML 文件中,并确保它们不会被记录或打印以维护安全性。
- 尽量避免使用机密:请检查管道是否能够通过其他方法(例如通过工作负载身份联合或托管身份进行服务连接)而不是使用机密来执行任务。 托管标识允许应用程序和服务使用 Azure 进行身份验证,而无需显式凭据。 如需了解更多信息,请参阅 使用服务主体和托管标识。 请勿在 YAML 中放置机密:切勿在 Azure Pipelines .yml 文件中将敏感值存储为纯文本。
- 不要记录或输出机密:避免将机密回显到控制台,在命令行参数中使用它们,或将它们记录到文件中。 Azure Pipelines 会尝试尽可能从日志中清理机密,但无法捕获机密泄露的每一种方式。
- 不要将结构化数据(如 JSON)用作机密:为每个敏感值创建单个机密。 此方法可确保更好的修订准确性,并最大程度地减少无意中公开敏感数据的风险。
审核和轮换机密
若要保护管道,请定期审核任务和日志中的机密处理,查看和删除不必要的机密,并轮换机密以最大程度地降低安全风险。
- 审核任务和日志中的机密处理:检查任务以确保机密不会发送到主机或打印到日志。 验证任何日志文件中是否有机密,包括错误日志。
- 查看已注册的机密:确认管道中的机密仍是必需的,并删除不再需要的任何机密,以减少混乱和潜在的安全风险。
- 轮换机密:定期轮换机密,以最大程度地减少可能利用泄露的机密的时间范围。
防止恶意代码执行
为了确保仅经过测试和消毒的代码运行通过您的管道,请定期审查您的管道以识别常见问题。
- 代码扫描:转义参数中的特殊字符以避免 shell 命令注入。 可以使用 Azure DevOps 的 GitHub 高级安全性 自动执行代码扫描。
- 验证输入并使用参数:验证输入参数和参数以防止意外行为。 在脚本中使用参数化查询来防止 SQL 注入。 运行时参数 有助于避免与变量(如 参数注入)相关的安全问题。
-
不要在脚本中使用 PATH:依赖代理
PATH
的设置很危险,因为它可由以前的脚本或工具更改。 始终使用完全限定的路径。 - 控制可用任务:禁用从市场安装和运行任务的功能,从而可以更好地控制管道中执行的代码。
保护容器
了解如何通过配置更改、扫描和策略保护容器。
-
将卷标记为只读:容器包括系统提供的卷装载,用于处理主机代理所需的任务、工具和外部组件。 设置
externals
、tasks
和tools
为只读,以增加安全性。 - 设置特定于容器的资源限制:设置 CPU 和内存限制,以防止容器消耗过多的资源,这可能导致拒绝服务或安全漏洞。
-
使用受信任的映像:使用来自信誉良好的源(例如 Azure 容器注册表或 Docker 中心)的官方和已验证映像。 始终指定特定的版本或标记来保持一致性和可靠性,而不是依赖于
latest
标记。 定期更新基本映像,以包括最新的安全修补程序和 bug 修复。 - 扫描容器中的漏洞并强制实施运行时威胁防护:使用 Microsoft Defender for Cloud 等工具来监视和检测安全风险。 此外,Azure 容器注册表还提供集成的 漏洞扫描 ,以帮助确保在部署之前确保容器映像安全。 还可以通过 Azure DevOps 扩展集成第三方扫描工具,以便进行添加的安全检查。
- 实施安全策略以防止特权升级并确保容器运行所需的特权最少:例如,Azure Kubernetes 服务(AKS)、 基于角色的访问控制和 Pod 安全允许 允许你强制实施限制容器特权的策略,确保非根执行,并限制对关键资源的访问。
- 利用网络策略: 网络策略 可用于限制容器之间的通信,确保只有授权的容器可以访问网络中的敏感资源。 此外,可以应用 适用于 AKS 的 Azure Policy 来强制实施容器安全最佳做法,例如确保仅部署受信任的容器映像。
使用模板强制实施最佳做法
从最小模板开始,并逐步强制实施扩展。 此方法可确保在实施安全做法时,具有涵盖所有管道的集中起点。
- 使用扩展模板:扩展模板定义外部结构,并为目标自定义项提供特定点。 使用扩展模板 可以防止恶意代码渗透管道。
- 通过步骤限制访问:通过执行下载包等步骤(而不是在主机上运行包)来限制网络访问。 当步骤在容器中运行时,可以阻止错误的执行组件修改代理配置或留下恶意代码供以后执行。