Развернул я себе Gitlab self-managed на своем домашнем сервере в локальной сети, и захотелось мне чтобы он был доступен из интернета.
Свой сервер чтобы хранить нужные мне проекты в управляемом мной хранилище, их никто случайно не удалит, а доступ из интернет чтобы мне самому иметь доступ в любой момент и делиться с другими.
Сразу можно найти инструкцию на сайте Gitlab и дальше идти по ссылкам, но у меня сходу не завелось и проблема была немного глубже, точнее их было несколько:
TLS-терминация
на внешнем сервере работала, но авторизация всегда выдавала 422
SSH-доступа
к репозиториям Gitlab, а надоДля начала определимся с желаемым результатом:
10.8.3.3
) расположен за Nginx reverse proxy (10.8.1.2
), который расположен на внешней виртуалке, Nginx обеспечивает httpsА вот так это выглядит схематично:
У Gitlab есть свой Nginx, его мы почти не будем трогать, во всех остальных случаях речь будет идти про Nginx на внешней виртуалке. В любом случае договоримся что сервер Gitlab имеет IP
10.8.3.3
, а внешний сервер Nginx10.8.1.2
.
Для настройки на Gitlab внешней TLS-терминации
есть документация, а еще здесь пример.
У себя на сервере Gitlab (10.8.3.3
) в конфиге /etc/gitlab/gitlab.rb
:
external_url
trusted_proxies
и real_ip_trusted_addresses
диапазоны адресов внутренних сетей и белый IP виртуалки с Nginx (10.8.1.2
)80
порт без https
proxy_set_headers
должна быть раскоментирована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 на прокси сервере 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
Теперь дата истечения куки будет верная и авторизация должна пройти успешно.
А как же работа с 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 где можно сохранить важные для себя репозитории :)