Files

14 KiB
Raw Permalink Blame History

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-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 без отдельной проверки оператором.