Настройка Gitlab self-managed за Nginx reverse proxy

2024.12.27
Установил на домашнем сервере во внутренней сети Gitlab self-managed, и захотелось прокинуть его в интернет за Gateway на основе Nginx, но тут воникает 2 проблемы: https и ssh. В статье разберем как это решать чтобы все работало как надо.

Развернул я себе Gitlab self-managed на своем домашнем сервере в локальной сети, и захотелось мне чтобы он был доступен из интернета.

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

Сразу можно найти инструкцию на сайте Gitlab и дальше идти по ссылкам, но у меня сходу не завелось и проблема была немного глубже, точнее их было несколько:

Для начала определимся с желаемым результатом:

А вот так это выглядит схематично:

У Gitlab есть свой Nginx, его мы почти не будем трогать, во всех остальных случаях речь будет идти про Nginx на внешней виртуалке. В любом случае договоримся что сервер Gitlab имеет IP 10.8.3.3, а внешний сервер Nginx 10.8.1.2.

Конфигурация Gitlab

Для настройки на Gitlab внешней TLS-терминации есть документация, а еще здесь пример.

У себя на сервере Gitlab (10.8.3.3) в конфиге /etc/gitlab/gitlab.rb:

external_url 'https://domain.zone'
gitlab_rails['trusted_proxies'] = ['10.8.0.0/16', '192.168.0.0/16', '{WHITE_IP}']
nginx['listen_port'] = 80
nginx['listen_https'] = false
nginx['real_ip_trusted_addresses'] = ['10.8.0.0/16', '192.168.0.0/16', '{WHITE_IP}']
nginx['proxy_set_headers'] = {
  "Host" => "$http_host_with_default",
  "X-Real-IP" => "$remote_addr",
  "X-Forwarded-For" => "$proxy_add_x_forwarded_for",
  "X-Forwarded-Proto" => "https",
  "X-Forwarded-Ssl" => "on",
  "Upgrade" => "$http_upgrade",
  "Connection" => "$connection_upgrade"
}

После правок конфига выполняем реконфигурацию:

$ gitlab-ctl reconfigure

Конфигурация Nginx

Конфиг для хоста в Nginx на прокси сервере 10.8.1.2 выглядит достаточно просто:

server {
    listen 443 ssl;
    server_name domain.zone;

    location /.well-known/acme-challenge/ {
        root /var/lib/letsencrypt;
    }

    ssl_certificate /etc/letsencrypt/live/domain.zone/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.zone/privkey.pem;
    
    location / {
        proxy_pass http://10.8.3.3;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Ssl on;
    }
}

Некоторую информацию по заголовкам можно почерпнуть из этого обсуждения, важно правильно передать X-Forwarded-Proto и X-Forwarded-Ssl чтобы избежать части проблем с CSRF-токеном.

Проблемы времени

Все выше достаточно просто и очевидно. Но неочевидным оказалось что любая авторизация в Gitlab заканчивалась ошибкой:

Поиски были долгими, не буду томить, а сразу покажу в чем была проблема (это обсуждение побудило смотреть на куки):

Здесь видно что на запрос авторизации сервер отправляет куки.

Если присмотреться, то дата истечения куки __gitlab_session в прошлом (неправильно), а заголовок Date в настоящем (правильно). Из-за неверного значения expires куки не устанавливаются браузером, и при отправке запроса авторизации сервер Gitlab не получает сессионную куку для авторизации.

Видимо заголовок Date отправляет сервер Gitlab и прокси его не трогает, а заголовок с куками модифицируется прокси сервером.

Намек на исправление проблемы я нашел в уже упоминаемом обсуждении, в этом комментарии.

Проблема в настройках времени на сервере с Nginx (10.8.1.2).

Смотрим текущее время сервера:

$ timedatectl 
               Local time: Сб 2022-01-01 12:00:06 MSK
           Universal time: Сб 2022-01-01 09:00:06 UTC
                 RTC time: Вт 2024-12-24 06:30:44
                Time zone: Europe/Moscow (MSK, +0300)
System clock synchronized: no
              NTP service: n/a
          RTC in local TZ: no

Видно что есть разница аж в годах между Universal time и RTC time.

На самом деле это я сделал специально для скриншотов статьи, разница при возникновении проблемы была в несколько часов.

Теперь установим и запустим NTPD:

$ sudo apt install ntp
$ sudo systemctl start ntpd
$ sudo systemctl enable ntpd

Еще раз проверим текуще время и убедимся что оно правильное:

$ timedatectl 
               Local time: Вт 2024-12-24 09:40:45 MSK
           Universal time: Вт 2024-12-24 06:40:45 UTC
                 RTC time: Вт 2024-12-24 06:40:45
                Time zone: Europe/Moscow (MSK, +0300)
System clock synchronized: no
              NTP service: n/a
          RTC in local TZ: no

Теперь дата истечения куки будет верная и авторизация должна пройти успешно.

Сквозной SSH порт

А как же работа с git через SSH? На данный момент это не будет работать, потому что запросы по SSH будут уходить на Nginx (10.8.1.2), а не на Gitlab (10.8.3.3).

Я решил использовать порт 2222 для git, потому что с Nginx сервером (10.8.1.2) мне нужно работать по SSH штатно на 22 порту. Схема такая:

client => nginx(10.8.1.2):2222 => gitlab(10.8.3.3):2222

В конфиге Gitlab есть директива gitlab_rails['gitlab_shell_ssh_port'] для указания порта SSH, но это лишь изменит порт в URL репозиториях, не более того:

Тем не менее значение в конфиге Gitlab (10.8.1.2) нужно установить:

gitlab_rails['gitlab_shell_ssh_port'] = 2222

И обязательно выполняем реконфигурацию Gitlab чтобы в URL появился порт для SSH:

$ gitlab-ctl reconfigure

Затем на сервере Gitlab в конфиг SSH-сервера (10.8.3.3), в моем случае OpenSSH, нужно добавить возможность работать и на порту 2222 (кроме 22) создав файл /etc/ssh/sshd_config.d/50_config.conf со следующим содержимым:

Port 22
Port 2222

После правки конфига рестарт службы SSH на сервере Gitlab(10.8.3.3):

$ sudo systemctl restart ssh

Теперь нужно перенаправить трафик с Nginx (10.8.1.2) на Gitlab (10.8.3.3) через iptables. Выполняем команды на сервере с Nginx (10.8.1.2):

# разрешаем IP-переадресацию
$ sudo echo 1 > /proc/sys/net/ipv4/ip_forward

# устанавливаем правило перенаправления tcp трафика с порта 2222 на 10.8.3.3:2222
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 2222 -j DNAT --to-destination 10.8.3.3:2222

Можно протестировать, работа по SSH через 2222 порт будет работать через сервер с Nginx на сервер с Gitlab.

Заключение

Желаемое достигнуто, хоть и пришлось повозиться, зато как приятно иметь свое хранилище репозиториев под управлением Gitlab где можно сохранить важные для себя репозитории :)

В телеграм канале DevOps от первого лица можно оставить комментарий или почитать интересные истории из практики DevOps