从 GitLab CI 到远程服务器的无密码 SSH

Passwordless SSH from GitLab CI to Remote Server

最近我偶然发现了一个 SSH 问题,我无法弄清楚缺少什么。我们使用 GitLab CI 构建项目并将其部署到我们的远程服务器之一。作为升级计划的一部分,我们需要用新的 RHEL 7 服务器替换性能下降的 Debian 6 服务器。我无法让无密码 SSH 从 GitLab Runner 直接工作到远程机器。

我在Dockerfile中创建了一个可重现的示例,远程服务器的IP和用户被替换为非敏感数据。

FROM centos:7
RUN yum install -y epel-release
RUN yum update -y
RUN yum install -y openssh-clients
RUN useradd -m joe
RUN mkdir -p /home/joe/.ssh
COPY id_rsa_shared /home/joe/.ssh/id_rsa
RUN echo "Host *\n\tStrictHostKeyChecking no\n" >> /home/joe/.ssh/config
RUN ssh-keyscan 10.x.x.x >> /home/joe/.ssh/known_hosts
RUN chown -R joe:joe /home/joe/.ssh
USER joe
CMD ["/bin/bash"]

文件 id_rsa_shared 是使用以下命令在本地计算机上创建的:

ssh-keygen -t rsa -b 2048 -f ./id_rsa_shared

ssh-copy-id -i ./id_rsa_shared joe@10.x.x.x

这适用于本地。 docker 容器中的简单 ssh joe@10.x.x.x uname -a 将输出以下内容:

Linux newweb01p.company.local 3.10.0-1160.25.1.el7.x86_64 #1 SMP Tue Apr 13 18:55:45 EDT 2021 x86_64 x86_64 x86_64 GNU/Linux

但是,如果我将其作为 GitLab CI 脚本提交到一个分支,如图所示:

image: centos:7

stages:
  - deploy

dev-www:
  stage: deploy
  tags:
    - docker
  environment:
    name: dev-www
    url: http://dev-www.company.local
    variables:
      DEV_HOST: 10.x.x.x
      APP_ENV: dev
      DEV_USER: joe
  script:
    - whoami
    - yum install -y epel-release
    - yum update -y
    - yum install -y openssh-clients
    - useradd -m joe
    - mkdir -p /home/joe/.ssh
    - cp "./gitlab/known_hosts" /home/joe/.ssh/known_hosts
    - echo "$DEV_USER_OPENSSH_KEY" >> /home/joe/.ssh/id_rsa
    - echo "Host *\n\tStrictHostKeyChecking no\n" >> /home/joe/.ssh/config
    - chown -R joe:joe /home/joe/.ssh/
    - chmod 600 /home/joe/.ssh/*
    - chmod 700 /home/joe/.ssh
    - ls -Fsal /home/joe/.ssh
    - su - joe
    - ssh -oStrictHostKeyChecking=no "${DEV_USER}@${DEV_HOST}" uname -a
  when: manual

如图所示,管道将无法通过身份验证:

Running with gitlab-runner 13.12.0 (7a6612da)
    on docker.hqgitrunner01d.company.local K47w1s77
Preparing the "docker" executor
Using Docker executor with image centos:7 ...
Authenticating with credentials from job payload (GitLab Registry)
Pulling docker image centos:7 ...
Using docker image sha256:xxx for centos:7 with digest centos:7@sha256:xxxx ...
Preparing environment
Running on runner-k47w1s77-project-93-concurrent-0 via hqgitrunner01d.company.local...
Getting source from Git repository
Fetching changes...
Reinitialized existing Git repository in /builds/webversion3/API/.git/
Checking out 6a7c193b as tdr/psr4-composer...

Updating/initializing submodules recursively...
Executing "step_script" stage of the job script
Using docker image sha256:xxx for centos:7 with digest centos:7@sha256:xxx ...
$ whoami
root
$ useradd -m joe
$ mkdir -p /home/joe/.ssh
$ cp "./gitlab/known_hosts" /home/joe/.ssh/known_hosts
$ echo "$DEV_USER_OPENSSH_KEY" >> /home/joe/.ssh/id_rsa
$ echo "Host *\n\tStrictHostKeyChecking no\n" >> /home/joe/.ssh/config
$ chown -R joe:joe /home/joe/.ssh/*
$ chmod 600 /home/joe/.ssh/*
$ chmod 700 /home/joe/.ssh
$ ls -Fsal /home/joe/.ssh
total 16
0 drwx------ 2 root root  53  Apr  1 15:19 ./
0 drwx------ 3 joe  joe   74  Apr  1 15:19 ../
4 -rw------- 1 joe  joe   37  Apr  1 15:19 config
4 -rw------- 1 joe  joe 3414  Apr  1 15:19 id_rsa
8 -rw------- 1 joe  joe 6241  Apr  1 15:19 known_hosts
$ su - joe
$ ssh -oStrictHostKeyChecking=no "${DEV_USER}@${DEV_HOST}" uname -a
Warning: Permanently added '10.x.x.x' (ECDSA) to the list of known hosts.
Permission denied, please try again.
Permission denied, please try again.
Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).

Cleaning up file based variables
ERROR: Job failed: exit code 1

也许我错过了一个步骤,因为我收到“权限被拒绝,请重试”消息。如何让 Docker Executor 使用无密码 SSH 连接到远程服务器?

只需内联所有 ssh 选项。使用 -i 指定您的密钥文件。您还可以使用 -o UserKnownHostsFile 来指定您已知的主机文件——您不需要将其全部复制到 ssh 配置中。

这应该足以让 ssh 成功:

# ...
- echo "$DEV_USER_OPENSSH_KEY" > "${CI_PROJECT_DIR}/id_rsa.key"
- chmod 600 "${CI_PROJECT_DIR}/id_rsa.key"
- |
  ssh -i "${CI_PROJECT_DIR}/id_rsa.key" \
      -o IdentitiesOnly=yes \
      -o UserKnownHostsFile="${CI_PROJECT_DIR}/gitlab/known_hosts" \
      -o StrictHostKeyChecking=no \
      user@host ...

此外,由于您要禁用 StrictHostKeyChecking,您也可以只使用 /dev/null 作为 UserKnownHostsFile。如果要进行密钥检查,请省略 StrictHostKeyChecking=no 选项。

解决方案非常简单明了。重要的部分是理解 SSH。

解决方案有效。来自 .gitlab-ci.yml 的片段,供与我有同样问题的人使用。

    ...
    - mkdir -p ~/.ssh
    - touch ~/.ssh/id_rsa ~/.ssh/config ~/.ssh/known_hosts
    - chmod 600 ~/.ssh/id_rsa ~/.ssh/config ~/.ssh/known_hosts
    - echo "$OPENSSH_KEY" >> ~/.ssh/id_rsa
    - echo "Host *\n\tStrictHostKeyChecking no" >> ~/.ssh/config
    - ssh-keyscan ${DEV_HOST} >> ~/.ssh/known_hosts