fix: adopt sing-box 1.14 DNS hijacking

This commit is contained in:
2026-09-08 21:20:08 +05:00
parent 76dce5b47a
commit b6e45d6e77
18 changed files with 219 additions and 99 deletions
+33 -17
View File
@@ -3,7 +3,8 @@
## Границы ответственности
Hysteria2 URI содержит переносимые реквизиты: auth, hostname/ports, SNI,
`insecure`, тип obfs и obfs password. Production HY2XS выдаёт Gecko URI.
`insecure`, тип obfs и obfs password. Новые production-установки HY2XS получают
Gecko URI. Salamander остаётся явно согласуемым compatibility fallback.
`policy.json` хранит локальную инфраструктуру: `eth0`, `eth1`, TUN CIDR и MTU,
route exclusions, таблицу 2022, marks, NFQUEUE, DNS, bandwidth и healthcheck.
@@ -20,18 +21,24 @@ URI + policy
├─ renderer_1_14_0
└─ sing-box check на private candidate
unchanged ───── changed
├─ проверка provenance текущего config
state.json ├─ versioned last-good backup
├─ atomic replace
├─ systemctl restart
└─ bounded healthcheck
current отсутствует ── current существует
provenance gate
│ │
│ unchanged ───── changed
│ │ │
│ ▼ ├─ versioned last-good backup
│ state.json │
└────────────────────────────────┼─ atomic replace
├─ systemctl restart
└─ bounded healthcheck
```
Первое применение после чистой установки не создаёт `last-good`: предыдущего
управляемого конфига нет. При следующих изменениях backup разрешён только если
SHA установленного config совпадает со state этого же релиза и sing-box 1.14.0.
Побайтовое совпадение с candidate не доказывает ownership: существующий config
без такого state отклоняется и не «усыновляется» контроллером.
## Gecko и DNS
@@ -39,11 +46,20 @@ Renderer добавляет для Gecko `min_packet_size=512` и
`max_packet_size=1200`. Эти параметры являются частью локального HY2XS
compatibility profile и не читаются из URI.
В sing-box 1.14.0 TUN по умолчанию использует `dns_mode=hijack`, что меняет
настройки интерфейса и platform-level interception. Проект явно задаёт
`dns_mode=disabled` и сохраняет собственное route action `port 53 ->
hijack-dns`. Bootstrap UDP DNS привязан к `eth0`; remote DoH использует detour
`hy2-out`.
Renderer явно задаёт штатный для sing-box 1.14.0 `dns_mode=hijack`. Поле
`dns_address` не задаётся: sing-box выводит IPv4 DNS address `172.19.0.2` как
следующий адрес после `172.19.0.1/30`, автоматически связывает его с DNS module
и при `auto_redirect=true` устанавливает nftables DNAT для TCP/UDP 53. Поэтому
отдельного route action `hijack-dns` в generated config нет.
Bootstrap UDP DNS привязан к `eth0`; remote DoH использует detour `hy2-out`.
Собственный outbound sing-box исключается из auto-redirect по output mark, что
необходимо подтвердить privileged Linux acceptance без bootstrap recursion.
Platform hijack не создаёт DNS listener на локальном LAN-адресе шлюза: трафик к
такому адресу попадает в kernel `local` table раньше пользовательских rules.
Если DHCP должен выдавать сам gateway как DNS server, требуется отдельный
listener/service, который не входит в контракт 0.2.0.
## Endpoint routing
@@ -73,7 +89,7 @@ Metadata не обеспечивает доверие от root-компроме
## Граница установки
0.2.0 не содержит кода миграции 0.1.0. `debian/preinst` отклоняет in-place
upgrade, а `postinst` отклоняет legacy policy/state или unmanaged
`/etc/sing-box/config.json`. Same-release reinstall допускается, если state и
config согласованы.
0.2.0 не содержит кода миграции 0.1.0. `debian/preinst` отклоняет установку
поверх любого другого релиза, а `postinst` отклоняет legacy policy/state или
unmanaged `/etc/sing-box/config.json`. Same-release reinstall 0.2.0 допускается,
если state и config согласованы.
+4 -2
View File
@@ -59,8 +59,10 @@ obfs=salamander -> требуется obfs-password; compatibility fallback
password без obfs -> ошибка
```
Для HY2XS допустимым production-режимом считается только Gecko. Salamander не
включается на сервере и не выбирается автоматически.
Для новых HY2XS production-установок Gecko является режимом по умолчанию.
Salamander полностью поддерживается как compatibility fallback, но требует
явного совпадения режима и password на сервере и клиенте. Автоматического
переключения между Gecko и Salamander нет.
Query parser применяет RFC percent-decoding, но не form decoding: сырой `+`
сохраняется как `+`, `%2B` также превращается в `+`, а `%26`/`%3D` безопасно
+7 -2
View File
@@ -7,6 +7,9 @@ In-place upgrade 0.1.0 → 0.2.0, автоматическая миграция
отклоняет upgrade. Установка выполняется через удаление старого пакета,
архивирование его данных и настройку 0.2.0 с нуля.
Повторная установка ровно 0.2.0 поверх согласованного состояния 0.2.0 не
является миграцией и поддерживается без этого runbook.
Описанные действия выполнять из console/maintenance-доступа. До успешного E2E
anti-leak guard должен оставаться в nftables.
@@ -62,7 +65,7 @@ systemctl daemon-reload
проверку: архивировать указанный объект и повторить `dpkg --configure
vpn-egressctl`.
## 5. Настроить новую policy и Gecko URI
## 5. Настроить новую policy и URI
Редактировать новый `/etc/vpn-egress/policy.json` вручную. Из старой policy
можно перенести осознанно проверенные локальные значения, но нельзя заменять ею
@@ -78,7 +81,9 @@ vpn-egressctl check
vpn-egressctl diff
```
Проверить, что URI содержит `obfs=gecko`. `check` не запускает service.
Для новой production-установки проверить, что URI содержит `obfs=gecko`.
`obfs=salamander` допускается только для явно настроенного server-side
compatibility fallback. `check` не запускает service.
## 6. Активировать защиту и config
+7 -5
View File
@@ -12,8 +12,10 @@ sudo vpn-egressctl import --stdin
sudo vpn-egressctl import --file /run/credentials/new-hysteria2.uri
```
Production URI HY2XS должен использовать Gecko. Не передавать URI через argv,
environment, shell history, тикет или журнал.
Для новой установки production URI HY2XS должен использовать Gecko. Salamander
допустим как compatibility fallback только при явно совпадающей серверной
настройке. Не передавать URI через argv, environment, shell history, тикет или
журнал.
## Изменение policy
@@ -57,6 +59,6 @@ systemctl reset-failed vpn-egress-sync.service
## Обновления package/engine
0.2.0 не объявляет совместимость с другими версиями приложения или sing-box.
Любой следующий переход выполняется по отдельному release runbook как clean
install. `apt upgrade` не считается допустимой процедурой обновления этого
control plane.
Переход на другой release выполняется по отдельному release runbook как clean
install. Повторная установка ровно 0.2.0 допускается при согласованных state и
config; `apt upgrade` на другой release не считается допустимой процедурой.
+9 -3
View File
@@ -45,9 +45,15 @@ runtime-состояние проверяет `vpn-egressctl doctor`.
## DNS и TLS
`dns_mode=disabled` запрещает новому TUN-механизму 1.14 менять native DNS и
platform interception. DNS контролируется явной route rule. Chrome QUIC
parroting включён (`disable_chrome_parrot=false`); production acceptance обязан
`dns_mode=hijack` включает native DNS integration и platform-level interception
TCP/UDP 53. При `auto_redirect` sing-box направляет non-local DNS на автоматически
выведенный TUN address `172.19.0.2`; явная route rule не используется. Linux не
может этим механизмом перенаправить DNS, адресованный локальному IP самого
шлюза. DoH/DoT также не распознаются как DNS, но проходят через общий TUN/VPN
маршрут как обычный трафик.
Chrome QUIC parroting включён upstream default; поле
`disable_chrome_parrot=false` не рендерится. Production acceptance обязан
подтвердить совместимый RSA/ECDSA/ACME certificate, поскольку Ed25519 с этим
режимом несовместим.
+15 -7
View File
@@ -17,19 +17,27 @@
```
512/1200 совпадает с profile HY2XS и текущими upstream defaults, но рендерится
явно. Hysteria отмечает Gecko как experimental; проект осознанно принимает его
как единственный production obfs сервера HY2XS.
явно. Hysteria отмечает Gecko как experimental; проект осознанно использует его
по умолчанию для новых production-установок HY2XS. Salamander остаётся полностью
поддержанным compatibility fallback при совпадающей серверной настройке.
### TUN DNS
Новый default `dns_mode=hijack` не используется. Renderer задаёт `disabled`,
потому что проект уже имеет явную DNS route rule и отдельные bootstrap/DoH
маршруты. Это решение должно подтверждаться privileged Linux acceptance.
Renderer явно задаёт `dns_mode=hijack`. При `auto_redirect=true` sing-box
перехватывает TCP/UDP 53 на platform level. `dns_address` намеренно отсутствует:
sing-box выводит следующий адрес из TUN subnet (`172.19.0.2`) и автоматически
передаёт обращения к нему в DNS module. Явная route rule `hijack-dns` поэтому
удалена.
Bootstrap DNS остаётся UDP outbound с `bind_interface=eth0`, remote DoH — с
`detour=hy2-out`. Отсутствие bootstrap loop и DNS leak является обязательным
privileged Linux acceptance gate.
### Chrome QUIC parroting
Renderer явно задаёт `disable_chrome_parrot=false`. Серверный certificate должен
быть RSA/ECDSA/ACME, не Ed25519.
Renderer не задаёт `disable_chrome_parrot`: в exact 1.14.0 upstream default
оставляет Chrome QUIC parroting включённым. Серверный certificate должен быть
RSA/ECDSA/ACME, не Ed25519.
### Congestion control
+31 -12
View File
@@ -5,7 +5,8 @@
1. Unit: URI, policy, version, renderer, redaction и metadata.
2. Failure injection: candidate rejection, restart failure, URI/config restore,
unmanaged config, corrupted/legacy last-good.
3. Packaging: MPL-2.0, exact dependency, upgrade rejection и postinst guard.
3. Packaging: MPL-2.0, exact dependency, cross-release rejection,
same-release reinstall и postinst guard.
4. Real binary: официальный sing-box 1.14.0 для Gecko и Salamander configs.
5. Privileged Linux: TUN, systemd, nftables, routing и DNS.
6. HY2XS acceptance: Gecko TCP/UDP/DNS, failures, reconnect и reboot.
@@ -38,15 +39,33 @@ Windows — schema decoding через `format`, поскольку Windows bina
В disposable Incus/VM с двумя NIC проверить:
1. чистую установку и отказ in-place upgrade;
2. Gecko config и отсутствие last-good после первого apply;
3. table 2022, rule 9000/32768, marks и NFQUEUE 100;
4. bootstrap DNS через `eth0`, DoH через `hy2-out`;
5. TCP/UDP/DNS с workload за `eth1`;
6. отсутствие прямого WAN при остановленном sing-box;
7. credential failure и same-release rollback;
8. отказ legacy/mismatched metadata;
9. A-record change и reboot recovery.
1. собрать package и выполнить чистую установку;
2. повторно установить ровно 0.2.0 поверх согласованного состояния;
3. подтвердить отказ переходов 0.1.x → 0.2.0 и другой release → 0.2.0;
4. применить Gecko config и подтвердить отсутствие `last-good` после первого
apply;
5. проверить table 2022, rule 9000/32768, marks и NFQUEUE 100;
6. до установления HY2 разрешить hostname endpoint через `bootstrap-dns` по
`eth0`, без recursion в `172.19.0.2`;
7. подтвердить DoH `remote-dns` через `hy2-out`;
8. с хоста выполнить `getent hosts example.com`, `dig @8.8.8.8 example.com` и
`dig +tcp @8.8.8.8 example.com`;
9. повторить оба `dig` с workload за `eth1` и через `tcpdump`/nft counters
подтвердить отсутствие реального TCP/UDP 53 через `eth0`;
10. при наличии `systemd-resolved` проверить DNS и route-only domain `~.` для
`tun-sb0`; отдельно повторить запуск без доступного `resolvectl`;
11. проверить TCP/UDP и внешний VPN IP с workload за `eth1`;
12. подтвердить отсутствие прямого WAN при остановленном sing-box;
13. проверить credential failure и same-release rollback;
14. проверить отказ legacy/mismatched metadata и unmanaged identical config;
15. проверить смену A-record, reconnect, stop/start и reboot recovery;
16. после stop убедиться в очистке sing-box nftables/resolved state, после start
— в полном восстановлении.
Server-side Gecko E2E выполняет владелец HY2XS. Salamander проверяется только на
disposable test server и не добавляется в production server configuration.
Server-side Gecko E2E выполняется с production-compatible HY2XS. Salamander E2E
выполняется на disposable server, явно настроенном в compatibility-режим; это не
автоматический fallback и не default новой production-установки.
Platform hijack не обслуживает DNS, направленный на локальный LAN IP gateway.
Такой сценарий допустим только после добавления отдельного DNS listener и не
входит в acceptance версии 0.2.0.
+12 -3
View File
@@ -12,17 +12,19 @@ vpn-egressctl status --json
Установлена не exact `1.14.0` либо отсутствуют `with_quic`/`with_gvisor`.
Конфигурация не изменялась. Не обходить version gate.
## In-place upgrades are not supported
## Cross-release upgrades are not supported
Пакет 0.2.0 устанавливается поверх старого релиза. Старый package остаётся
Пакет 0.2.0 устанавливается поверх другого релиза. Старый package остаётся
установленным; выполнить clean-install runbook из `docs/migration.md`.
Повторная установка ровно 0.2.0 поддерживается, если state и config согласованы.
## State from another release / unmanaged configuration
Обнаружены сохранённые файлы 0.1.0 или конфиг вне управления 0.2.0. Остановить
sing-box, убедиться в наличии guard, переместить старые данные в защищённый архив
и повторить `dpkg --configure vpn-egressctl`. Не редактировать старый state так,
чтобы обойти проверку.
чтобы обойти проверку. Даже побайтово идентичный candidate не делает unmanaged
config принадлежащим этому релизу.
## sing-box rejected generated configuration
@@ -54,3 +56,10 @@ Backup и metadata рассогласованы или повреждены. А
Проверить DNS, Gecko password, auth, SNI, certificate, UDP path и nftables.
Успешный локальный healthcheck не заменяет forwarding E2E с `eth1`.
## DNS hijack не обслуживает LAN IP шлюза
`dns_mode=hijack` перехватывает TCP/UDP 53 только к non-local destination. Если
LAN-клиент обращается к адресу самого gateway, например `10.20.0.1:53`, нужен
отдельный DNS listener/service. Не добавлять `dns_address` вручную и не
возвращать route rule как workaround: это меняет проверенный контракт renderer.