Мёртвый переключатель для редактирования брандмауэра
Самая страшная строка в автоматизации домашней лаборатории — та, которая редактирует правило брандмауэра на маршрутизаторе, к которому вы подключены по SSH.
Вот как Claude Code и я всё равно их редактируем.
Страх
Мне нужно было изменить правило UDM Pro под названием “Block inter-VLAN traffic” с accept на drop. Правило было неправильно настроено долгое время — установлено в accept, что замыкало все разрешения выше по потоку — и закрыть его обратно было основной целью. Оркестратор, выполнявший изменение, был VM NixOS в моей домашней сети. Если переключение прервало бы путь, который использовала та VM (или мой собственный SSH), я оказался бы заблокирован на собственном маршрутизаторе без консольного резерва.
Решение не в том, чтобы “быть осторожным”. Решение в том, чтобы сделать опасное изменение с автоматическим откатом, если я не подтверждаю, что всё в порядке.
Этот шаблон вам уже знаком. Измените разрешение монитора в macOS или Windows, и система применит его на пятнадцать секунд с обратным отсчётом в диалоге “Сохранить эти параметры экрана?”. Если экран почернел, вы не можете ничего нажать — и в этом весь смысл. Результат по умолчанию — откат. Подтверждение — по желанию.
Именно это нам и нужно при переключении брандмауэра на маршрутизаторе, правила которого мы собираемся изменить.
Трюк
udm_set_firewall_rules.py --flip-with-revert 300 --apply
Читать как: “переключить правило, но автоматически откатить через 300 секунд, если я не скажу иначе.”
Что происходит по порядку:
- Снимок. GET текущего правила и сохранение полного JSON-payload на UDM в
/root/.udm-flip-revert/payload-<id>.json. - Подготовка отката. Рядом с payload записывается небольшой shell-скрипт, который снова PUT-ит снимок в UDM API.
- Планирование отката.
systemd-run --on-active=300s --unit=udm-flip-revert-<id>ставит скрипт в очередь как временный таймер. - Переключение. PUT нового правила (
action: drop).
Теперь у вас есть 5-минутное окно для проверки:
✓ flipped 'Block inter-VLAN traffic' to action='drop'
⏱ auto-revert scheduled (~300s)
TO KEEP THE FLIP: ssh udm 'systemctl stop udm-flip-revert-66c1d…timer'
TO ROLL BACK NOW: ssh udm 'systemctl stop …timer; bash /root/.udm-flip-revert/revert-….sh'
WAIT IT OUT: do nothing — timer will revert in ~300s
Три исхода:
- Сработало. Останавливаем таймер. Переключение постоянно.
- Что-то сломалось. Останавливаем таймер, запускаем скрипт отката. Или просто перестаём печатать — таймер сработает через N секунд, и правило вернётся само.
- Меня заблокировало. Аналогично. Таймеру не нужно моё участие для срабатывания.
Третий случай — вот ради чего всё это существует.
Почему systemd-run, а не at(1)
UDM Pro (Debian 11) не поставляется с atd. Зато там есть systemd. systemd-run --on-active=Ns создаёт временный таймер, который срабатывает один раз и самоудаляется — это именно форма “сделай это через N секунд и исчезни”.
ssh_udm(
f"systemd-run --on-active={seconds} --unit={unit} --collect "
f"--quiet /bin/bash {script_file}"
)
--collect — ключевой флаг: без него юнит зависает в состоянии failed/inactive и засоряет systemctl.
Оговорка
Временные юниты systemd живут в /run, который является tmpfs. Если UDM перезагрузится в пределах окна отката, таймер исчезнет — и переключение останется применённым без присмотра мёртвого переключателя.
Контринтуитивно, но реально: держите окно коротким. 5-минутное окно с небольшим риском перезагрузки безопаснее, чем 1-часовое окно, при котором сбой ИБП на 12-й минуте оставит вас с постоянной блокировкой. Достаточно долгое для проверки, достаточно короткое, чтобы “забыл ли я про ожидающий откат?” никогда не становилось вопросом.
Вывод
Паттерн мёртвого переключателя обобщается за пределы правил брандмауэра UDM. В любом месте, где вы собираетесь применить изменение, которое может оборвать путь, через который вы его применяли — таблицы маршрутизации, sshd_config, nftables, BGP — работает та же форма:
- Зафиксировать текущее состояние
- Подготовить скрипт, который его восстанавливает
- Запланировать запуск скрипта через N секунд
- Применить изменение
Если вы всё ещё здесь через N+ε секунд и всё выглядит нормально, отмените таймер. Если нет, таймер отменит вас.
Мне лучше спится с этим в наборе инструментов.