Управление серверными конфигурациями через Git и символьные ссылки

Практическое руководство по хранению серверных конфигов в Git с использованием символьных ссылок (symlinks). SSH, Nginx, Systemd, iptables — полный цикл от инициализации до разворачивания на новом сервере.

Управление серверными конфигурациями через Git и символьные ссылки

Зачем это нужно

Конфигурационные файлы на сервере разбросаны по разным директориям: /etc/ssh/, /etc/nginx/, /etc/systemd/system/. При ручном управлении легко потерять изменения, случайно удалить файл или забыть, какой именно конфиг был изменён неделю назад.

Подход с символьными ссылками (symlinks) и Git решает эти проблемы:

  • Все конфиги физически хранятся в одной директории-репозитории
  • На своих рабочих местах (где их ждут программы) находятся ссылки на файлы из репозитория
  • История изменений — в Git, бэкап — в удалённом репозитории
Как это работает: вы переносите файлы из /etc/ssh/sshd_config, /etc/nginx/sites-available/mysite и т. д. в ~/server-configs/, а на их местах создаёте символьные ссылки. Git отслеживает все изменения, а удалённый репозиторий служит бэкапом.

Подготовка структуры репозитория

Создайте единую директорию и подпапки для разных сервисов:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Корневая папка для всех конфигов
mkdir -p ~/server-configs

# Подпапки для сервисов — повторяем логику /etc
mkdir -p ~/server-configs/nginx
mkdir -p ~/server-configs/ssh
mkdir -p ~/server-configs/myapp
mkdir -p ~/server-configs/systemd
mkdir -p ~/server-configs/iptables

# Инициализация Git
cd ~/server-configs
git init

.gitignore — защита от случайной утечки

Первым делом создайте .gitignore. Это критически важно, чтобы случайно не закоммитить секреты:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
cat > ~/server-configs/.gitignore << 'EOF'
# Пароли, ключи и сертификаты
*.key
*.pem
*.p12
*.pfx
*.private
*secret*
.env
*.env

# Редакторные файлы
*.swp
*.swo
*~
EOF

Первый коммит:

1
2
3
cd ~/server-configs
git add .gitignore
git commit -m "Инициализация репозитория конфигураций сервера"

Шаблоны секретных конфигов

Никогда не храните пароли и ключи в Git. Вместо этого создавайте файлы-шаблоны с расширением .example:

myapp/database.yml.example yaml
1
2
3
4
5
6
7
8
9
# Шаблон конфигурации базы данных
# Скопируйте в database.yml и заполните реальными данными
database:
  host: localhost
  port: 5432
  name: myapp_production      # <-- укажите имя БД
  user: myapp_user            # <-- укажите пользователя
  password: YOUR_PASSWORD     # <-- укажите пароль
EOF

Сам database.yml добавьте в .gitignore, а шаблон закоммитьте:

1
2
3
cd ~/server-configs
git add myapp/database.yml.example
git commit -m "Добавлен шаблон конфига базы данных"
Важно: при разворачивании на новом сервере вы вручную копируете .example в рабочий файл и заполняете реальные данные. Сам рабочий файл должен быть в .gitignore.
Альтернатива — переменные окружения или Vault: если конфиги содержат пароли, токены или API-ключи, рассмотрите вариант выноса секретов в переменные окружения (через EnvironmentFile= в systemd или env_file в Docker) либо использование HashiCorp Vault, Mozilla sops или pass. В таком случае сам конфиг хранит только ссылку на секрет (например, DB_PASSWORD_FILE=/run/secrets/db_password), а реальные значения подставляются во время запуска сервиса. Git-репозиторий остаётся чистым.

Перенос конфигов и создание символьных ссылок

Общий алгоритм одинаков для любого конфига:

  1. Скопировать существующий конфиг из /etc/... в папку репозитория
  2. Удалить оригинал
  3. Создать символьную ссылку, указывающую на файл в репозитории
  4. Закоммитить файл

Пример 1: SSH-сервер

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Копируем конфиг в репозиторий
sudo cp /etc/ssh/sshd_config ~/server-configs/ssh/sshd_config
sudo chown $USER:$USER ~/server-configs/ssh/sshd_config

# Резервная копия оригинала
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup

# Удаляем оригинал и создаём symlink
sudo rm /etc/ssh/sshd_config
sudo ln -s ~/server-configs/ssh/sshd_config /etc/ssh/sshd_config

