# singbox_glue / vpn-egressctl `vpn-egressctl` — локальный декларативный control plane для шлюза `vpn-egress-gw`. Он получает реквизиты подключения из защищённого Hysteria2 URI, объединяет их с локальной инфраструктурной политикой и полностью генерирует конфигурацию sing-box. Проект намеренно поддерживает только **sing-box 1.13.19**. Версии 1.13.12, другие patch-релизы и вся ветка 1.14 отклоняются до изменения файлов. ## Что решает проект - IP Hysteria2-сервера отсутствует в `route_exclude_address` и nftables policy. - Смена DNS A-записи не требует перегенерации конфигурации. - Смена credential выполняется одной безопасной командой через stdin. - Перед установкой candidate проверяется реальным `sing-box check`. - Запись атомарна; после неуспешного restart/healthcheck выполняется rollback. - URI, production config, state и backups имеют режим `0600`. - Неизвестные URI/policy-параметры отклоняются, а не игнорируются. - systemd следит за desired state без постоянно работающего Python-процесса. - Отдельный nftables guard блокирует прямой forwarding `eth1 -> eth0`. ## Источники состояния ```text /etc/vpn-egress/policy.json /etc/vpn-egress/hysteria2.uri │ ▼ renderer 1.13.19 │ ▼ /etc/sing-box/config.json ``` `/etc/sing-box/config.json` является генерируемым артефактом. Редактировать его вручную после миграции нельзя. ## Основные команды ```bash # URI не попадает в argv и shell history. sudo vpn-egressctl import --stdin sudo vpn-egressctl check sudo vpn-egressctl diff sudo vpn-egressctl sync sudo vpn-egressctl status sudo vpn-egressctl doctor sudo vpn-egressctl rollback ``` Позиционный `vpn-egressctl import 'hysteria2://...'` запрещён специально. ## Документация - [Архитектура](docs/architecture.md) - [Конфигурация](docs/configuration.md) - [Установка и миграция](docs/migration.md) - [Эксплуатация](docs/operations.md) - [Безопасность](docs/security.md) - [Диагностика](docs/troubleshooting.md) - [Тестирование](docs/testing.md) - [Почему не поддерживается 1.14](docs/sing-box-1.14.md) ## Локальная проверка Проект не имеет runtime-зависимостей вне Python stdlib. ```powershell E:\python-31312\python.exe -m compileall -q src tests ``` Изолированная Windows-сборка Python в указанном каталоге не добавляет cwd в `sys.path`, поэтому полный тестовый запуск выполняется так: ```powershell E:\python-31312\python.exe -c "import sys,unittest; sys.path[:0]=[r'F:\projects\singbox_glue\src',r'F:\projects\singbox_glue']; s=unittest.defaultTestLoader.discover(r'F:\projects\singbox_glue\tests'); r=unittest.TextTestRunner(verbosity=2).run(s); raise SystemExit(not r.wasSuccessful())" ``` На Linux достаточно `make check`.