В этой заметке не буду обсуждать вопрос зачем поднимать свой git-сервер, а не использовать github/gitlab/codeberg или даже sourceforge.

Как развернуть свой легкий git сервер

Начнем с того, что самый простой способ - это создать git --bare init репозиторий на вашем сервере и написать обычные скрипты для выполнения автоматических задач. Собственно этот блог собирался скриптом который запускался в cron, делал git pull репозитория с кодом блога, а потом собирал и публиковал результат.

Из плюсов подхода - всё очень просто, из минусов - всё очень просто.

Forgejo/Gitea

Что там с github дома? Есть отличный легковесный вариант - Forgejo - форк Gitea, который форк Gogs. Очень похож по функционалу на porngithub (issue, wiki,…) с приятным внешним видом:

Установка с помощью Docker

Создаем docker-compose.yaml:

version: "3"

networks:
  gitea:

volumes:
  gitea:

services:
  gitea:
    image: codeberg.org/forgejo/forgejo:1.18.0-1
    restart: always
    ports:
      - "3000:3000"
      - "3022:22"
    networks:
      - gitea
    volumes:
      - gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
docker-compose up -d

Остаётся опубликовать 3000 порт в nginx:

server{
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name code.example.com;

    location / {
      proxy_pass http://localhost:3080;
      client_max_body_size 16M;
    }

    ssl_certificate /etc/.../code.example.com.cer;
    ssl_certificate_key /etc/.../code.example.com.key;
}

Заходим в браузере на code.example.com и настраиваем Forgejo. Из коробки сервис может работать с базой sqlite, для личного использования и маленьких команд этого достаточно. Если нужна более продвинутая база - можете почитать по ссылке.

Настройка SSH Passthrough

Доступ git к сервису по ssh можно настроить несколькими способами:

  • опубликовать ssh порт контейнера (в примере выше 3022)
  • использовать ssh сервер хост системы.

Чтобы настроить второй способ нам необходимо:

  • одинаковый пользователь git в хост-системе и в контейнере;
  • общий каталог с ключами для пользователя git в обеих системах;
  • возможность передавать команды из ssh-сервера хоста в контейнер.

На хосте создадим пользователя git:

sudo adduser --system --shell /bin/bash --gecos 'Git Version Control' \
  --group --disabled-password --home /home/git git

Получаем uid и gid пользователя:

id git
uid=118(git) gid=118(git)

Исправим docker-compose.yaml:

...
services:
  gitea:
    ...
    volumes:
      - gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
      # пробрасываем ключи пользователя
      - /home/git/.ssh/:/data/git/.ssh
    environment:
      # задаём uid и gid пользователю git
      - USER_UID=118
      - USER_GID=118

Далее создайте пользователю git ключи и добавьте публичный в authorized_keys. Т.к. каталог .ssh на хосте и в контейнере общий - это позволит пользователю git выполнять команды по ssh из хост системы в контейнере. Проверить можно так:

ssh -p 3022 -i /home/git/.ssh/id_rsa git@localhost hostname

Следующий шаг, создадим скрипт который будет пробрасывать команды из ssh-сервера хоста в контейнер, сделаем его исполняемым и назначим в качестве shell пользователю git:

cat <<"EOF" | sudo tee /home/git/ssh-shell
#!/bin/sh
shift
ssh -p 3022 -o StrictHostKeyChecking=no [email protected] "SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $@"
EOF
sudo chmod +x /home/git/ssh-shell
sudo usermod -s /home/git/ssh-shell git

Добавим в /etc/ssh/sshd_config блок и перезапустим sshd:

Match User git
  AuthorizedKeysCommandUser git
  AuthorizedKeysCommand /usr/bin/ssh -p 3022 -o StrictHostKeyChecking=no [email protected] /usr/local/bin/gitea keys -e git -u %u -t %t -k %k

AuthorizedKeysCommand - позволяет sshd вместо authorized_keys использовать скрипт который должен вернуть публичный ключ пользователя, например из какого-то внешнего хранилища или базы.

Получается следующая схема:

  1. Внешний пользователь загружает через web интерфейс свой публичный ключ в Forgejo
  2. Подключается к ssh-серверу хоста под пользователем git используя свой приватный ключ
  3. ssh сервер с помощью параметра AuthorizedKeysCommand пытается получить публичный ключ из хранилища gitea (… /usr/local/bin/gitea keys -e git -u %u …)
  4. Если удалось проверить ключи - в качестве оболочки для пользователя используется наш ssh-shell который передает ssh команды внутрь контейнера.

В документации всё это описано более подробно и есть еще несколько вариантов настройки ssh passthrough.

После всех настроек у нас должна появиться возможность работать с git по ssh:

git clone [email protected]:jonhconnor/t800.git

или в существующем репозитории

git remote add origin [email protected]:jonhconnor/t800.git