# Коммитим
cd ~/server-configs
git add ssh/sshd_config
git commit -m "Добавлен конфиг SSH-сервера"
Всегда используйте абсолютные пути в symlink. Относительный путь ../config может сломаться при изменении рабочей директории.

Проверка:

1
2
3
4
5
6
7
8
9
# Убедимся, что ссылка работает
ls -la /etc/ssh/sshd_config
# Вывод: ... /etc/ssh/sshd_config -> /home/user/server-configs/ssh/sshd_config

# Валидация конфига
sudo sshd -t

# Перезагрузка SSH (осторожно — не потеряйте доступ к серверу!)
sudo systemctl reload sshd

Пример 2: Nginx (конфиг сайта)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Копируем конфиг сайта
sudo cp /etc/nginx/sites-available/mysite ~/server-configs/nginx/mysite
sudo chown $USER:$USER ~/server-configs/nginx/mysite

# Удаляем оригинал и создаём symlink
sudo rm /etc/nginx/sites-available/mysite
sudo ln -s ~/server-configs/nginx/mysite /etc/nginx/sites-available/mysite

# Восстанавливаем ссылку в sites-enabled (она могла порваться)
sudo rm /etc/nginx/sites-enabled/mysite 2>/dev/null
sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite

# Коммитим
cd ~/server-configs
git add nginx/mysite
git commit -m "Добавлен конфиг Nginx сайта mysite"

# Проверка
sudo nginx -t
sudo systemctl reload nginx

Пример 3: Systemd unit-файл сервиса

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Копируем unit-файл
sudo cp /etc/systemd/system/myapp.service ~/server-configs/systemd/myapp.service
sudo chown $USER:$USER ~/server-configs/systemd/myapp.service

# Останавливаем сервис
sudo systemctl stop myapp.service

# Удаляем оригинал и создаём symlink
sudo rm /etc/systemd/system/myapp.service
sudo ln -s ~/server-configs/systemd/myapp.service /etc/systemd/system/myapp.service

# Коммитим
cd ~/server-configs
git add systemd/myapp.service
git commit -m "Добавлен systemd unit для myapp"

# Перезагружаем демоны и запускаем сервис
sudo systemctl daemon-reload
sudo systemctl start myapp.service
sudo systemctl status myapp.service

Права доступа

Git сохраняет только бит исполнения (chmod +x), но не владельца и точные права (600, 644). Для конфигов это критично — нельзя, чтобы sshd_config был доступен всем подряд.

Создайте скрипт восстановления прав прямо в репозитории:

set-permissions.sh bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/bash
# Скрипт для установки правильных владельцев и прав на конфиги

echo "Восстанавливаю права доступа..."

# SSH: владелец root, права 600
chown root:root /etc/ssh/sshd_config
chmod 600 /etc/ssh/sshd_config

