Kill switch на роутере, покрывающий FORWARD, OUTPUT и IPv6
Kill switch только в FORWARD оставляет роутер свободным для утечек, а правила только для IPv4 оставляют IPv6 открытым. Упорядочьте цепочки и проверьте при опущенном туннеле.
Вы входите в роутер, вставляете набор правил kill switch из поста на форуме, и LAN перестаёт ходить в интернет в тот самый момент, когда туннель падает. Два дня спустя роутер всё ещё обновляет свою динамическую запись DNS через обычный WAN, а гостевой ноутбук всё ещё достигает сайтов IPv6. Оба поведения нормальны, потому что вставленные правила покрывали одну цепочку и одно семейство адресов, и ни то ни другое не является всем роутером. Проход по путям пакетов по порядку показывает именно то, что было упущено.
Начните с роутера, а не с ноутбука
Политика на уровне роутера привлекательна для небольшого офиса, потому что каждое устройство за ним наследует одинаковое поведение и никто не может забыть включить переключатель на клиенте. Она также даёт одно место для проверки после каждого обновления прошивки. Чего она не даёт — автоматического покрытия собственного трафика роутера или второго семейства адресов, и именно это две дыры за большинством неудачных внедрений.
Прежде чем что-то менять, запишите три факта на бумаге: имя интерфейса WAN, имя моста LAN, а также адрес и порт конечной точки VPN. Все три понадобятся в правилах, а угадывание имён интерфейсов — самая частая причина, по которой правильно выглядящий набор правил не делает ничего.
Пройдите три пути, которые может выбрать пакет
Трафик с ноутбука в интернет пересылается, поэтому он проходит через цепочку FORWARD. Трафик, который создаёт сам роутер, например пересылка DNS, запросы NTP, обновления DDNS и канал управления VPN, проходит через OUTPUT. Управляющий трафик, адресованный роутеру, приходит через INPUT.
Поэтому kill switch, построенный только в FORWARD, защищает клиентов и ничего больше. Таблица показывает, почему утечка из вступительной сцены предсказуема, а не загадочна.
| Packet path | Example | Chain that sees it |
|---|---|---|
| LAN client to WAN or tunnel | Browser request | FORWARD |
| Router-originated | DDNS update, NTP | OUTPUT |
| Addressed to the router | SSH from the LAN | INPUT |
В большинстве проектов kill switch на INPUT не нужен, но нужно поддерживать управляющий доступ работающим, пока туннель опущен. Иначе при первом отказе VPN вы поедете в офис с консольным кабелем.
Упорядочьте правила так, чтобы туннель всё ещё мог подняться
Работающий kill switch — это список разрешений, который заканчивается отказом, и именно порядок делает его kill switch, а не кирпичом. Сначала разрешите loopback, конечную точку VPN, ответы для установленных потоков и интерфейс туннеля; остальное отклоните последним.
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -d 198.51.100.7 -p udp --dport 51820 -j ACCEPT
iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -o wg0 -j ACCEPT
iptables -A OUTPUT -o eth0 -j REJECT
Конечная точка на этом этапе должна быть адресом, а не именем хоста. Как только финальный отказ блокирует DNS, правило, в котором указано имя хоста провайдера, больше нельзя разрешить, и туннель больше никогда не запустится после перезагрузки. Если провайдер меняет конечные точки, разрешите имя до применения политики и обновляйте это правило из того же скрипта.
Повторите форму в FORWARD для клиентского трафика и поместите явное правило управления выше отказа. Держите это правило управления узким, но достижимым, и подтвердите его, открыв вторую сессию SSH до того, как закроете первую. Запереть себя снаружи во время изменения kill switch случается достаточно часто, чтобы это планировать. На роутере с WireGuard allowed-ips партнёра решают, что входит в туннель, а эти правила решают, чему разрешено выходить без него.
IPv6 нужны собственные ворота
Всё выше — это IPv4. Если в вашей LAN работает IPv6, а туннель несёт только IPv4, у каждого покрытого устройства остаётся прямой маршрут в интернет, которого ваши новые правила никогда не видят. Ноутбук с двойным стеком охотно предпочтёт его, и kill switch покажется сломанным только тому, кто проверяет с IPv4.
ip6tables -A FORWARD -i br-lan -j REJECT
ip6tables -A OUTPUT -o eth0 -j REJECT
ip6tables -A OUTPUT -o lo -j ACCEPT
Здесь порядок тоже важен: разрешение loopback должно идти перед отказом WAN, иначе локальные службы на роутере начнут странно отказывать. Если сеть вообще не использует IPv6, проще отключить его на WAN и LAN, и не останется ничего, что можно забыть.
Некоторые клиенты VPN устанавливают маршрут по умолчанию только для IPv4 и оставляют существующий маршрут по умолчанию IPv6 нетронутым. Проверьте таблицу маршрутизации после подключения туннеля, а не предполагайте, что клиент всё уладил.
Докажите это при опущенном туннеле
Остановите туннель с роутера и проверьте из трёх мест в таком порядке:
# systemctl stop wg-quick@wg0
# ip route get 203.0.113.1
# curl -m 5 -sS http://example.com || echo blocked
# iptables -vnL OUTPUT | head
# ip6tables -vnL OUTPUT | head
Поиск маршрута говорит, куда пойдёт пакет, а счётчики говорят, срабатывали ли когда-нибудь ваши правила. Правило с нулём пакетов после неудачной проверки ничего не защищает, и обычная причина — имя интерфейса, отличающееся от введённого. Проверяйте трафик, создаваемый роутером, явно, потому что проверка со стороны LAN не может задействовать OUTPUT.
Определите режим отказа заранее, до того как он понадобится. Fail-closed держит клиентов вне сети, пока туннель не вернётся; fail-open восстанавливает обычный доступ к WAN и обесценивает цель для трафика, который вам был важен. Маршрут blackhole в пул VPN с высокой метрикой — полезный второй слой для проектов fail-closed, поскольку он выживает, когда маршрут туннеля исчезает, вместо того чтобы падать обратно на маршрут по умолчанию.
Принимайте настройку только тогда, когда клиент в LAN, запрос, созданный роутером, и запрос IPv6 все терпят неудачу при опущенном туннеле, и все три снова работают, как только он поднимается.