Настройка Git сервера с нуля

Любой начинающий DevOps начинает своё знакомство с Git. Этот инструмент стал неотъемлемой частью рабочего процесса разработчиков по всему миру. Во многих курсах и руководствах по DevOps описывается настройка серверов через популярные платформы, такие как GitLab, изредка через Gitea. Однако мне стало интересно рассмотреть другой путь — использование встроенного в Git инструмента под названием GitWeb.

В данной статье я подробно опишу процесс настройки системы управления репозиториями, используя лишь возможности самого Git. Этот подход может показаться необычным, но он позволяет лучше понять внутренние механизмы работы Git и получить новые навыки, которые могут оказаться полезными в вашей карьере DevOps.

В этом руководстве мы рассмотрим, как настроить собственный Git сервер на базе Debian 12. Весь процесс настройки Git сервера поделен на следующие задачи:

  1. настройка SSH;

  2. создание пользователей;

  3. инициализация проекта;

  4. конфигурация Git‑демона;

  5. настройка веб‑интерфейса для просмотра репозиториев;

  6. настройка дополнительных аспектов безопасности.

Настройка SSH на сервере

Первым шагом является установка и настройка SSH сервера, который обеспечит безопасный доступ к нашему серверу.

Установка пакета openssh-server

Для установки пакетаopenssh-server выполните следующие команды:

apt update
apt install openssh-server

Настройка SSH сервера

Для начала надо открыть файл конфигурации SSH:

nano /etc/ssh/sshd_config

Рекомендуется изменить следующие параметры для повышения безопасности:

  • Port: измените стандартный порт на другой, чтобы уменьшить числа автоматических атак;

  • PermitRootLogin: отключите вход по пользователю root, исключительно после настройки отдельного пользователя для работы с сервером;

  • PasswordAuthentification: отключите вход по паролю, исключительно после настройки ключей;

  • PermitEmptyPasswords: отключите пустые пароли;

  • PubkeyAuthentification: включите аутентификацию по ключам. Обычно данный параметр включен по‑умолчанию.

После внесения изменений перезапустите SSH службу:

systemctl restart sshd

Создание пользователя admin

Зачем использовать отдельного пользователя с sudo?

На это есть ряд причин:

  1. Пользователь root минует все ограничения системы, можно случайно исполнить критические команды, которые могут повредить систему;

  2. Учетная запись root будет в первую очередь атакована злоумышленниками;

  3. sudo позволяет отслеживать действия пользователей, все команды могут быть записаны в журнал.

Естественно, что существует гораздо больше причин почему стоит использовать отдельного пользователя, но моя статья не об этом, поэтому ограничимся этими пунктами.

Настройка пользователя admin

Для управления сервером создайте пользователя admin с правами sudo:

adduser admin
apt install sudo
usermod -aG sudo admin
su admin
cd
mkdir .ssh
chmod 700 .ssh
touch .ssh/authorized_keys
chmod 600 .ssh/authorized_keys

После установите в конфигурации SSH PermitRootLogin на отрицательное значение и отключите доступ к пользователю root:

sudo nano /etc/passwd
# Меняем root:x:0:0:root:/root:/bin/bash на root:x:0:0:root:/root:/sbin/nologin

Если попробовать зайти в root, то выведется следующие сообщение: This account is currently not available.

Создание пользователя git

Для работы с Git создадим отдельного пользователя git:

adduser git
su git
cd 
mkdir .ssh
chmod 700 .ssh
touch .ssh/authorized_keys
chmod 600 .ssh/authorized_keys

Ограничение доступа к системе через git-shell

На данный момент пусть пользователь git и не имеет доступа к root правам, он все равно в той или иной мере взаимодействует с системой. Данный пользователь нужен исключительно для работы с репозиториями, однако он все еще имеет доступ к системным командам.

Данное поведение можно исправить используя специальную командную оболочку — git-shell.

