背景

在使用 Docker 搭建 Gitlab/Gitee 会导致无法与主机端共用 22 端口,这导致 ssh 连接的时候会使用形如 ssh://git@git.xxxx.cn:4022/zs/zsblog.git 的 ssh 链接,而不是像官方 Gitlab 那种非常干净的 git@git.xxxx.cn/zs/zsblog.git 链接。这对于我这种强迫症而言非常的难受啊,但因为主机的 22 端口已经被占用了,无法共用,所以需要考虑两者共享端口的问题。

虽说是两者共用,但其实还是使用类似于端口转发的特点,简单说就是在主机设置 git 用户,然后通过一个脚本将 git 用户的所有 ssh 流量转发到 Gitlab 容器中,从而完成对应的事情。

关于 Gitee 的设置,Gitee 官方的 Docker 部署教程1已经说的很清楚了,按照该步骤执行完全没问题。

而关于 Gitlab 貌似没有比较详尽的教程,搜索后发现了一个 Issue2 以及一篇博文3,后者讲的比较清楚,但是经过实践后发现存在一定问题,因此决定将可行的方案记录下来。

具体步骤

一、初始设置

在开始之前,docker-compose.yml 中设置比较关键的几个配置如下:

gitlab-web:
	image: 'gitlab/gitlab-ce:latest'
	container_name: 'gitlab'
	restart: always
	environment:
		GITLAB_OMNIBUS_CONFIG: |
			gitlab_rails['gitlab_shell_ssh_port'] = 4022
	ports:
		- '3090:80'
		- '4022:22'
		- '6060:6060'
	volumes:
		- '/srv/gitlab/config:/etc/gitlab'
		- '/srv/gitlab/logs:/var/log/gitlab'
		- '/srv/gitlab/data:/var/opt/gitlab'
		- .... #一些其他的配置

如上设置基本可以确保 Gitlab 形如 ssh://git@git.xxxx.cn:4022/zs/zsblog.git 的链接可以使用。

二、在 Host 宿主机创建与 Gitlab 相同的 git user

为了确保后续一些麻烦的权限问题,我们需要在宿主机也创建一个 git 用户。

首先我们要检查 Gitlab 容器中 git 用户的 UID 以及 GID,如果不出意外的话,两者都已被硬编码为 998 。不过为了以防万一,我们可以通过下述指令进一步确认:

❯ docker exec -it gitlab cat /etc/passwd | awk -F':' '{if($1=="git") printf("uid: %s; gid: %s\n"), $3, $4}'
# 不出意外结果如下,证明 git 账户的 UID 与 GID 都为 998.
uid: 998; gid: 998

这条指令会给出我们对应的 UID 以及 GID。

接下来我们需要在宿主机中也创建 git 用户,确保 UID 及 GID 都与 Gitlab 容器内的一致。大家可以通过如下指令查看是否已有 git 用户,以及其 UID 与 GID。

❯ cat /etc/passwd | awk -F':' '{if($1=="git") printf("uid: %s; gid: %s\n"), $3, $4}'
  • 如果没有任何结果,说明没有 git 用户
  • 如果有结果,并且 UID 与 GID 不为 998,说明需要重新该一下对应的 UID 与 GID,大家可以自行搜索解决方案。
  • 我们还需要查看 /etc/passwd/etc/group 两个文件确保 998 没有被使用,如果有使用也需要做对应改变。

如果没有 git 用户,则使用下面指令创建并指定家目录为 /home/git (在 root 用户下)

❯ groupadd -g 998 git
❯ useradd -m -u 998 -g git -s /bin/sh -d /home/git git

如果已有,则确保其 UID 与 GID 和 Gitlab 容器中 git 用户相同,并确保有家目录 /home/git

三、复制 Gitlab 密钥文件

Gitlab 的密钥文件目前存放在容器的 /var/opt/gitlab/.ssh 中,然后根据你在 docker-compose.yml 配置的映射位置看,好比我配置的映射是 /srv/gitlab/data:/var/opt/gitlab,你就可以直接在 /srv/gitlab/data/.ssh 目录下找到对应的密钥文件。

