fix(v1): сделать отзыв доступа, бэкап и диагностику соответствующими своим именам
Проход по операциям, которые делают не то, что обещает их имя. P0. Удаление bootstrap-admin-peer не было отзывом доступа. Признаком «создавать пир или нет» служило наличие строки в таблице, а HY2XS_ADMIN_CON_PASS продолжает жить в /etc/hy2xs/hy2xs.env — его читает systemd-юнит. Оператор удалял пира, доступ исчезал, и ближайший restart возвращал того же пира с тем же секретом. Молча. Признаком стала отметка BOOTSTRAP_PEER_SEEDED в таблице config: «создавался когда-либо», а не «существует сейчас». Отметка и пир пишутся одной транзакцией. P1. Резервная копия с includeSecrets=true проглатывала и ошибку расшифровки, и отсутствие шифртекста, отдавая пира с пустым secret и успешный ответ. Теперь недоступный секрет любого пира отклоняет весь запрос с указанием имени. P1. DecryptPeerSecret возвращала содержимое колонки как расшифрованный секрет, если оно не начиналось с v1: — остаток поколения с открытыми секретами. P1. doctor перезапускал hysteria-server и hy2xs-admin: диагностика подозрения на проблему обрывала все живые соединения. P1. Админка сама генерировала HYSTERIA2_TRAFFIC_STATS_SECRET, записать который в /etc/hysteria/config.yaml она не может. Сервис объявлял себя здоровым, а machine auth переставал совпадать. P1. Обходы проверки зависимостей (accepted-risk/skipped) не могли произвести артефакт: приёмка требует dependency_security_gate=true. Удалены из сборки и документации, отсутствие проверяется приёмкой. P2. UPDATE по отсутствующей строке config считался успехом, и cron перепланировался при несохранённом значении. Решение по RowsAffected. P2. Слой данных не отличал «записи нет» от «база не ответила»: sentinel-значения ErrPeerNotFound / ErrAdminUserNotFound / ErrConfigNotFound / ErrStorage. P2. Удалены алиасы /:id/client-url и /:id/qr. Контракт разработки: apps/go.mod объявляет toolchain go1.26.7 (директива go — языковой baseline, а не выбор компилятора), tools/dev/doctor.sh|.ps1 сверяют среду с versions.env.
This commit is contained in:
@@ -369,6 +369,52 @@ wildcard-маршрутом фронтенда или дублирующая р
|
||||
- удаление **разрешено**: это осознанное действие оператора, и расхождения
|
||||
между базой и `bootstrap-admin.secret` оно не создаёт.
|
||||
|
||||
## A9e. Жизненный цикл пира установщика (unit, настоящая SQLite)
|
||||
|
||||
`apps/dao/bootstrap_peer_test.go` — проверяется не функция, а поведение сервиса
|
||||
при перезапуске: дефект, ради которого написан этот файл, проявлялся только на
|
||||
ВТОРОМ запуске, поэтому каждый тест прогоняет полную последовательность
|
||||
`InitSqlAt` дважды на одной базе.
|
||||
|
||||
- первый запуск создаёт пира и выставляет отметку `BOOTSTRAP_PEER_SEEDED`;
|
||||
- обычный перезапуск не пересоздаёт пира и не плодит дублей (`id` тот же,
|
||||
запись ровно одна);
|
||||
- **удаление переживает перезапуск**: после `DELETE` и рестарта пир не
|
||||
возвращается, хотя `HY2XS_ADMIN_CON_PASS` остаётся в окружении;
|
||||
- то же после трёх перезапусков подряд;
|
||||
- отключённый пир сохраняет `disabled = 1` и свой `secret_digest`;
|
||||
- отметка и пир пишутся одной транзакцией: при конфликте `UNIQUE(name)` внутри
|
||||
транзакции отметка не остаётся выставленной;
|
||||
- отсутствие `HY2XS_ADMIN_CON_PASS` на чистой базе — отказ старта;
|
||||
- перезапуск установленного сервиса без этой переменной проходит штатно;
|
||||
- `HYSTERIA2_TRAFFIC_STATS_SECRET`: пустой env при пустой базе — отказ старта,
|
||||
сгенерированного токена в базе не появляется; токен, уже согласованный
|
||||
ранее, принимается без переменной.
|
||||
|
||||
## A9f. Резервная копия пиров (unit)
|
||||
|
||||
`apps/service/peer_export_backup_test.go`:
|
||||
|
||||
- `includeSecrets=true` на исправных данных отдаёт секрет каждого пира;
|
||||
- нерасшифровываемый секрет хотя бы одного пира отклоняет **весь** запрос,
|
||||
сообщение называет пира, частичное содержимое не возвращается;
|
||||
- пир вовсе без шифртекста — тот же отказ;
|
||||
- `includeSecrets=false` повреждённых данных не замечает и пустой `secret`
|
||||
отдаёт штатно: это и есть смысл безопасного режима.
|
||||
|
||||
## A9g. Слой данных: «нет записи» против «база не ответила» (unit)
|
||||
|
||||
`apps/dao/config_test.go`:
|
||||
|
||||
- `UpdateConfig` по отсутствующей строке — **отказ**, а не тихий успех: UPDATE
|
||||
без совпавших строк не является ошибкой SQL, и раньше оператор получал
|
||||
подтверждение изменения, которого не произошло, а планировщик тут же получал
|
||||
новое расписание;
|
||||
- `UpdateConfig` не создаёт строк: это работа `UpsertConfigValue`;
|
||||
- транзакционная партия откатывается целиком, если одна из строк отсутствует;
|
||||
- `GetConfig`/`GetPeer` возвращают `ErrConfigNotFound`/`ErrPeerNotFound`,
|
||||
отличимые через `errors.Is` от `ErrStorage`.
|
||||
|
||||
## A10. Импорт пиров (unit)
|
||||
|
||||
`apps/service/peer_import_test.go`:
|
||||
@@ -811,3 +857,12 @@ hy2xs-orchestrator doctor
|
||||
32. документация нигде не утверждает, что Salamander — фиксированный инвариант
|
||||
33. документация не фиксирует конкретный номер версии как «текущую версию», а объясняет latest-stable build policy
|
||||
34. форма создания пира содержит примеры значений и пояснения для полей «Пир», «Комментарий» и «Секрет»
|
||||
35. `hy2xs-orchestrator doctor` не перезапускает сервисы и не рвёт живые соединения
|
||||
36. удаление `bootstrap-admin-peer` переживает `systemctl restart` и `reboot`: пир не воскресает
|
||||
37. отключённый `bootstrap-admin-peer` остаётся отключённым после перезапуска
|
||||
38. резервная копия с `includeSecrets=true` завершается ошибкой целиком, если секрет хотя бы одного пира недоступен
|
||||
39. админка не генерирует `HYSTERIA2_TRAFFIC_STATS_SECRET` сама: пустой env при пустой базе — отказ старта
|
||||
40. проверка зависимостей на уязвимости не имеет обходов ни в сборке, ни в документации
|
||||
41. `apps/go.mod` объявляет `toolchain`, совпадающий с `GO_VERSION` из `versions.env`
|
||||
42. `tools/dev/doctor.sh` / `doctor.ps1` показывают расхождение среды разработки с `versions.env`
|
||||
43. маршруты-алиасы `/:id/client-url` и `/:id/qr` удалены и не входят в публичный API v1
|
||||
|
||||
Reference in New Issue
Block a user