# Nginx: владелец root, права 644
chown root:root /etc/nginx/sites-available/*
chmod 644 /etc/nginx/sites-available/*

# MyApp: владелец myapp, права 640
chown myapp:myapp /opt/myapp/config.yml
chmod 640 /opt/myapp/config.yml

echo "Готово."
1
2
3
4
chmod +x ~/server-configs/set-permissions.sh
cd ~/server-configs
git add set-permissions.sh
git commit -m "Добавлен скрипт установки прав доступа"

Запускайте скрипт с sudo после каждого клонирования репозитория на новый сервер.

Подключение удалённого репозитория

Без бэкапа все преимущества теряются. Добавьте удалённый репозиторий:

1
2
3
4
5
6
7
cd ~/server-configs

# Добавляем remote
git remote add origin git@github.com:user/repo-configs.git

# Отправляем
git push -u origin main
Deploy Key vs SSH-ключ: для доступа сервера к GitHub безопаснее использовать Deploy Key — SSH-ключ, привязанный строго к одному репозиторию. Он добавляется в Settings → Deploy keys конкретного репозитория. Даже при компрометации сервера злоумышленник получит доступ только к конфигам этого репозитория.

Разворачивание на новом сервере

Полный цикл настройки нового сервера:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 1. Клонируем
git clone git@github.com:user/repo-configs.git ~/server-configs

# 2. Для каждого конфига — создаём symlink

# SSH
sudo mv /etc/ssh/sshd_config /etc/ssh/sshd_config.original
sudo ln -s ~/server-configs/ssh/sshd_config /etc/ssh/sshd_config
sudo chown root:root /etc/ssh/sshd_config
sudo chmod 600 /etc/ssh/sshd_config
sudo systemctl reload sshd

# Nginx
sudo rm /etc/nginx/sites-available/default
sudo ln -s ~/server-configs/nginx/mysite /etc/nginx/sites-available/mysite
sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite
sudo systemctl reload nginx

# 3. Запускаем скрипт прав
sudo ./set-permissions.sh

Практические нюансы

Права доступа к репозиторию в /root/

Если репозиторий находится в /root/server-configs/, сервисы, запущенные от пользователя без доступа к /root/, не смогут прочитать файлы через symlink.

Признак: ошибка Permission denied при попытке сервиса прочитать конфиг.

Решение: открыть доступ на чтение для остальных:

1
chmod o+rx /root /root/server-configs /root/server-configs/nginx

Или, что правильнее, перенести репозиторий в общедоступное место (/opt/, /srv/, /home/).

Iptables: сохранение правил

При замене файла /etc/iptables/rules.v4 на symlink нужно помнить: правила, добавленные вручную через iptables -A, но не сохранённые в файл, пропадут после netfilter-persistent reload.

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

1
iptables-save > /etc/iptables/rules.v4.backup.$(date +%Y%m%d-%H%M%S)

После замены обновляйте файл в Git:

1
2
3
iptables-save > ~/server-configs/iptables/rules.v4
cd ~/server-configs
git add -A && git commit -m "iptables: update rules" && git push

Автокоммиты в скриптах

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

1
2
3
4
5
6
7
8
git_commit() {
    local MSG="$1"
    if [ -d "$REPO_DIR/.git" ]; then
        cd "$REPO_DIR"
        git add -A
        git commit -m "$MSG" --quiet 2>/dev/null || true
    fi
}
git push всё равно делайте вручную. Автокоммит только фиксирует изменения локально — это защита от случайной отправки незаконченных изменений.

Идемпотентный install.sh

Автоматический скрипт развёртывания symlink’ов должен:

  • Делать backup оригиналов перед заменой
  • Устанавливать права доступа для пользователя сервиса
  • Предлагать перезапуск сервисов
  • Поддерживать идемпотентность (повторный запуск не ломает работающую систему)
install.sh bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/bin/bash
set -e

REPO_DIR="$(cd "$(dirname "$0")" && pwd)"

# Backup и создание symlink для каждого конфига
link_config() {
    local src="$1"
    local dst="$2"
    local service="$3"

    if [ -f "$dst" ] && [ ! -L "$dst" ]; then
        cp "$dst" "${dst}.backup.$(date +%Y%m%d-%H%M%S)"
        echo "  backup: $dst"
    fi

    rm -f "$dst"
    ln -s "$src" "$dst"
    echo "  symlink: $dst -> $src"

    if [ -n "$service" ]; then
        systemctl reload "$service" 2>/dev/null || systemctl restart "$service" 2>/dev/null || true
        echo "  service: $service reloaded"
    fi
}

echo "=== Deploy server configs ==="
link_config "$REPO_DIR/ssh/sshd_config" "/etc/ssh/sshd_config" "ssh"
link_config "$REPO_DIR/nginx/mysite" "/etc/nginx/sites-available/mysite" "nginx"
link_config "$REPO_DIR/systemd/myapp.service" "/etc/systemd/system/myapp.service" ""

echo "=== Permissions ==="
chmod +x "$REPO_DIR/set-permissions.sh"
sudo "$REPO_DIR/set-permissions.sh"

echo "=== Done ==="

Таблица: рекомендуемые права доступа

Файл Владелец Права Пояснение
/etc/ssh/sshd_config root:root 600 Приватный конфиг SSH
/etc/nginx/sites-available/* root:root 644 Публично читаемые
/etc/systemd/system/*.service root:root 644 Стандарт systemd
/etc/iptables/rules.v4 root:root 600 Правила файрвола

Заключение

Подход с Git и символьными ссылками превращает хаотичное управление серверными конфигами в стройную, версионированную и воспроизводимую систему.

Ключевые правила:

  1. Абсолютные пути в symlink — всегда
  2. Секреты — в .gitignore и .example-файлах
  3. Права доступа — через отдельный скрипт
  4. Deploy Key — вместо личного SSH-ключа
  5. Тестируйте конфиг перед перезагрузкой (nginx -t, sshd -t)
  6. Автокоммиты — локально, push — вручную
Схема управления конфигурациями через Git и symlinks
Структура репозитория server-configs: Git-репозиторий в центре, символьные ссылки на системные директории, удалённый бэкап и Deploy Key