在 GitLab CI/CD 中使用 SSH 密钥

目前没有在构建环境(GitLab Runner 运行的地方)中管理 SSH 密钥的内置支持。

在以下情况下使用 SSH 密钥:

  1. 检出内部子模块
  2. 使用包管理器(例如 Bundler)下载私有包
  3. 将您的应用程序部署到您自己的服务器,或者例如 Heroku
  4. 从构建环境到远程服务器执行 SSH 命令
  5. 将文件从构建环境 rsync 到远程服务器

对于实现上述任何一项,您很可能需要一个 SSH 密钥。

最广泛支持的方法是,通过扩展您的.gitlab-ci.yml 将一个 SSH 密钥注入你的构建环境,这是一个适用于任何类型的 executor(例如 Docker 或 shell)的解决方案。

工作原理

  1. 在本地创建一个新的 SSH 密钥对 ssh-keygen
  2. 将私钥作为变量添加到您的项目中。
  3. 在作业期间运行 ssh-agent 来加载私钥。
  4. 如果您正在访问私有仓库,将公钥复制到您想要访问的服务器(通常在 ~/.ssh/authorized_keys 中)或将其添加为部署密钥。

在以下示例中,ssh-add - 命令不会在作业日志中显示 $SSH_PRIVATE_KEY 的值,但如果您启用 debug logging,它可能会暴露。您可能还想检查流水线的可见性

使用 Docker executor 时的 SSH 密钥

当您的 CI/CD 作业在 Docker 容器内运行(意味着包含环境),并且您希望将代码部署在私有服务器中时,您需要一种访问它的方法。在这种情况下,您可以使用 SSH 密钥对。

  1. 您首先必须创建一个 SSH 密钥对。有关更多信息,请参阅生成 SSH 密钥不要向 SSH 密钥添加密码,否则 before_script 会提示输入。

  2. 创建一个新的 CI/CD 变量。输入名称 SSH_PRIVATE_KEY 作为 Key,然后在 Value 字段中粘贴您之前创建的私钥的内容。

  3. 使用 before_script 操作修改你的 .gitlab-ci.yml。在以下示例中,假设是基于 Debian 的镜像。根据您的需要编辑:

    before_script:
      ##
      ## Install ssh-agent if not already installed, it is required by Docker.
      ## (change apt-get to yum if you use an RPM-based image)
      ##
      - 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'
    
      ##
      ## Run ssh-agent (inside the build environment)
      ##
      - eval $(ssh-agent -s)
    
      ##
      ## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
      ## We're using tr to fix line endings which makes ed25519 keys work
      ## without extra base64 encoding.
      ## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556
      ##
      - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    
      ##
      ## Create the SSH directory and give it the right permissions
      ##
      - mkdir -p ~/.ssh
      - chmod 700 ~/.ssh
    
      ##
      ## Optionally, if you will be using any Git commands, set the user name and
      ## and email.
      ##
      # - git config --global user.email "user@example.com"
      # - git config --global user.name "User name"
    

    before_script 可以全局设置或按作业设置。

  4. 确保私有服务器的 SSH 主机密钥已验证

  5. 作为最后一步,将您在第一步中创建的公钥添加到您希望在构建环境中访问的服务。如果您正在访问私有 GitLab 仓库,则必须将其添加为部署密钥。

您现在可以访问构建环境中的私有服务器或仓库。

使用 Shell executor 时的 SSH 密钥

如果您使用的是 Shell executor 而不是 Docker,则设置 SSH 密钥会更容易。

您可以从安装了 GitLab Runner 的机器生成 SSH 密钥,并将该密钥用于在该机器上运行的所有项目。

  1. 首先,登录到运行您的作业的服务器。

  2. 然后,从终端以 gitlab-runner 用户身份登录:

    sudo su - gitlab-runner
    
  3. 按照生成 SSH 密钥的说明生成 SSH 密钥对。不要向 SSH 密钥添加密码,否则 before_script 会提示输入。

  4. 作为最后一步,将您之前创建的公钥添加到您希望在构建环境访问的服务中。如果您正在访问私有 GitLab 仓库,则必须将其添加为部署密钥。

生成密钥后,尝试登录远程服务器接受指纹:

ssh example.com

验证 SSH 主机密钥

建议检查私有服务器自己的公钥,以确保您没有成为中间人攻击的目标。如果发生任何可疑情况,因为作业失败(当公钥不匹配时 SSH 连接失败),您会注意到。

要找出服务器的主机密钥,请从受信任的网络(理想情况下,从私有服务器本身)运行 ssh-keyscan 命令:

## Use the domain name
ssh-keyscan example.com

## Or use an IP
ssh-keyscan 1.2.3.4

创建一个新的 CI/CD 变量,将 SSH_KNOWN_HOSTS 作为 “Key”,并添加 ssh-keyscan 的输出作为 “Value”。

如果必须连接多台服务器,则所有服务器主机密钥必须收集在变量的 Value 中,每行一个密钥。

note通过在 .gitlab-ci.yml 中直接使用变量而不是 ssh-keyscan,如果主机域名因某种原因发生变化,您不必更改 .gitlab-ci.yml。此外,这些值是由您预定义的,这意味着如果主机密钥突然更改,CI/CD 作业不会失败,此时是服务器或网络存在问题。

现在创建了 SSH_KNOWN_HOSTS 变量,除了上面的 .gitlab-ci.yml 的内容之外,还必须添加:

before_script:
  ##
  ## Assuming you created the SSH_KNOWN_HOSTS variable, uncomment the
  ## following two lines.
  ##
  - echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
  - chmod 644 ~/.ssh/known_hosts

  ##
  ## Alternatively, use ssh-keyscan to scan the keys of your private server.
  ## Replace example.com with your private server's domain name. Repeat that
  ## command if you have more than one server to connect to.
  ##
  # - ssh-keyscan example.com >> ~/.ssh/known_hosts
  # - chmod 644 ~/.ssh/known_hosts

  ##
  ## You can optionally disable host key checking. Be aware that by adding that
  ## you are susceptible to man-in-the-middle attacks.
  ## WARNING: Use this only with the Docker executor, if you use it with shell
  ## you will overwrite your user's SSH config.
  ##
  # - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >> ~/.ssh/config'