Add initial files
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
/.idea/
|
||||||
@@ -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@<interface>` при использовании 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.
|
||||||
Executable
+200
@@ -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 <<EOF
|
||||||
|
Использование:
|
||||||
|
sudo $0 CLIENT_NAME [ENDPOINT]
|
||||||
|
|
||||||
|
Примеры:
|
||||||
|
sudo $0 android-phone
|
||||||
|
sudo $0 work-laptop vpn.example.com
|
||||||
|
sudo $0 user5 37.26.136.232
|
||||||
|
|
||||||
|
Что делает:
|
||||||
|
- создаёт ключи клиента
|
||||||
|
- подбирает следующий свободный IP
|
||||||
|
- добавляет peer в ${WG_CONF}
|
||||||
|
- создаёт клиентский конфиг в ${CLIENT_DIR}
|
||||||
|
- применяет конфиг WireGuard
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ $# -ge 1 ]] || { usage; exit 1; }
|
||||||
|
|
||||||
|
CLIENT_NAME="$1"
|
||||||
|
ENDPOINT="${2:-$DEFAULT_ENDPOINT}"
|
||||||
|
|
||||||
|
[[ "$EUID" -eq 0 ]] || die "Скрипт нужно запускать через sudo/root"
|
||||||
|
[[ -f "$WG_CONF" ]] || die "Не найден конфиг ${WG_CONF}"
|
||||||
|
|
||||||
|
need_cmd wg
|
||||||
|
need_cmd awk
|
||||||
|
need_cmd grep
|
||||||
|
need_cmd sed
|
||||||
|
need_cmd sort
|
||||||
|
need_cmd tee
|
||||||
|
need_cmd mkdir
|
||||||
|
need_cmd chmod
|
||||||
|
|
||||||
|
mkdir -p "$CLIENT_DIR"
|
||||||
|
|
||||||
|
if [[ ! "$CLIENT_NAME" =~ ^[a-zA-Z0-9._-]+$ ]]; then
|
||||||
|
die "Имя клиента может содержать только буквы, цифры, точку, подчёркивание и дефис"
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLIENT_CONF="${CLIENT_DIR}/${WG_INTERFACE}-client-${CLIENT_NAME}.conf"
|
||||||
|
CLIENT_PRIVKEY_FILE="${CLIENT_DIR}/${CLIENT_NAME}.key"
|
||||||
|
CLIENT_PUBKEY_FILE="${CLIENT_DIR}/${CLIENT_NAME}.pub"
|
||||||
|
CLIENT_PSK_FILE="${CLIENT_DIR}/${CLIENT_NAME}.psk"
|
||||||
|
|
||||||
|
if grep -qE "^### Client ${CLIENT_NAME}$" "$WG_CONF"; then
|
||||||
|
die "Клиент с именем '${CLIENT_NAME}' уже есть в ${WG_CONF}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -e "$CLIENT_CONF" || -e "$CLIENT_PRIVKEY_FILE" || -e "$CLIENT_PUBKEY_FILE" || -e "$CLIENT_PSK_FILE" ]]; then
|
||||||
|
die "Файлы клиента '${CLIENT_NAME}' уже существуют в ${CLIENT_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
find_next_ipv4() {
|
||||||
|
local used host
|
||||||
|
used="$(
|
||||||
|
grep -oE "${VPN_SUBNET_PREFIX//./\\.}\.[0-9]+/32" "$WG_CONF" \
|
||||||
|
| sed 's#/32##' \
|
||||||
|
| awk -F. '{print $4}' \
|
||||||
|
| sort -n \
|
||||||
|
| uniq
|
||||||
|
)"
|
||||||
|
|
||||||
|
for host in $(seq "$START_HOST" "$END_HOST"); do
|
||||||
|
if ! grep -qx "$host" <<<"$used"; then
|
||||||
|
echo "${VPN_SUBNET_PREFIX}.${host}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CLIENT_IPV4="$(find_next_ipv4)" || die "Не удалось найти свободный IPv4 адрес"
|
||||||
|
CLIENT_HOST_OCTET="${CLIENT_IPV4##*.}"
|
||||||
|
|
||||||
|
if [[ "$USE_IPV6" == "1" ]]; then
|
||||||
|
CLIENT_IPV6="${IPV6_PREFIX}${CLIENT_HOST_OCTET}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Создаю клиента: ${CLIENT_NAME}"
|
||||||
|
echo "IPv4 клиента: ${CLIENT_IPV4}"
|
||||||
|
if [[ "$USE_IPV6" == "1" ]]; then
|
||||||
|
echo "IPv6 клиента: ${CLIENT_IPV6}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
wg genkey | tee "$CLIENT_PRIVKEY_FILE" | wg pubkey > "$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" <<EOF
|
||||||
|
[Interface]
|
||||||
|
PrivateKey = ${CLIENT_PRIVATE_KEY}
|
||||||
|
Address = ${CLIENT_ADDRESS}
|
||||||
|
DNS = ${SERVER_VPN_IP},192.168.0.200
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = ${SERVER_PUBLIC_KEY}
|
||||||
|
PresharedKey = ${CLIENT_PRESHARED_KEY}
|
||||||
|
Endpoint = ${ENDPOINT}:${SERVER_PORT}
|
||||||
|
AllowedIPs = ${CLIENT_ALLOWED_IPS}
|
||||||
|
PersistentKeepalive = 25
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod 600 "$CLIENT_CONF"
|
||||||
|
|
||||||
|
echo "Применяю конфиг WireGuard..."
|
||||||
|
wg syncconf "$WG_INTERFACE" <(wg-quick strip "$WG_INTERFACE")
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Готово."
|
||||||
|
echo "Клиент: ${CLIENT_NAME}"
|
||||||
|
echo "IP: ${CLIENT_IPV4}"
|
||||||
|
echo "Конфиг: ${CLIENT_CONF}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if command -v qrencode >/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}"
|
||||||
Reference in New Issue
Block a user