Hashicorp Vault 集成

Hashicorp Vault 是一个密钥管理系统 (KMS),它允许您自行管理一组密钥(凭据)、配置项以及对这些密钥的访问。

Travis CI 可以在您的 Travis CI 构建的构建、测试或部署阶段从您的 Hashicorp Vault 实例获取所需的密钥,而不是将它们存储在 Travis CI 中。

如果您所在的组织更倾向于完全控制密钥的访问权限,或者希望在一个密钥管理系统 (KMS) 中方便地管理 CI/CD 管道中所需的密钥,那么这将非常有用。

先决条件 #

您将需要

  • 一个启用了 KV 引擎并支持 KV2 API 的 Hashicorp Vault 实例的 URL(请参阅 Hashicorp Vault 文档)。
    • Hashicorp Vault 实例 API 必须接受来自公共网络的传入请求;请参阅我们的 IP 地址页面,了解必须允许哪些内容以及有关配置网络规则的一些建议。
  • Hashicorp Vault 中所需密钥的路径。
  • 一个令牌;通过 API 或通过 Hashicorp Vault CLI 登录 Hashicorp Vault 后获得(请保密!)。
    • 在将令牌授予 Travis CI 之前,我们建议您查看 Hashicorp Vault 帐户对密钥集的访问权限;最佳实践是将其限制在构建/测试/部署任务所需的项目中,您希望 Travis CI 执行这些任务。请考虑在 Hashicorp Vault 中使用一个专用帐户,该帐户仅访问与 CI/CD 相关的少量密钥。
  • 使用 Travis CI 加密令牌以供进一步使用
    • 请使用 travis-cli 加密密钥,并在您的 .travis.yml 文件中使用加密后的字符串;在初始版本中,我们有意将其限制为向您的 KMS 提供访问令牌的唯一方式。

集成是如何工作的? #

集成基于与 Hashicorp Vault API 的通信。Travis CI 中唯一存储的密钥是访问令牌(以加密形式)。

如果您使用的是专用 Hashicorp Vault 帐户,并且密钥需要在多个存储库之间共享,我们建议您考虑通过将其存储在一个存储库中来管理 Hashicorp Vault 访问令牌,并在您的组织中 将其导入其他存储库的构建配置。这样,您可以集中管理 Hashicorp Vault 访问令牌。但是,请注意,导入的共享配置将可用于构建矩阵中的所有作业。

首先,在您存储库的 .travis.yml 中定义一组简单的项目。然后,一旦 Travis CI 处理构建请求,就会发生以下步骤

  • 首先,在构建期间解密访问令牌。
  • 在特定构建作业开始时,.travis.yml 中定义的密钥将下载到特定的构建作业中,并在构建作业日志中进行屏蔽(以避免意外泄露这些密钥)。
    • 密钥将在特定构建作业的持续时间内存在,从而限制了可以使用这些密钥的时间。因此,请考虑从 Hashicorp Vault 获取密钥,并在 作业矩阵 中尽可能少的构建作业中根据需要处理这些密钥。
    • 从 Hashicorp Vault 的“密钥”节点获取密钥。
    • 仅获取已定义的密钥;如果未定义密钥路径,则不会从 Vault 实例获取任何内容。
  • KV 引擎中的 Hashicorp Vault 密钥以键值对的形式出现。密钥将转换为 Travis CI 构建作业中的环境变量名称;密钥值是环境变量的值。
    • 请注意:如果 Hashicorp Vault 中存在重复的密钥,并且通过 .travis.yml 中的定义获取这些密钥,则最后一个获取的值将覆盖前面的值;.travis.yml 中的出现顺序是从上到下处理的。
    • 环境变量由路径中的最后一个引用加上密钥组成,例如,如果 Hashicorp Vault 中存在一个 /ns1/project_id/secret_key_a 密钥,其中密钥为“message”,则在 Travis CI 构建作业中创建的环境变量名称将为:SECRET_KEY_A_MESSAGE
  • 获取密钥后,您可以在构建作业中使用它们;密钥的值将在 Travis CI 构建作业日志中进行屏蔽。

用法 #

Travis Ci 在 .travis.yml 中引入了新的 vault 节点。它可以在文件的根级别使用,也可以作为作业矩阵的一部分使用。