然后首先在 root 用户权限下执行

❯ cp -r /srv/gitlab/data/.ssh /home/git/.ssh

将对应的文件夹复制到 git 用户家目录下,然后执行

❯ chown -R git:git /home/git/.ssh

将所有的文件的权限及组权限都更改为 git 用户

现在 git 用户的 .ssh 目录结构应该如下,也要确保 .ssh 文件权限为 700authorized_keys 权限为 6004

❯ ls -la
total 72
drwx------ 2 git git  4096 Apr 24 15:40 .
drwxr-xr-x 5 git git  4096 Apr 24 02:00 ..
-rw------- 1 git git 50122 Apr 24 02:00 authorized_keys
-rw-r--r-- 1 git git     0 Apr 24 01:57 authorized_keys.lock
-rw------- 1 git git  1679 Apr 24 01:48 id_rsa
-rw-r--r-- 1 git git   390 Apr 24 01:48 id_rsa.pub
-rw-r--r-- 1 git git   222 Apr 24 02:00 known_hosts

四、生成密钥文件

切换到 git 用户并生成密钥对

❯ su - git
❯ ssh-keygen #然后一路回车

然后将公钥加入 authorized_keys

❯ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys 

五、创建脚本文件

我们不妨看一下现在的 authorized_keys 文件,里面应该有形如下面的内容一些行

command="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell key-105",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 xxxxxxxx

这里说明我们提交时会执行一个 /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell 脚本,这里本应该执行的是容器内的脚本,但是我们现在把它放在了容器外,因此我们需要设置一个脚本将请求转发进去,因此选择在本机的 /opt/gitlab/embedded/service/gitlab-shell/bin 目录下创建一个名为 gitlab-shell 的脚本,命令如下:

# 创建文件夹
❯ sudo mkdir -p /opt/gitlab/embedded/service/gitlab-shell/bin
# 进入文件夹
cd /opt/gitlab/embedded/service/gitlab-shell/bin
# 创建文件
❯ sudo vim gitlab-shell

文件内容如下:

#!/bin/sh

ssh -i /home/git/.ssh/id_rsa -p 4022 -o StrictHostKeyChecking=no git@127.0.0.1 "SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@"

需要注意的是,这里的 4022 是指在 docker-compose.yml 文件映射的 4022:22 端口,如果你是 xxx:22 ,则需要在这里填写 xxx,对应起来。内

然后不要忘记添加执行权限

❯ sudo chmod +x /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell

六、挂载额外的文件到 Gitlab 容器

修改我们的 docker-compose.yml ,注释掉 gitlab_rails['gitlab_shell_ssh_port'] = 4022 以及添加 '/home/git/.ssh/:/var/opt/gitlab/.ssh' ,将 git 用户的 .ssh 目录挂载到容器内。

gitlab-web:
	image: 'gitlab/gitlab-ce:latest'
	container_name: 'gitlab'
	restart: always
	environment:
		GITLAB_OMNIBUS_CONFIG: |
				....
	ports:
		- '3090:80'
		- '4022:22'
		- '6060:6060'
	volumes:
		- '/srv/gitlab/config:/etc/gitlab'
		- '/srv/gitlab/logs:/var/log/gitlab'
		- '/srv/gitlab/data:/var/opt/gitlab'
		- '/home/git/.ssh/:/var/opt/gitlab/.ssh'
		- .... #一些其他的配置

最后,重启容器即可生效。

❯ docker-compose up -d

简单的原理说明

使用的原理就是将主机 git 用户的所有 ssh 流量都转发到容器内部。

转发使用的是宿主机中的/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell 脚本,而因为已经将主机 git 用户 id_rsa.pub 添加到了 authorized_keys 中,因此可以直接免密转发内容到容器内部。

随后,容器内的 gitlab-shell 脚本对请求进行处理,完成 ssh 请求。

这里很巧妙的使用了同一个位置的脚本,这也是需要将 git 用户的 .ssh 目录挂载到 Gitlab 内部的原因,但是每个脚本却作用不同,十分的巧妙。