docs: описать транзакционный guard и взаимное исключение операций
- docs/07: полный порядок staged apply, инвариант снятия guard, объяснение почему окно 45 секунд не обязано покрывать smoke и почему guard не трогает nftables.service, семантическая проверка эффективного firewall; - docs/11: разделы A5e/A5f для новых unit-тестов и серверные сценарии D1e (guard доходит до дедлайна), D1f (конкурентные операции), D1g (успешная операция не оставляет следов транзакции); матрица и acceptance criteria дополнены; - docs/12: разбор отказов "уже выполняется другая операция" и firewall_guard_fired; - docs/13: строки журнала guard в таблице recovery, новый раздел 8a про замок операций; - docs/14 и purge-v0.sh: очистка /run/hy2xs, замка операций и candidate-файлов firewall — /run это tmpfs, но очистка не имеет права требовать перезагрузки; - README: защита от потери доступа при смене firewall и раздел "Одна операция за раз"; - CHANGELOG: шестой проход.
This commit is contained in:
@@ -82,12 +82,78 @@ IPv4-only policy:
|
||||
|
||||
## Порядок применения
|
||||
|
||||
1. Подготовить candidate-файл (`/etc/nftables.d/hy2xs.nft`).
|
||||
2. Проверить `nft -c -f`.
|
||||
3. Создать rollback timer.
|
||||
4. Применить candidate и проверить SSH/Hysteria/UI.
|
||||
5. При успехе отменить rollback timer.
|
||||
6. При провале — rollback.
|
||||
1. Снять резервную копию `/etc/nftables.conf`, `/etc/nftables.d/hy2xs.nft` и
|
||||
состояния юнита `nftables.service` в `/run/hy2xs/rollback/<op-id>/` и
|
||||
**доказать**, что копия создана. Отказ здесь останавливает операцию до
|
||||
первой мутации.
|
||||
2. Подготовить candidate-файлы и проверить их `nft -c -f`.
|
||||
3. Подставить candidate в production-пути.
|
||||
4. Взвести rollback guard: транзиентный юнит `hy2xs-fw-rollback-<op-id>`
|
||||
с окном 45 секунд.
|
||||
5. Применить ruleset и проверить SSH/Hysteria/UI.
|
||||
6. Снять guard и **доказать**, что он снят (см. ниже).
|
||||
7. Долговечно зафиксировать успех.
|
||||
8. Только после этого удалить данные отката и candidate-файлы.
|
||||
|
||||
Порядок шагов 6–8 существенен: между снятием guard и удалением данных отката
|
||||
стоит фиксация успеха, поэтому отказ записи маркера (заполненный диск,
|
||||
read-only ФС) оставляет откат выполнимым.
|
||||
|
||||
### Rollback guard
|
||||
|
||||
Guard — это защита от потери доступа к серверу. Он существует ради ситуации, в
|
||||
которой применённые правила отрезали SSH и оператор больше не может ничего
|
||||
сделать руками.
|
||||
|
||||
Окно guard намеренно короткое — 45 секунд — и намеренно **не** покрывает
|
||||
smoke: smoke на медленном, но исправном сервере может идти заметно дольше.
|
||||
Увеличение окна лечило бы гонку расширением, а не устранением.
|
||||
|
||||
Вместо этого guard оставляет за собой факт:
|
||||
|
||||
```text
|
||||
/run/hy2xs/rollback/<op-id>/auto-rollback-fired
|
||||
```
|
||||
|
||||
Маркер создаётся rollback-скриптом **первым действием**, до любой проверки и до
|
||||
первой попытки восстановления. Отсюда инвариант фиксации успеха:
|
||||
|
||||
```text
|
||||
маркер auto-rollback-fired отсутствует
|
||||
И hy2xs-fw-rollback-<op-id>.timer в состоянии inactive
|
||||
И hy2xs-fw-rollback-<op-id>.service в состоянии inactive
|
||||
=> автоматический откат больше не может сработать
|
||||
```
|
||||
|
||||
Пока этот инвариант не доказан, `phase: installed` не записывается. Если guard
|
||||
успел сработать, операция **обязана** завершиться отказом — даже если smoke
|
||||
прошёл зелёным: сервер в этот момент работает на прежнем firewall, а не на том,
|
||||
который сгенерировала операция.
|
||||
|
||||
Проверка состояния юнитов идёт по `ActiveState`, а не по коду возврата
|
||||
`systemctl stop`: для транзиентного юнита, который уже отработал и был убран
|
||||
systemd, `stop` возвращает 5, и этот исход неотличим от успешного снятия
|
||||
взведённого таймера.
|
||||
|
||||
Сам rollback-скрипт восстанавливает файлы и ruleset, накапливает код возврата и
|
||||
уходит в `failed` при частичном восстановлении. Состояние `nftables.service` он
|
||||
сознательно не трогает: у этого юнита `ExecStop=/usr/sbin/nft flush ruleset`, то
|
||||
есть остановка сервиса стёрла бы только что восстановленные правила. Enable и
|
||||
active восстанавливает обычный откат в процессе оркестратора, где порядок стадий
|
||||
контролируется.
|
||||
|
||||
### Проверка эффективного firewall
|
||||
|
||||
`nft -c -f /etc/nftables.conf` разбирает текущий файл, каким бы он ни был, и
|
||||
поэтому ничего не говорит о том, чей это firewall. Smoke дополнительно сверяет:
|
||||
|
||||
1. `/etc/nftables.d/hy2xs.nft` совпадает с фрагментом, отрендеренным для этой
|
||||
конфигурации;
|
||||
2. `/etc/nftables.conf` принадлежит HY2XS и подключает именно его;
|
||||
3. таблица `inet hy2xs` реально загружена в ядро.
|
||||
|
||||
Все три — наблюдение, поэтому проверка выполняется и в `doctor`, где она
|
||||
обнаруживает расхождение effective firewall с конфигурацией.
|
||||
|
||||
## Что не делаем
|
||||
|
||||
|
||||
Reference in New Issue
Block a user