git-shell — это ограниченная оболочка, предназначенная для пользователей Git, которые не должны иметь доступ к полноценной командной строке.

Мы можем задействовать данную оболочку следующим образом:

  1. Добавим git-shell к другим оболочкам:

    nano /etc/shells
  2. Внесем следующее содержимое:

    /bin/git-shell
    /usr/bin/git-shell
  3. Cоздадим директорию git-shell-commands:

    mkdir /home/git/git-shell-commands
  4. Теперь изменим командную оболочку по-умолчанию для пользователя git:

    chsh -s $(command -v git-shell) git

Теперь если мы войдем в пользователя git по SSH, то мы ничего не сможем сделать, так как там нету зарегистированных команд. Если надо создать репозиторий, то мы будем использовать пользователя admin, а затем менять права доступа на директорию через chown.

Однако если нам понадобится какие-то команды у пользователя git, то мы можем добавить свои собственные команды в директорию git-shell-commands.

Для чего нужна директория git-shell-commands

Данная директория используется для настройки ограниченной среды, когда Git используется через SSH в режиме git-shell. Если данной директории не будет существовать, то мы не сможем использовать пользователя git в интерактивном режиме.

Как добавить команду в git-shell

Для того, чтобы добавить команду для пользователя git, мы должны создать скрипт внутри директории git-shell-commands. Например, создадим скрипт для вывода существующих репозиториев:

  1. Создадим скрипт:

    touch /home/git/git-shell-commands/list-repos
  2. Впишем следующее содержимое:

    #!/bin/bash
    ls /srv/git/
  3. Сделаем его исполняемым:

    chmod +x /home/git/git-shell-commands/list-repos

Теперь мы можем выполнить команду list-repos и она выведет все репозитории.

Настройка SSH клиента для подключения к серверу

Генерация ключей на клиенте

Создайте необходимые SSH ключи на клиенте:

ssh-keygen -t ed25519 -f ~/.ssh/key_for_git -C "Arthur Lokhov "
ssh-keygen -t ed25519 -f ~/.ssh/key_for_admin -C "Arthur Lokhov "

Далее необходимо настроить клиент SSH для работы с только что созданными ключами. Для этого выполним команду:

nano ~/.ssh/config

И запишем следующее содержимое, также не забудем поменять HostName и Port:

Host server_git
  HostName 
  StrictHostKeyChecking no
  User git
  Port 
  ForwardAgent yes
  IdentityFile /home/username/.ssh/key_for_git
  IdentitiesOnly yes
  UserKnownHostsFile=/dev/null
  AddKeysToAgent yes
  ServerAliveInterval 60
  ServerAliveCountMax 1200

Host server_admin
  HostName 
  StrictHostKeyChecking no
  User admin
  Port 
  ForwardAgent yes
  IdentityFile /home/username/.ssh/key_for_admin
  IdentitiesOnly yes
  UserKnownHostsFile=/dev/null
  AddKeysToAgent yes
  ServerAliveInterval 60
  ServerAliveCountMax 1200

Последним шагом будет добавление публичных ключей на сервер. Ключи добавляются в соответствующие пользователям authorized_keys. Если вход через SSH проходит успешно, то отключите вход по паролю для SSH.

Создание «голого» репозитория

После настройки пользователей и SSH, следующий шаг — это инициализация проекта на сервере. В этом разделе рассмотрим как создать «голый» репозиторий, сделать первый коммит и настроить удаленный репозиторий для работы.

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

Что такое «голый» репозиторий?

«Голый» репозиторий (‑bare) используется на сервере, чтобы хранить центральное хранилище кода. Он не содержит рабочей копии файлов и предназначен исключительно для обмена данными между разработчиками, то есть «голый» репозиторий представляет содержимое директории .git.

Как устроена директория .git?

Внутри директории .git находятся различные файлы и директории, которые Git использует для хранения информации о репозитории, включая историю изменений, настройки, ссылки и метаданные.