# use vault node to configure connection and secrets obtained from the Hashicorp Vault
vault:
  api_url: https://[your hashicorp vault endpoint here]
  token:
    secure: "..." # encrypted token value, alphanumeric string
  secrets:         # configure which secrets to obtain
# either define it via calling out the Hashicorp Vault namespace and providing relative paths to the secrets/namespace
    namespace: ns1
      - project_id/secret_key_a
      - project_id/another_key
# or define it by providing a list of paths relative to the ‘secrets’ node in the Hashicorp Vault
    - ns1/project_id/secret_key_a
    - ns1/project_id/another_key
# Remember always to obtain only secrets necessary for the specific build job!

请参阅下面的用法示例

单个 .travis.yml 文件 #

作为构建定义中所有作业可访问的数据

os: linux
dist: focal

# vault data at the root level of `.travis.yml` makes it available for all jobs in the build
vault:
  token: 
    secure: "Your encrypted token goes here."
  api_url: https://your-vault-api.endpoint
  secrets:
    - ns1/project_id/secret_key_a 

script:
#assuming that under /ns1/project_id/secret_key_a a secret with key ‘message’ is present
  - echo $SECRET_KEY_A_MESSAGE 

作为仅在一个作业中可访问的数据

os: linux
dist: focal

jobs:
  include:
    - name: “Vault job” # make vault secrets and connections only in this job
      vault:
        token: 
          secure: "Your encrypted token goes here.”
        api_url: https://your-vault-api.endpoint
        secrets:
          - ns1/project_id/secret_key_a
      script:
        - echo $SECRET_KEY_A_KEYNAME
    - name: "Another job" # this env variable contains nothing
      script: 
        - echo $SECRET_KEY_A_KEYNAME

导入的共享构建配置 #

vault:
  token: 
    secure: "Your encrypted token goes here."
  api_url: https://your-vault-api.endpoint
import:
  - source: vault-secrets.yml  
    mode: deep_merge_prepend #deep_merge for overwriting the values in vault-secrets.yml 

os: linux
dist: focal

jobs:
  Include:
    - name: "Vault job utilizing imported values job" # vault connection details come from imported vault-secrets.yml
        secrets:
          - ns1/project_id/secret_key_a
      script: 
        - echo $SECRET_KEY_A_MESSAGE
    - name: “Vault job overwriting imported values.”
      vault:
        token:   
          secure: "another encrypted token goes here.”
        api_url: https://different-vault-api.endpoint
        secrets:
          - ns1/project_id/secret_key_b
      script:
        - echo $SECRET_KEY_B_SOMEKEY

请注意:导入的内容可用于您的整个构建(.travis.yml),除非它被您 travis.yml 中的某些作业显式覆盖。YAML 锚点无法导入并在主 .travis.yml 中使用 - 请参阅 导入共享构建配置 页面了解更多信息。

常见问题 #

我可以在 UI 中将 Hashicorp Vault 令牌定义为环境变量吗? #

目前不行。目前,作为一项强制性实践,我们保持对令牌的限制性规则,该令牌强制作为 Travis CI 密钥提供给您的 Hashicorp Vault。但是,我们希望最大程度地降低在强制执行保护特别容易受到攻击的数据(例如对您的密钥管理系统的访问令牌)的实践时意外泄露您的密钥的风险。因此,如果您在 UI 中定义环境变量,如果构建作业没有看到 vault.token.secure,则它将不会连接到 Vault。默认情况下,它会阻止在构建作业日志中打印其解密内容,而不管 UI(存储库设置)中的选项是什么。

由于总有办法规避安全措施,因此我们希望听到您的意见,并了解将来是否应该放松仅在 vault.token.secure 下添加 Vault 令牌的限制。

我可以将 Hashicorp Vault 令牌作为加密的环境变量与派生存储库共享吗? #

目前不行。将来,我们计划允许它与派生存储库共享(用于从派生存储库到基本存储库提交 Pull Request 的协作模型)。但是,我们打算先观察一段时间您对此事的反馈。

是否仅支持 KV2 API 版本的 Hashicorp Vault 密钥值 API? #

为了支持旧版安装,内置了对 KV API 的支持。但是,它尚未经过广泛测试。请谨慎使用,并自行承担风险。

vault:
  api_url: url
  token:
    secure: secure_token
  secrets:
    - kv_api_ver: kv1 #optional, kv1 or kv2, default is kv2, single value
    - project_id2/secret_key