From 48cec1c1b0af357da9dc241d492dabb22f98627d Mon Sep 17 00:00:00 2001 From: sasa Date: Sun, 11 Oct 2026 22:44:49 +0300 Subject: [PATCH] Add initial files --- .gitignore | 1 + MVP.md | 481 ++++++++++++++++++++++++++++++++++++++++++++++++++++ add-user.sh | 200 ++++++++++++++++++++++ 3 files changed, 682 insertions(+) create mode 100644 .gitignore create mode 100644 MVP.md create mode 100755 add-user.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85e7c1d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea/ diff --git a/MVP.md b/MVP.md new file mode 100644 index 0000000..fb21928 --- /dev/null +++ b/MVP.md @@ -0,0 +1,481 @@ +# WireGuard Manager: план MVP + +## 1. Назначение проекта + +WireGuard Manager — локальный Bash-скрипт для администрирования WireGuard на том же Linux-сервере, где запущен WireGuard. + +Основные задачи проекта: + +- просмотр состояния WireGuard; +- просмотр списка клиентов; +- добавление клиентов; +- удаление клиентов; +- получение клиентского конфига и QR-кода; +- диагностика конфигурации, сервиса и сети; +- ведение локального журнала административных операций; +- создание резервных копий перед изменением конфигурации. + +Существующий `add-user.sh` используется как исходный пример, но его логика будет переработана в безопасные переиспользуемые функции. + +## 2. Модель распространения и запуска + +Проект не устанавливается в систему и не добавляется в `PATH`. Скрипт и сопутствующие файлы находятся в переносимой директории, например: + +```text +/root/wireguard-manager/ +├── wg-manager.sh +├── wg-manager.conf +├── add-user.sh +├── bin/ +│ └── gum +├── backups/ +├── logs/ +└── tests/ +``` + +Основная точка входа: + +```bash +sudo /root/wireguard-manager/wg-manager.sh +``` + +CLI-команды вызываются через тот же файл: + +```bash +sudo /root/wireguard-manager/wg-manager.sh status +sudo /root/wireguard-manager/wg-manager.sh list +sudo /root/wireguard-manager/wg-manager.sh add alice +sudo /root/wireguard-manager/wg-manager.sh diagnose +``` + +Скрипт определяет свою директорию через `BASH_SOURCE`, поэтому всю директорию проекта можно перемещать. Скрипты `install.sh` и `uninstall.sh` не нужны. + +Установку или размещение зависимостей выполняет администратор сервера. + +## 3. TUI + +Для терминального интерфейса используется `gum`. Встроенный TUI на чистом Bash, а также интерфейсы на `dialog` и `whiptail` в MVP не планируются. + +Поиск бинарника `gum` выполняется в следующем порядке: + +1. Путь из переменной окружения `WG_MANAGER_GUM`. +2. `${SCRIPT_DIR}/bin/gum` рядом со скриптом. +3. Команда `gum` из `PATH`. + +Если `gum` отсутствует: + +- CLI-команды продолжают работать; +- при попытке открыть TUI выводится понятное сообщение об ошибке; +- скрипт не устанавливает и не скачивает `gum` автоматически. + +### Главное меню + +```text +WireGuard Manager + +Интерфейс: wg0 +Состояние: активен +Клиентов: 12 Активны недавно: 4 + +1. Состояние WireGuard +2. Список клиентов +3. Добавить клиента +4. Информация о клиенте +5. Показать QR-код / конфиг +6. Удалить клиента +7. Диагностика +8. Журнал операций +9. Резервные копии +0. Выход +``` + +TUI должен использовать возможности `gum` для меню, фильтрации списка клиентов, ввода данных, таблиц, подтверждений, просмотра длинного текста и индикации выполнения операций. + +## 4. Архитектурный принцип + +TUI не содержит бизнес-логику. Он только собирает ввод пользователя и вызывает те же функции, которые доступны через CLI. + +Логические слои внутри `wg-manager.sh`: + +1. Загрузка и проверка конфигурации. +2. Проверка прав и зависимостей. +3. Чтение состояния WireGuard. +4. Управление клиентами. +5. Безопасное изменение серверного конфига. +6. Резервное копирование и восстановление. +7. Диагностика. +8. Audit log. +9. CLI-маршрутизация. +10. TUI на `gum`. + +Для MVP эти функции могут находиться в одном `wg-manager.sh`. Если файл станет слишком большим, их можно будет позднее вынести в `lib/*.sh`, сохранив прежнюю точку входа. + +## 5. Конфигурация менеджера + +Из `add-user.sh` необходимо убрать жёстко заданные параметры и перенести их в `wg-manager.conf`. + +Пример состава конфигурации: + +```bash +WG_INTERFACE="wg0" +WG_CONF="/etc/wireguard/wg0.conf" +CLIENT_DIR="/etc/wireguard/clients" + +SERVER_ENDPOINT="vpn.example.com" +SERVER_PORT="51820" +SERVER_VPN_IP="10.9.0.1" +CLIENT_DNS="10.9.0.1,192.168.0.200" + +VPN_SUBNET_PREFIX="10.9.0" +START_HOST="2" +END_HOST="254" + +USE_IPV6="0" +IPV6_PREFIX="fd05:13:53::" + +DEFAULT_CLIENT_MODE="full-tunnel" +RECENT_HANDSHAKE_SECONDS="180" +``` + +Конфигурация должна проверяться до выполнения любой операции. Неизвестные или небезопасные значения не должны напрямую подставляться в команды. + +Рекомендуемые права: + +```bash +chmod 700 /root/wireguard-manager +chmod 700 /root/wireguard-manager/wg-manager.sh +chmod 600 /root/wireguard-manager/wg-manager.conf +``` + +## 6. Функции MVP + +### 6.1. Состояние WireGuard + +Экран состояния показывает: + +- имя интерфейса; +- состояние интерфейса и `wg-quick`-сервиса; +- публичный ключ сервера; +- UDP-порт; +- количество peers; +- время работы или доступную информацию о запуске сервиса; +- наличие предупреждений диагностики. + +### 6.2. Список клиентов + +Для каждого клиента показываются: + +- имя; +- VPN-адрес; +- сокращённый публичный ключ; +- endpoint, если он известен; +- время последнего handshake; +- принятый и переданный трафик; +- вычисляемое состояние; +- признак, управляется ли клиент этим скриптом. + +Предлагаемые состояния: + +- `active` — handshake был недавно; +- `inactive` — клиент ранее подключался, но давно неактивен; +- `never` — handshake ещё не было; +- `unmanaged` — peer найден в WireGuard, но его имя и метаданные неизвестны менеджеру. + +Источником текущего состояния является `wg show ... dump`. Серверный конфиг используется для постоянных параметров и сопоставления peers. + +### 6.3. Добавление клиента + +Операция добавления: + +1. Проверяет имя клиента. +2. Проверяет отсутствие клиента, ключей и файлов с таким именем. +3. Находит свободный IP в заданном диапазоне. +4. Проверяет конфликты во всех `AllowedIPs`. +5. Создаёт private key, public key и preshared key. +6. Формирует серверный peer-блок. +7. Формирует клиентский конфиг. +8. Проверяет будущую серверную конфигурацию. +9. Создаёт резервную копию. +10. Атомарно сохраняет и применяет конфигурацию. +11. Сохраняет метаданные клиента. +12. Записывает административное действие в журнал. + +Имя клиента допускает только безопасный ограниченный набор символов: латинские буквы, цифры, точку, подчёркивание и дефис. + +MVP поддерживает два режима маршрутизации клиента: + +- `full-tunnel` — весь IPv4-трафик идёт через VPN; +- `split-tunnel` — через VPN идут только указанные сети. + +### 6.4. Просмотр и экспорт клиента + +Для выбранного клиента доступны: + +- сведения о peer; +- путь к клиентскому конфигу; +- просмотр конфига после отдельного подтверждения; +- QR-код через `qrencode`, если команда доступна; +- понятное сообщение и путь к конфигу, если `qrencode` отсутствует. + +Приватные ключи и содержимое клиентских конфигов не записываются в audit log. + +### 6.5. Удаление клиента + +Удаление требует выбора клиента и явного подтверждения. Перед изменением создаётся резервная копия. + +Операция: + +1. Находит peer по сохранённому публичному ключу, а не только по имени. +2. Удаляет peer из будущей серверной конфигурации. +3. Проверяет новый конфиг. +4. Применяет изменение. +5. Удаляет или архивирует клиентские файлы согласно выбранной политике. +6. Записывает действие в audit log без секретов. + +Для MVP безопаснее по умолчанию перемещать клиентские файлы в локальный архив, а не уничтожать безвозвратно. + +### 6.6. Диагностика + +Диагностика проверяет: + +- запуск с необходимыми правами; +- наличие `wg`, `wg-quick`, `ip` и других обязательных команд; +- наличие серверного конфига; +- права на конфиги и ключи; +- наличие и состояние интерфейса; +- состояние `wg-quick@` при использовании systemd; +- возможность прочитать активную конфигурацию; +- корректность адресов и `AllowedIPs`; +- дублирующиеся адреса и пересекающиеся сети; +- включён ли IPv4 forwarding; +- IPv6 forwarding, если IPv6 включён в настройках; +- маршруты сервера; +- наличие ожидаемых NAT/firewall-правил; +- прослушивание настроенного UDP-порта; +- расхождение между конфигом на диске и активным состоянием; +- последние релевантные сообщения systemd journal. + +Результат каждой проверки имеет статус `OK`, `WARN`, `FAIL` или `SKIP`. Команда диагностики возвращает ненулевой exit code при критических ошибках. + +WireGuard не предоставляет полноценную историю клиентских подключений. Менеджер показывает текущее состояние, последние handshake и доступные системные журналы, но не заявляет их как полную историю активности пользователей. + +### 6.7. Журнал операций + +Локальный audit log содержит: + +- дату и время; +- выполненную операцию; +- имя клиента, если применимо; +- результат; +- краткое описание ошибки; +- пользователя или UID, запустившего операцию. + +В журнал запрещено записывать: + +- private keys; +- preshared keys; +- полные клиентские конфиги; +- команды, содержащие секретные значения. + +### 6.8. Резервные копии + +Перед каждым изменением создаётся резервная копия серверного конфига и относящихся к операции метаданных. + +MVP предоставляет: + +- создание резервной копии; +- список резервных копий; +- восстановление выбранной копии с подтверждением; +- автоматический rollback при ошибке применения нового конфига; +- ограничение количества автоматически сохраняемых копий. + +## 7. Безопасное изменение конфигурации + +Изменение `wg0.conf` должно быть транзакционным: + +1. Получить эксклюзивную блокировку через `flock`. +2. Повторно прочитать актуальное состояние после получения блокировки. +3. Проверить входные данные. +4. Создать backup. +5. Сформировать новый конфиг во временном файле с `umask 077`. +6. Проверить синтаксис и возможность преобразования конфига средствами WireGuard. +7. Установить правильные права и владельца. +8. Атомарно заменить рабочий файл. +9. Применить конфигурацию через `wg syncconf` и `wg-quick strip`. +10. Проверить итоговое активное состояние. +11. При ошибке восстановить предыдущий файл и активную конфигурацию. +12. Освободить блокировку и записать результат в журнал. + +Нельзя сначала дописывать peer непосредственно в рабочий конфиг, как это сейчас делает `add-user.sh`, а затем надеяться, что применение завершится успешно. + +## 8. CLI + +Минимальный набор команд: + +```text +wg-manager.sh Открыть TUI +wg-manager.sh status Состояние WireGuard +wg-manager.sh list Список клиентов +wg-manager.sh add NAME Добавить клиента +wg-manager.sh show NAME Информация о клиенте +wg-manager.sh config NAME Показать путь к клиентскому конфигу +wg-manager.sh qr NAME Показать QR-код +wg-manager.sh remove NAME Удалить клиента +wg-manager.sh diagnose Выполнить диагностику +wg-manager.sh backup Создать резервную копию +wg-manager.sh backups Показать резервные копии +wg-manager.sh restore BACKUP Восстановить резервную копию +wg-manager.sh log Показать audit log +wg-manager.sh help Показать справку +``` + +CLI должен иметь стабильные exit codes и пригодный для чтения текстовый вывод. Структурированный JSON-вывод не входит в обязательный объём MVP. + +## 9. Метаданные клиентов + +Комментарии вида `### Client NAME` можно поддерживать для читаемости и совместимости с существующим `add-user.sh`, но они не должны быть единственным источником данных. + +Для управляемого клиента необходимо хранить как минимум: + +- имя; +- публичный ключ; +- VPN-адрес; +- дату создания; +- путь к клиентскому конфигу; +- режим маршрутизации; +- статус управления. + +Формат метаданных должен быть простым и обрабатываться стандартными средствами Bash без обязательной зависимости от `jq`. Серверный конфиг и фактическое состояние `wg` остаются источниками истины для сетевых параметров. + +## 10. Зависимости + +Обязательные: + +- Bash; +- WireGuard tools: `wg`, `wg-quick`; +- стандартные Linux-команды, используемые скриптом; +- `flock` для защиты от параллельных изменений. + +Для TUI: + +- `gum`, предоставляемый или устанавливаемый администратором. + +Необязательные: + +- `qrencode` для QR-кодов; +- `systemctl` и `journalctl` для серверов с systemd; +- инструменты конкретного firewall: `nft` или `iptables`. + +При отсутствии необязательной команды соответствующая функция должна показывать `SKIP` или понятное сообщение, а не завершать весь менеджер аварийно. + +## 11. Тестирование + +Большая часть тестов не должна требовать реального интерфейса WireGuard или изменения `/etc/wireguard`. + +Необходимые сценарии: + +- разбор серверного конфига; +- определение следующего свободного IP; +- обнаружение конфликтующих `AllowedIPs`; +- проверка имён клиентов; +- построение peer-блока; +- удаление только выбранного peer-блока; +- обработка unmanaged peers; +- успешное применение транзакции; +- rollback при ошибке применения; +- защита от параллельного запуска; +- отсутствие секретов в логах; +- работа CLI без `gum`; +- корректная ошибка TUI без `gum`; +- поиск локального и системного бинарника `gum`. + +Для тестов используются fixtures конфигураций и mock-команды `wg`, `wg-quick`, `systemctl`, `ip`, `nft` и `iptables`, подставляемые через контролируемый `PATH`. + +Дополнительные проверки качества: + +- `shellcheck`; +- `bash -n`; +- единый формат shell-кода; +- тестирование на поддерживаемых версиях Bash. + +## 12. Этапы реализации + +### Этап 1. Каркас + +- создать `wg-manager.sh`; +- реализовать загрузку конфигурации; +- определить директорию проекта; +- добавить обработку ошибок и временных файлов; +- добавить проверку root для изменяющих операций; +- реализовать CLI `help`, `status` и `list`. + +### Этап 2. Модель клиентов + +- разобрать существующий `wg0.conf`; +- связать имена, публичные ключи и адреса; +- отображать managed и unmanaged peers; +- добавить метаданные клиентов. + +### Этап 3. Безопасные изменения + +- перенести и переработать логику `add-user.sh`; +- добавить блокировку, backup, валидацию и rollback; +- реализовать `add` и `remove`; +- реализовать архивирование клиентских файлов. + +### Этап 4. Диагностика и журнал + +- добавить диагностические проверки; +- добавить статусы `OK/WARN/FAIL/SKIP`; +- добавить audit log без секретов; +- добавить просмотр резервных копий и восстановление. + +### Этап 5. TUI на `gum` + +- реализовать главное меню; +- таблицу и фильтрацию клиентов; +- формы добавления; +- карточку клиента; +- подтверждение удаления и восстановления; +- экраны диагностики, логов и резервных копий. + +### Этап 6. Проверка MVP + +- добавить fixtures и mocks; +- протестировать успешные и аварийные сценарии; +- проверить поведение на реальном тестовом интерфейсе WireGuard; +- подготовить README с примерами запуска и конфигурации. + +## 13. Критерии готовности MVP + +MVP считается готовым, если администратор может: + +1. Скопировать или переместить директорию проекта на Linux-сервер. +2. Настроить `wg-manager.conf` без редактирования основной логики. +3. Запустить TUI через `wg-manager.sh` с предоставленным `gum`. +4. Использовать основные функции через CLI без TUI. +5. Просмотреть WireGuard и список известных клиентов. +6. Безопасно добавить клиента и получить его конфиг или QR-код. +7. Безопасно удалить клиента. +8. Выполнить диагностику и увидеть конкретные предупреждения и ошибки. +9. Просмотреть журнал административных операций без утечки ключей. +10. Восстановить конфигурацию из backup после неудачного изменения. +11. Не повредить рабочий конфиг при ошибке команды или параллельном запуске. + +## 14. За пределами MVP + +После завершения MVP можно добавить: + +- временное отключение и повторное включение клиента без удаления; +- срок действия клиента; +- ротацию ключей; +- импорт существующих unmanaged peers; +- поддержку нескольких WireGuard-интерфейсов; +- шаблоны маршрутов и DNS для разных типов клиентов; +- экспорт диагностического отчёта; +- уведомления о проблемах; +- JSON-вывод для интеграций; +- локализацию интерфейса; +- дополнительные варианты поставки `gum` для разных архитектур. + +Эти функции не должны задерживать выпуск первого работоспособного MVP. diff --git a/add-user.sh b/add-user.sh new file mode 100755 index 0000000..c8d016c --- /dev/null +++ b/add-user.sh @@ -0,0 +1,200 @@ +#!/usr/bin/env bash +set -euo pipefail + +WG_INTERFACE="wg0" +WG_CONF="/etc/wireguard/${WG_INTERFACE}.conf" +WG_DIR="/etc/wireguard" +CLIENT_DIR="${WG_DIR}/clients" +SERVER_PORT="51820" +DEFAULT_ENDPOINT="37.26.136.232" +SERVER_VPN_IP="10.9.0.1" +VPN_SUBNET_PREFIX="10.9.0" +START_HOST="2" +END_HOST="254" +USE_IPV6="0" +IPV6_PREFIX="fd05:13:53::" + +umask 077 + +die() { + echo "Ошибка: $*" >&2 + exit 1 +} + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Не найдена команда: $1" +} + +usage() { + cat < "$CLIENT_PUBKEY_FILE" +wg genpsk > "$CLIENT_PSK_FILE" + +chmod 600 "$CLIENT_PRIVKEY_FILE" "$CLIENT_PUBKEY_FILE" "$CLIENT_PSK_FILE" + +CLIENT_PRIVATE_KEY="$(cat "$CLIENT_PRIVKEY_FILE")" +CLIENT_PUBLIC_KEY="$(cat "$CLIENT_PUBKEY_FILE")" +CLIENT_PRESHARED_KEY="$(cat "$CLIENT_PSK_FILE")" + +if command -v wg >/dev/null 2>&1 && wg show "$WG_INTERFACE" >/dev/null 2>&1; then + SERVER_PUBLIC_KEY="$(wg show "$WG_INTERFACE" public-key)" +else + SERVER_PUBLIC_KEY="$(wg pubkey < <(grep '^PrivateKey *= *' "$WG_CONF" | head -n1 | awk -F'=' '{gsub(/ /,"",$2); print $2}'))" +fi + +TMP_PEER_FILE="$(mktemp)" +trap 'rm -f "$TMP_PEER_FILE"' EXIT + +{ + echo + echo "### Client ${CLIENT_NAME}" + echo "[Peer]" + echo "PublicKey = ${CLIENT_PUBLIC_KEY}" + echo "PresharedKey = ${CLIENT_PRESHARED_KEY}" + if [[ "$USE_IPV6" == "1" ]]; then + echo "AllowedIPs = ${CLIENT_IPV4}/32,${CLIENT_IPV6}/128" + else + echo "AllowedIPs = ${CLIENT_IPV4}/32" + fi +} > "$TMP_PEER_FILE" + +cat "$TMP_PEER_FILE" >> "$WG_CONF" + +if [[ "$USE_IPV6" == "1" ]]; then + CLIENT_ADDRESS="${CLIENT_IPV4}/32,${CLIENT_IPV6}/128" + CLIENT_ALLOWED_IPS="0.0.0.0/0,::/0" +else + CLIENT_ADDRESS="${CLIENT_IPV4}/32" + CLIENT_ALLOWED_IPS="0.0.0.0/0" +fi + +cat > "$CLIENT_CONF" </dev/null 2>&1; then + echo "QR-код для клиента:" + qrencode -t ansiutf8 < "$CLIENT_CONF" + echo +else + echo "Пакет qrencode не установлен, QR-код не напечатан." + echo "Установить можно так:" + echo " sudo apt update && sudo apt install -y qrencode" + echo +fi + +echo "Проверка:" +echo " sudo wg show ${WG_INTERFACE}" +echo +echo "Передать клиенту файл:" +echo " ${CLIENT_CONF}"