Данная директория состоит из следующих компонентов:

  1. HEAD: Этот файл содержит ссылку на текущий коммит или ветку, с которой вы работаете;

  2. branches/: данная директория используется для устаревшей механики управления ветками, которая фактически уже не используется в современных версиях Git. Директория обычная пуста и не играет никакой роли в управления ветками;

  3. config: файл конфигурации репозитория, содержащий настройки, такие как информация о пользователе, удаленные репозитории, алиасы команд и другие параметры, специфичные для этого репозитория;

  4. description: файл, который обычно используется в bare репозиториях для описания их назначения. В рабочих репозиториях он не имеет особого значения;

  5. hooks/: директория для хранения скриптов, которые запускаются при определенных действиях Git, таких как коммиты, слияния, получение изменений и другие. Эти скрипты могут быть использованы для автоматизации задач и проверки;

  6. info/: Содержит дополнительные файлы конфигурации, например, файл exclude, который используется для указания шаблонов, игнорируемых только в этом репозитории (локальный аналог .gitignore);

  7. objects/: директория, в которой хранятся все объекты данных, такие как коммиты, деревья, блобы (бинарные файлы) и теги. Эти объекты идентифицируются по их SHA-1 хэшам и являются основой для хранения истории и содержимого репозитория;

  8. refs/: содержит ссылки на коммиты для веток (heads/), тегов (tags/) и удаленных веток (remotes/). Эти ссылки помогают Git отслеживать, где находятся ветки и теги;

  9. logs/: содержит историю изменений ссылок (refs). Например, файл logs/HEAD хранит историю изменений, указывающих на коммиты, на которые ссылался HEAD, что позволяет отслеживать изменения веток.

Взаимодействие Git с директорией .git

Мы разобрали из чего данная директория состоит. Но не разобрали как Git взаимодействует с этими данными. Есть несколько взаимодействий Git с файлами в этой директории:

  1. Коммит (commit): когда вы делаете коммит, Git берет файлы из индекса (index), создает новые объекты в objects/, и обновляет ссылки в refs/heads/ для текущей ветки;

  2. Чекаут (checkout): когда вы переключаетесь между ветками, Git читает ссылки из refs/heads/ и обновляет рабочую директорию до состояния, соответствующего коммиту, на который указывает ссылка;

  3. Пуш (push) и пул (pull): при обмене данными с удаленными репозиториями Git обновляет ссылки в refs/remotes/ и перемещает объекты в objects/;

  4. Управления конфигурацией (config): Git читает файл config для применения настроек, таких как параметры пользователя и удаленные репозитории.

Инициализация проекта

ВАЖНО: Все команды на сервере с этого момента выполняются через пользователя admin

  1. Установим Git, если он еще не установлен:

sudo apt install git
  1. Создадим директорию для хранения проектов и настроим права доступа:

sudo mkdir -p /srv/git
sudo chown git:git /srv/git/
  1. Создадим «голый» репозиторий:

cd /srv/git
sudo mkdir my_project.git
cd my_project.git
sudo git init --bare
cd ..
sudo chown git:git /srv/git/my_project.git/

Настройка локального репозитория и первый коммит

Теперь настроим локальный репозиторий, сделаем первый коммит и отправим его на сервер. Для этого выполним данные команды:

mkdir my_project
cd my_project
git init
echo "# My Project" > README.md
git add README.md
git commit -m "Initial commit"
git remote add origin git@hostname:/srv/git/my_project.git
git push -u origin main

Теперь в файле /srv/git/my_project/refs/heads/main будет хранится указатель HEAD(указатель на последний коммит, сделанный в текущей ветке) на текущий коммит. Он должен быть таким же как и указатель вашего коммита в локальном репозитории. Это будет доказательством того, что процесс прошел успешно.

Настройка демона Git

Git-демон позволяет предоставлять доступ к репозиториям через протокол git://, что обеспечивает быструю и легкую настройку для анонимного чтения.

Про команду git daemon

