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-команды.
Проект можно копировать как каталог:
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.
Примеры используют только документационные адреса и имена:
./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 -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-owned0600config, абсолютные пути, отсутствие symlink и соответствие интерфейса.69для TUI/QR: установите совместимый gum/qrencode самостоятельно или используйте CLI без capability; TUI не скачивает бинарники.70: проверьте stderr, lock и доступностьwg-quick/wg; исходный config должен оставаться восстановленным.71 КРИТИЧНО: остановите mutation, сохраните backup/archive и вручную проверьте disk/runtime перед повтором.diagnoseSKIP: 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 (не выполнен)
- Создать отдельный non-production interface и disposable host.
- Скопировать каталог проекта, не устанавливая его в
PATH. - Проверить Bash, WireGuard tools, coreutils/util-linux и архитектуру gum.
- Скопировать example config и заменить только placeholder values.
- Проверить owner root, config
0600и каталоги0700. - Выполнить
help,version,statusиlist. - Выполнить
diagnoseи сохранить безопасный отчёт. - Добавить disposable client и проверить modes/metadata/audit.
- Проверить
show, safeconfigpath и QR fallback. - Удалить client с явным подтверждением и проверить archive.
- Создать backup и проверить newest-first listing.
- Восстановить backup с typed confirmation на disposable config.
- Проверить lock contention и rollback на тестовом runtime.
- Сверить SSH/noninteractive, firewall visibility и systemd limitations.
- Только после human review решить, допустим ли production rollout.
Этот список намеренно не выполняется агентом и не является разрешением менять реальный host state без отдельной проверки оператором.