Files
wireguard-scripts/README.md
T

226 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# wireguard-scripts
Безопасный копируемый Bash-инструмент для управления одним WireGuard-интерфейсом
на Linux. Скрипт не устанавливается в `PATH`, не запускает установщики и не
изменяет систему вне явно настроенных путей. Операции изменения выполняются под
`flock`, с резервной копией, атомарной заменой, проверкой runtime и откатом.
## Поддерживаемая среда и ограничения
Требуются Linux, Bash 4.3+, `wg`, `wg-quick`, `ip`, `flock`, стандартные
coreutils и util-linux. Минимальная заявленная архитектура — x86_64. Другие
архитектуры не считаются протестированными; `gum` и `qrencode` должны
соответствовать архитектуре самостоятельно установленного оператором бинарника.
Обязательные capabilities: `bash`, `wg`, `wg-quick`, `ip`, `flock`, `awk`,
`grep`, `sed`, `sort`, `stat`, `mktemp`, `sha256sum`, `cp`, `mv`, `chmod` и
прочие обычные coreutils, используемые системой. Необязательные: `gum` (TUI),
`qrencode` (QR), `systemctl`/`journalctl` (systemd), `ss` (UDP), `nft` и
`iptables` (наблюдение firewall). Отсутствие optional capability даёт
`SKIP`/fallback, а не скрытый вызов host-команды.
Проект можно копировать как каталог:
```text
wireguard-scripts/
├── wg-manager.sh
├── wg-manager.conf.example
├── add-user.sh
├── bin/gum # optional, если оператор его положил сюда
├── tests/
│ ├── fixtures/ # placeholder-only fixtures
│ └── mocks/bin/ # deny-by-default test commands
├── backups/ logs/ state/ # runtime paths, создаются при настройке
└── README.md
```
## Настройка
Скопируйте `wg-manager.conf.example` в `wg-manager.conf`, замените только
placeholder-значения и задайте владельца root и режим `0600`. Все пути должны
быть абсолютными, без symlink-компонентов, а каталоги client/metadata/backup/
archive/log — `0700`; конфигурации, metadata, key и log — `0600`.
| Ключ | Назначение и ограничения |
|---|---|
| `WG_INTERFACE` | имя интерфейса, например `wg0` |
| `WG_CONF` | абсолютный серверный конфиг |
| `CLIENT_DIR` | каталог клиентских `.conf/.key/.pub/.psk` |
| `SERVER_ENDPOINT` | проверяемое имя/IP сервера без порта |
| `SERVER_PORT` | UDP-порт 1..65535 |
| `SERVER_VPN_IP` | адрес сервера IPv4 |
| `CLIENT_DNS` | непустой comma-separated список IPv4/IPv6 |
| `VPN_SUBNET_PREFIX` | IPv4 prefix из трёх октетов, например `192.0.2` |
| `START_HOST` | начало пула host-частей |
| `END_HOST` | конец пула host-частей |
| `USE_IPV6` | `0` или `1` |
| `IPV6_PREFIX` | prefix, оканчивающийся `::`, если IPv6 включён |
| `DEFAULT_CLIENT_MODE` | `full-tunnel` или `split-tunnel` |
| `RECENT_HANDSHAKE_SECONDS` | неотрицательное окно active-state |
| `BACKUP_RETENTION` | bounded число automatic backups |
Окружение может изолированно переопределить расположение:
`WG_MANAGER_CONFIG`, `WG_MANAGER_BACKUP_DIR`, `WG_MANAGER_LOG_DIR`,
`WG_MANAGER_METADATA_DIR`, `WG_MANAGER_ARCHIVE_DIR`, `WG_MANAGER_LOCK_FILE`.
`WG_MANAGER_GUM` имеет приоритет над локальным `bin/gum`, затем `PATH`.
`WG_MANAGER_NOW` — только документированный deterministic clock override; перед
боевым запуском эти переменные следует убрать. Test-only hooks принимаются лишь
в confined `WG_MANAGER_TEST_MODE` под `TEST_ROOT` и не являются production API.
## Команды и коды
| Команда | Поведение, stdout и подтверждение |
|---|---|
| `help`, `version` | безопасная справка/версия, без mutation |
| `status` | disk/runtime summary, без secrets |
| `list` | tab-separated peer rows с `state`/`origin`, без secrets |
| `add NAME [--mode MODE] [--split-networks CIDRS] [--endpoint HOST]` | явный add под lock |
| `show NAME` | identity-safe details и safe path, если он доступен |
| `config NAME` | печатает только валидированный путь |
| `config NAME --show` | единственная форма полного client config |
| `qr NAME` | QR через stdin; без `qrencode` печатает путь и возвращает `69` |
| `remove NAME [--yes]` | managed peer, archive вместо purge; noninteractive требует `--yes` |
| `diagnose [--quiet]` | read-only checks; quiet даёт `id=STATUS` |
| `backup` | ручная backup текущего server config под lock |
| `backups` | только валидные backups current interface, newest-first |
| `restore BACKUP [--yes]` | CLI: без `--yes` нужен обычный интерактивный `y/N`; без terminal команда отклоняется. В TUI требуется ввод полного backup ID; сначала создаётся current-state backup |
| `log [LIMIT]` | validated audit records newest-first, `LIMIT` 1..1000 |
| `tui` или запуск без аргументов | gum TUI, требует terminal stdin и stdout |
Коды: `0` — успех или безопасная отмена; `64` — usage/flags/arity; `65` —
validation или unsafe identity/path; `69` — отсутствующая optional capability;
`70` — operation/backend failure; `71` — critical rollback/recovery failure;
`77` — недостаточные права. Ошибка audit после mutation сообщается как partial
operation и не маскирует исходный status. `FAIL` в diagnose даёт `70`,
`WARN`/`SKIP` сами по себе не дают failure.
Примеры используют только документационные адреса и имена:
```bash
./wg-manager.sh status
./wg-manager.sh list
./wg-manager.sh add alice --mode full-tunnel
./wg-manager.sh add bob --mode split-tunnel --split-networks '192.0.2.0/24,2001:db8:40::/48' --endpoint vpn.example.invalid
./wg-manager.sh show alice
./wg-manager.sh config alice
./wg-manager.sh config alice --show
./wg-manager.sh qr alice
./wg-manager.sh remove alice --yes
./wg-manager.sh diagnose --quiet
./wg-manager.sh log 25
./wg-manager.sh backup
./wg-manager.sh backups
./wg-manager.sh restore 20231114T221500Z-wg0-001 --yes
./wg-manager.sh tui
./add-user.sh alice vpn.example.invalid
```
`add-user.sh` — source-safe compatibility wrapper: он только передаёт
`add NAME [--endpoint HOST]`, не исполняет аргументы и не содержит второй
реализации backend.
## Транзакции, backups, archive и audit
Каждая mutation повторно читает ресурсы после lock, валидирует staged config,
создаёт backup, делает atomic replace, выполняет `wg syncconf`, затем сравнивает
runtime dump. При ошибке восстанавливаются disk и runtime; если восстановление
не доказано, возвращается `71` и сообщение `КРИТИЧНО`. Add публикует client
artifacts только после успешной server transaction. Remove архивирует связанные
файлы в новый restrictive каталог и не удаляет архивы автоматически. Restore
перед применением создаёт backup текущего состояния, не восстанавливает client
metadata/key files и принимает только validated backup своего интерфейса.
Audit line содержит только UTC timestamp, actor UID, safe target, outcome и
короткую error category. PrivateKey, PresharedKey, полный config, команды и
окружение туда не попадают. `log` не source-ит записи и пропускает
malformed/hostile lines. Handshake — текущее значение dump, не история.
`firewall=OK` означает только видимое ожидаемое NAT-свидетельство, не полную
проверку политики firewall.
## TUI
TUI требует пригодный `gum` и оба terminal descriptors. Main screen показывает
interface/runtime/managed/active/warning counts; Escape возвращает на предыдущий
экран, отмена mutation ничего не меняет. Клиент выбирается по стабильной
trusted identity, а не по decorated name. Config/QR требуют отдельного действия;
missing `qrencode` — безопасный fallback. Critical rollback немедленно выходит
с `71`; обычная ошибка сохраняет диагностический текст и возвращается в меню
ограниченное число раз. Backend functions не вызывают gum напрямую.
## Offline-проверка
Тесты используют только temporary `TEST_ROOT`, deny-by-default mocks и placeholder
keys. Они не читают реальный WireGuard/network/service/firewall/sysctl state:
```bash
bash -n wg-manager.sh add-user.sh tests/run.sh tests/helpers.sh
bash tests/run.sh
bash tests/run.sh m11-e2e
bash tests/run.sh secret-scan
bash tests/run.sh no-host-writes
bash tests/run.sh no-host-command-bypass
bash tests/run.sh symlink-safety
git diff --check
```
Тестовые mocks не заменяют kernel WireGuard, distribution-specific systemd,
firewall policy или архитектурный smoke-test на disposable interface.
## Troubleshooting
- `65`: проверьте root-owned `0600` config, абсолютные пути, отсутствие symlink
и соответствие интерфейса.
- `69` для TUI/QR: установите совместимый gum/qrencode самостоятельно или
используйте CLI без capability; TUI не скачивает бинарники.
- `70`: проверьте stderr, lock и доступность `wg-quick`/`wg`; исходный config
должен оставаться восстановленным.
- `71 КРИТИЧНО`: остановите mutation, сохраните backup/archive и вручную
проверьте disk/runtime перед повтором.
- `diagnose` `SKIP`: capability отсутствует или systemd не является active init.
- Для gum проверьте архитектуру, `WG_MANAGER_GUM`, локальный `bin/gum` и `PATH`.
## M11 evidence matrix
| Требование | Evidence |
|---|---|
| status / list | `status`, `list`, `m11-e2e` — машинный вывод и смешанные origins |
| add и allocation | `add-client`, `tui-add`, `m11-e2e` — полная/выборочная сеть и rollback |
| show / config export | `show-client`, `config-export`, `m11-e2e` |
| QR и fallback | `qr`, `tui-client-actions`, `secret-scan` |
| remove / archive | `remove-client`, `archive-client`, `tui-remove`, `m11-e2e` |
| diagnose | `diagnostics`, `diagnostics-exit-codes`, `m11-e2e` |
| backup / backups | `backup-list`, `runtime-file-modes`, `tui-backup` |
| restore | `restore`, `restore-rollback`, `tui-restore`, `m11-e2e` |
| audit / log | `audit`, `audit-no-secrets`, `secret-scan` |
| public-key identity | `parser`, `show-client`, `remove-client`, `tui-client-actions` |
| transaction, apply и rollback | `transaction`, `rollback`, `add-rollback`, `m11-e2e` |
| paths, symlinks и modes | `path-safety`, `symlink-safety`, `backup-path-safety`, `runtime-file-modes` |
| secret redaction | `no-secret-logs`, `secret-scan`, `tui-no-secret-args` |
| host write/command isolation | `no-host-writes`, `no-host-command-bypass` |
| exit/status semantics | `config`, `diagnostics-exit-codes`, `rollback`, CLI-focused groups |
| docs и deployment | этот README и disposable checklist; live-server deployment остаётся ручным шагом |
Это offline evidence, а не обещание, что live server уже настроен или что
deployment checklist выполнен.
## Disposable deployment checklist (не выполнен)
1. Создать отдельный non-production interface и disposable host.
2. Скопировать каталог проекта, не устанавливая его в `PATH`.
3. Проверить Bash, WireGuard tools, coreutils/util-linux и архитектуру gum.
4. Скопировать example config и заменить только placeholder values.
5. Проверить owner root, config `0600` и каталоги `0700`.
6. Выполнить `help`, `version`, `status` и `list`.
7. Выполнить `diagnose` и сохранить безопасный отчёт.
8. Добавить disposable client и проверить modes/metadata/audit.
9. Проверить `show`, safe `config` path и QR fallback.
10. Удалить client с явным подтверждением и проверить archive.
11. Создать backup и проверить newest-first listing.
12. Восстановить backup с typed confirmation на disposable config.
13. Проверить lock contention и rollback на тестовом runtime.
14. Сверить SSH/noninteractive, firewall visibility и systemd limitations.
15. Только после human review решить, допустим ли production rollout.
Этот список намеренно не выполняется агентом и не является разрешением менять
реальный host state без отдельной проверки оператором.