Данная команда, используется для управления Git-демоном, что можно понять из названия. С помощью этой команды мы и будет взаимодействовать с нашим демоном. С этой командой мы будем использовать следующие флаги:

  1. reuseaddr означает, что сервер будет перезапускаться без ожидания тайм-аута существующих подключений;

  2. export-all означает, что будут экспортированы все проекты из репозитория, иначе для каждого проекта надо создавать файл git-daemon-export-ok;

  3. base-path означает, что данный путь будет автоматически подставляться. То есть вместо git://hostname/srv/git/my_project.git можно будет использовать git://hostname/my_project.git.

Создание службы для systemd.

Так как systemd является самой распространенной системой инициализации, то и примеры я буду приводить для нее. Сам же я использую Devuan с OpenRC, но смысла приводить настройки для другой системы инициализации я не вижу.

Теперь же перейдем к самому процессу.

  1. Создадим файл службы для Git-демона:

sudo nano /etc/systemd/system/git-daemon.service
  1. Пропишем следующее содержимое:

[Unit]
Description=Git daemon

[Service]
ExecStart=/usr/bin/git daemon --reuseaddr --export-all --base-path=/srv/git/ /srv/git/
Restart=always
RestartSec=500ms

StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=git-daemon

User=git
Group=git

[Install]
WantedBy=multi-user.target
  1. Запустим и активируем службу.

sudo systemctl enable git-daemon
sudo systemctl start git-daemon
sudo systemctl status git-daemon

Настройка GitWeb

GitWeb предоставляет веб-интерфейс для просмотра репозиториев Git. Мы рассмотрим два способы настройки:

Использование git instaweb

git instaweb позволяет быстро развернуть веб-интерфейс отдельно для каждого репозитория.

  1. Установим lighttpd, если он еще не установлен:

sudo apt install lighttpd
  1. Перейдем в директорию проекта и запустим git instaweb:

cd /srv/git/my_project.git
git instaweb
  1. Перейдем в браузер и введем адрес http://hostname:1234, чтобы увидеть интерфейс GitWeb.

gitweb with libhttpd

GitWeb, запущенный через команду git instaweb

  1. Завершим работу сервера:

git instaweb --stop

Настройка GitWeb через Nginx

  1. Установим необходимые пакеты:

sudo apt install nginx gitweb fcgiwrap
  1. Настроим Nginx для работы с GitWeb. Создадим конфигурационный файл для работы сайта:

sudo nano /etc/nginx/sites-available/gitweb
  1. Пропишем следующее содержимое:

server {
    listen 80;
    server_name example.com;

    location /index.cgi {
        root /usr/share/gitweb/;
        include fastcgi_params;
        gzip off;
        fastcgi_param SCRIPT_NAME $uri;
        fastcgi_param GITWEB_CONFIG /etc/gitweb.conf;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
    }

    location / {
        root /usr/share/gitweb/;
        index index.cgi;
    }
}
  1. Активируем конфигурацию:

sudo ln -s /etc/nginx/sites-available/gitweb /etc/nginx/sites-enabled/gitweb
sudo rm /etc/nginx/sites-enabled/default
sudo systemctl restart nginx
  1. Перейдем в браузер и введем адрес нашего сервера, чтобы увидеть интерфейс GitWeb:

nginx with gitweb

GitWeb, запущенный через Nginx

  1. Обновим конфигурацию GitWeb для правильного отображения наших репозиториев:

sudo nano /etc/gitweb.conf
  1. Изменим путь к репозиториям:

$projectroot = "/srv/git";

Теперь наш Git сервер настроен, и у нас есть доступ к репозиториям через SSH, Git-демон и веб-интерфейс GitWeb.

Эта конфигурация обеспечивает как безопасность, так и удобство использования, а также поддержку анонимного и аутентифицированного доступа к репозиториям.

nginx gitweb final

GitWeb, после изменения конфигурации

Дополнительные аспекты безопасности и оптимизаци

Защита от brute-force атак

