容器化部署私人代码管理平台 Gitea ,并设置 SSH 穿透

2019-05-15

背景

  • Github 被 M$ 爸爸收购后虽然开放了无限免费私有仓库,但是团队项目想私有还是要付费订阅。

  • Gitlab 虽然可以免费团队私有但是大陆访问速度实在是慢。

这时自己搭建一个 Git 服务就很方便了。加上容器化技术,可以轻易完成。

本文采用的 Git 服务:Gitea

基础架设

以下是一个简单的 docker-compose.yml 样例配置文件

version: "2"

services:
  server:
    image: gitea/gitea
    environment:
      - DB_TYPE=mysql
      - DB_HOST=db:3306
      - DB_NAME= # Fill with your OWN
      - DB_USER= # Fill with your OWN
      - DB_PASSWD= # Fill with your OWN
    restart: always
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "8080:3000"
      - "127.0.0.1:2222:22"
    depends_on:
      - db
    links:
      - db

  db:
    image: mysql:5.7
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD= # Fill with your OWN
      - MYSQL_USER= # Fill with your OWN
      - MYSQL_PASSWORD= # Fill with your OWN
      - MYSQL_DATABASE= # Fill with your OWN
    volumes:
      - ./mysql:/var/lib/mysql

相信阅读此文的读者已经有相关知识背景,就不再详细解释每项配置的含义了。

随后,我们加一个反代就可以上线了。

example.com {
    proxy / http://127.0.0.1:8080 {
        transparent
    }
    log / log/git.log
}

首次进入系统需要初始化并创建管理员账户,所以最好再加一个 basicauth

basicauth / USERNAME PASSWORD

简单配置

Gitea 的配置文件为 ./gitea/gitea/conf.ini,修改其来完成一些配置。

APP_NAME = # 应用名称

[server]
HTTP_PORT        = 3000 # 本地监听端口
ROOT_URL         = https://git.example.com/ # 你的基地址
SSH_LISTEN_PORT  = 22 # ssh 本地监听端口
SSH_DOMAIN       = git.example.com # 你的域名,用于生成 ssh url
SSH_PORT         = 2222 # 生成 repo ssh url 时使用的端口(即暴露端口)
DOMAIN           = git.example.com # 你的域名

[service]
DISABLE_REGISTRATION    = true # 禁用注册
REQUIRE_SIGNIN_VIEW     = true # 登陆后才能查看内容

[openid]
ENABLE_OPENID_SIGNIN = false # 禁用 openid 登录
ENABLE_OPENID_SIGNUP = false # 禁用 openid 注册

如此简单配置一波,你已经拥有这个私人专属豪华定制的代码管理平台了。

SSH 穿透

但此时,SSH 暴露端口为 2222,等于说每次使用 Git+SSH 时都需要输入端口号!

ssh://git@git.example.com:2222/username/repo.git

丑陋不堪!

思路

日常使用 Git+SSH 时,相当于我们用 git 用户登录远程服务器去操作文件。

那我们是不是可以在宿主机也创建 Git 用户去做容器内的 Git 用户的代理人?

1. 创建宿主机 Git 用户

注意,此处的 Git 用户的 UID/GID 应与容器内 Git 用户相同。

adduser -u {GIT_UID} -g {GIT_GID} --system --shell /bin/bash --gecos "Git Version Control" --disabled-password --home /home/git git

其中,GIT_UIDGIT_GID 也应在 docker-compose.yml 中指明:

environment:
  - USER_UID={GIT_UID}
  - USER_GID={GIT_GID}

随后,为其生成 SSH 密钥对:

sudo -u git ssh-keygen -t rsa -b 4096 -C "Gitea Host Key"

2. 创建伪可执行文件

创建 /app/gitea/gitea 文件,赋予执行权限并写入以下内容:

#!/bin/sh
ssh -p 2222 -o StrictHostKeyChecking=no git@127.0.0.1 "SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@"

这就好像二级经销商。客户来买车,不是买我的库存,而是我一收到客户的订单就去 4S 店进一辆,再转卖给他。

3. 链接授权公钥列表文件

将容器内 Git 用户与宿主机 Git 用户的 ~/.ssh/authorized_keys 文件捆绑,这意味着被授予容器内权限的密钥也可以在宿主机获得权限。

这很好理解,就像 4S 店卖车给一些人,二级代理经销商也会卖给那些人。

有几种不同的方法可以做到:

  1. 创建符号链接
ln -s ./gitea/git/.ssh/authorized_keys /home/git/.ssh/authorized_keys
  1. Volume 挂载
volumes:
  - /home/git/.ssh/authorized_keys:/data/git/.ssh/authorized_key

个人更倾向于方法 2,因为这样都写在 compose 配置文件里,耦合性更低,不用涉及系统、磁盘层面的配置。

4. 授权宿主机访问容器的权限

二级经销商和 4S 店交易需要授权,不能你随便倒买倒卖我家产品。

可以通过如下命令,令客户机信任宿主机的公钥。

echo "no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty $(cat /home/git/.ssh/id_rsa.pub)" >> /var/lib/gitea/git/.ssh/authorized_keys

到这一步,就已经大功告成啦!

如果你的宿主机 SSH 监听在 22 端口,那么你以及可以直接通过 ssh://git@git.example.com/username/repo.git 访问你的仓库了!

修改 URL 生成配置

还记得之前 conf.ini 的配置中我们把 SSH Port 设置为 2222 吗?现在我们已经可以改掉他了。

把他修改为 22,重启,就可以看到 Web 端生成的 SSH URL 已经没有讨厌的端口号了!