Гости в сети libvirt по умолчанию прекрасно разрешают имена, пока не поднимется интерфейс WireGuard, и тогда каждый запрос застревает в одном и том же месте. dnsmasq по-прежнему слушает на 192.168.122.1:53, у гостя по-прежнему этот адрес в качестве резолвера, и запрос по-прежнему приходит. Изменилось то, что собственный вышестоящий запрос хоста или ответ на него теперь отбрасывается правилом, которое вычисляется раньше любой цепочки фильтрации, которую вы бы вздумали править. Выход — метка, поставленная достаточно рано, на соединении, которое наследует ответ.

Гости теряют DNS в тот момент, когда поднимается туннель

Подтвердите картину, прежде чем что-то менять. Опустите туннель, выполните dig @192.168.122.1 example.com с гостя и посмотрите, что он отвечает. Поднимите туннель, повторите и посмотрите, что он уходит в тайм-аут. Эта пара результатов одним шагом исключает сломанную сеть libvirt, неверный адрес резолвера и мёртвый dnsmasq.

Затем посмотрите со стороны хоста. Если dnsmasq пишет в журнал запрос и его вышестоящую пересылку, пакет дошёл и сбой на исходящем направлении. Если dnsmasq вообще ничего не пишет, запрос гостя отбрасывается на входящем направлении, и исключение должно стоять на другом хуке, не на том, который помечает эта статья.

Почему правило accept в более поздней цепочке никогда не срабатывает

VPN-межсетевой экран на метках решает, глядя на метку пакета или метку соединения, а не на ваш адрес источника. Всё, что приходит на физический интерфейс без ожидаемой метки, отклоняется, и это отклонение происходит в наборе правил, которому принадлежит проверка метки. ip saddr 192.168.122.0/24 accept строка, добавленная в какую-нибудь более позднюю цепочку, для этого пакета просто никогда не рассматривается, поэтому очевидное правило выглядит верным и ничего не меняет.

Расширять его, пока что-нибудь не заработает, — вторая ловушка. Принятие всей подсети libvirt на входном пути хоста открывает гостям все службы хоста, тогда как реально нужен один порт.

Механизм, который делает узкое исключение возможным, — отслеживание соединений. Когда вы задаёте ct mark на пакете, значение сохраняется в записи conntrack, а не только на пакете, поэтому каждый пакет этого соединения потом его несёт, включая ответ от хоста обратно гостю. В этом весь трюк: пометьте запрос один раз, и обратный путь уже помечен.

Пометьте запрос раньше, чем его увидит таблица VPN

Поместите исключение в отдельную таблицу, чтобы поздний читатель нашёл его, не разбирая правила VPN:

table inet allowedLocal {
  chain guest_dns {
    type filter hook input priority mangle; policy accept;
    ip saddr 192.168.122.0/24 meta l4proto { tcp, udp } \
      th dport 53 ct mark set 0x00000f41
  }
}

priority mangle отрицательный, что ставит эту цепочку впереди обычных цепочек фильтрации, которые пакет VPN устанавливает с приоритетом ноль. Именно порядок хуков делает правило действенным; то же правило в цепочке, идущей после цепочки VPN, — лишь украшение.

Сам номер метки не переносим. Возьмите значение, которое установленные правила VPN принимают для своего собственного трафика, прочитайте его из работающего набора правил и используйте именно его. Неверная метка даёт правило, которое загружается чисто, совпадает с каждым запросом и ничего не пропускает.

Держите дыру шириной в порт 53

Как написано, это разрешает DNS из подсети libvirt к хосту по TCP или UDP на порт 53 и ничего больше. Оно не даёт гостям выйти в интернет в обход туннеля и не даёт хосту открывать соединения в сторону гостей. Если гостю позже понадобится другая служба хоста, добавьте отдельное правило с именем этого порта назначения вместо ослабления этого.

Будьте честны в том, чем вы торгуете. Гость, который может дотянуться до резолвера, может и протолкнуть произвольные данные через DNS-запросы, и это исключение — намеренная брешь в границе VPN, а не заявление о том, что локальная сеть заслуживает доверия. Ограничение источника до 192.168.122.0/24 держит брешь подальше от любой другой локальной подсети, а подсчёт запросов от этого источника дёшев, если угроза для вас важна.

Сделайте так, чтобы это пережило перезагрузку, и докажите обе половины

Правило, набранное в командной строке, исчезает при следующей загрузке, а правило, загруженное уже после того, как интерфейс VPN пропускает трафик, проигрывает гонку при первом соединении. Поместите таблицу в постоянную конфигурацию nftables хоста или в юнит, упорядоченный раньше службы VPN, и проверьте порядок, а не полагайтесь на него.

Проверка требует трёх отдельных ответов. Подтвердите, что цепочка загружена и упорядочена первой, подтвердите, что запись conntrack показывает метку в обоих направлениях, и подтвердите, что запрос из гостя возвращает адрес. Затем подтвердите вторую половину: обычный веб-трафик гостя к публичному хосту по-прежнему не проходит, пока туннель опущен или блокирует. Если DNS работает, а всё остальное утекает, ваше правило шире приведённого выше.

Если запросы всё ещё не проходят после установки метки, сдержите желание расширить источник. Проверьте, куда перенаправляет dnsmasq, указывает ли резолвер гостя на 192.168.122.1 или на публичный адрес, который кто-то вписал, и доходит ли запрос вообще до хоста. Пометка пропускает пакет и ничего больше, поэтому плохой вышестоящий сервер или сломанная виртуальная сеть останутся сломанными, сколько бы правил вы ни добавили.

Похожие статьи