Для дополнительной защиты нашего сервера можно установить и настроить такие инструменты, как fail2ban, которые будут блокировать IP (Internet Protocol)-адреса, совершившие слишком много неудачных попыток входа.

  1. Установим пакет fail2ban:

sudo apt install fail2ban
  1. Настроим fail2ban для защиты SSH:

sudo nano /etc/fail2ban/jail.local
  1. Добавим следующие строки:

[sshd]
enabled = true
port = ssh
logpath = /var/log/auth.log
maxretry = 5
  1. Создадим файл, необходимый для логов, если он не создан, и перезапустим fail2ban:

sudo touch /var/log/auth.log
sudo systemctl restart fail2ban
sudo systemctl status fail2ban

Настройка брандмауэра

Для ограничения доступа к нашему серверу настроим брандмауэр, например, ufw:

sudo apt install ufw
sudo ufw allow 443/tcp
sudo ufw allow 80/tcp
sudo ufw allow 20/tcp
# Также не забудьте открыть порт, через который у вас работает SSH

Командами выше мы установили ufw и разрешили запросы к портам 443, 20 и 80.

Теперь включим ufw:

sudo ufw enable

Вы можете попробовать закрыть 80-ый порт и открыть GitWeb в браузере, он не откроется.

Автоматическое обновление пакетов

Для обеспечения безопасности системы рекомендуется настроить автоматическое обновление пакетов.

Установим пакет unattended-upgrades и настроим его:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades

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

Создание резервных копий

Для предотвращения потери данных важно настроить регулярное создание резервных копий. Мы можем использовать rsync или специализированные инструменты для создания резервных копий репозиториев и конфигураций сервера.

  1. Установим rsync:

sudo apt install rsync
  1. Настроим cron для автоматизации резервного копирования:

crontab -e
  1. Добавим следующее задание, чтобы оно выполнялось ежедневно в 2 часа ночи:

0 2 * * * rsync -avz /srv/git/ /srv/git-backup/

Синтаксис crontab

Параметры идут в следующем порядке:

  1. Минута

  2. Час

  3. День месяца

  4. Месяц

  5. День недели

  6. Команда

Итог

В этом руководстве мы рассмотрели, как настроить собственный Git сервер на базе Debian 12, обеспечив при этом высокий уровень безопасности и функциональности. Мы прошли через установку и настройку SSH, создание пользователей, инициализацию проектов, настройку веб-интерфейса и рассмотрели дополнительные аспекты безопасности, такие как защита от brute-force атак, настройка брандмаэура и автоматическое обновления пакетов. Также мы настроили резервное копирование данных.

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

Что планируется улучшить?

На данный момент у меня в планах изменить или добавить следующие вещи:

  1. Резервной копирование не в другую директорию, а на другой сервер;

  2. Мониторинг через Prometheus и Grafana;

  3. Настроенный CI/CD пайплайн для работы с нашими репозиториями.

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

P.S

Всем привет, меня зовут Артур Лохов и это моя первая статья на хабре. На выбор такой темы меня сподвигло то, что во многих курсах рассматривается настройка работы с репозиториями исключительно через GitLab, за редкими исключениями в виде Gitea. Я считаю, что любой DevOps должен знать как Git работает изнутри и как его настраивать с нуля, чтобы понимать что скрывается под тем же Gitlab и как он работает.

Одно дело уметь работь с инструментов, а другое дело уметь работать с инструментов и знать как он работает изнутри.

Если вы нашли какую-либо ошибку, пожалуйста дайте мне знать об этом, чтобы я мог ее исправить, ведь я мог забыть указать, что-либо важное. Также приветствуются советы по-тому, как можно улучшить сервер с Git. Идеи для улучшение, приведенные выше, скорее всего будут мною реализованы, когда я перейду к данным тематикам (CI/CD и мониторинг), и будут оформлены в виде цикла статей, которые будут являться логичиским продолжением.

Habrahabr.ru прочитано 1553 раза