Два сайта, одна подсеть, один концентратор WireGuard: заставьте это работать
Обе сети LAN находятся в 10.10.7.0/24, поэтому концентратор не может выбрать сайт по назначению. Дайте каждой службе псевдоним и переводите на её шлюзе.
Оба сайта используют один и тот же диапазон LAN, и дизайн ломает не само перекрытие. Концентратор должен выбирать сайт только по адресу назначения, а два одинаковых назначения не дают ему ничего для выбора. Перенумерация работающего офиса ради туннеля обычно худшая сделка, чем выдача каждой опубликованной службе отдельного адреса, который туннель может пронести. Дизайн ниже публикует одну службу HTTPS на сайт и ничего больше.
Почему перекрытие не является настоящей проблемой
WireGuard пересылает по назначению: AllowedIPs пира определяют, какой пир получит пакет. Два пира не могут одновременно объявлять 10.10.7.0/24, если один из них не выигрывает каждый поиск. Это свойство таблицы пиров, а не туннеля, поэтому решение здесь — план адресов, а не трюк с маршрутизацией.
План в том, чтобы вообще перестать объявлять LAN. Вместо этого объявите псевдоним для каждой службы, один адрес на сайт, и пусть шлюз каждого сайта переводит этот псевдоним в своё реальное приложение.
Пользователь маршрутизирует два уникальных адреса, никогда не видит неоднозначный префикс, и ни в одном из двух офисов ничего не меняется. Это также честно обозначает границу: вы публикуете одну службу HTTPS с каждого сайта, а не объединяете две сети. Следующие правила остаются небольшими, потому что утверждение узкое. Более широкое утверждение потребовало бы более широких правил, и именно так эти дизайны усложняются.
Дайте каждому сайту псевдоним, который пользователь может маршрутизировать
Выберите адреса из диапазона, который не конфликтует ни с чем на машине пользователя, концентраторе или в любой из двух сетей LAN. Здесь концентратор — 10.66.0.1, пользователь — 10.66.0.10, шлюзы — 10.66.0.21 и 10.66.0.22, а два псевдонима — 10.77.1.20 и 10.77.2.20. Концентратор публикует каждый псевдоним через пира, которому он принадлежит, поэтому исходящий пакет, адресованный одному псевдониму, может уйти только через соответствующий сайт:
[Interface]
Address = 10.66.0.1/32
ListenPort = 51820
PrivateKey = HUB_KEY
[Peer]
PublicKey = USER_PUBLIC_KEY
AllowedIPs = 10.66.0.10/32
[Peer]
PublicKey = SITE_A_PUBLIC_KEY
AllowedIPs = 10.66.0.21/32, 10.77.1.20/32
[Peer]
PublicKey = SITE_B_PUBLIC_KEY
AllowedIPs = 10.66.0.22/32, 10.77.2.20/32
Стороне пользователя нужны только два псевдонима, что оставляет конфигурацию клиента свободной от обоих префиксов LAN. Ничто в этой конфигурации не упоминает ни одну из настоящих сетей LAN:
[Interface]
Address = 10.66.0.10/32
PrivateKey = USER_KEY
[Peer]
PublicKey = HUB_PUBLIC_KEY
Endpoint = hub.example.net:51820
AllowedIPs = 10.77.1.20/32, 10.77.2.20/32
PersistentKeepalive = 25
Каждый шлюз объявляет концентратор и источник пользователя, ничего больше, поэтому сайт никогда не узнаёт LAN другого сайта. Keepalive поддерживает сопоставление NAT на восходящем канале сайта; он не даёт разрешения на трафик, и межсетевой экран по-прежнему решает, что допускается. Копировать конфигурацию одного шлюза в другой можно, если закрытый ключ, адрес интерфейса и псевдоним все меняются.
Что концентратор должен разрешать между двумя своими пирами
Концентратор пересылает пакеты между двумя своими пирами, а это решение в цепочке forward, а не входное решение. Включите пересылку IPv4 обычным механизмом платформы, затем разрешите ровно те пары, которые нужны дизайну.
Правило ниже разрешает пользователю один псевдоним и один порт и разрешает обратный трафик через отслеживание соединений. Оно отбрасывает всё остальное на пути forward, и в этом суть:
nft add table inet hub_transit
nft add chain inet hub_transit forward '{ type filter hook forward priority 0 ; policy drop ; }'
nft add rule inet hub_transit forward ct state established,related accept
nft add rule inet hub_transit forward iifname wg0 oifname wg0 tcp dport 443 ip saddr 10.66.0.10 ip daddr 10.77.1.20 accept
Два момента стоит назвать, потому что они вызывают тихие отказы. Accept в одной базовой цепочке не отменяет drop в другой, поэтому объедините это с существующей политикой, а не загружайте рядом. И адрес источника идентифицирует устройство, а не человека; держите аутентификацию приложения отдельно от допуска в сеть.
Где псевдоним становится реальным адресом
Сайт A получает запрос к 10.77.1.20, переписывает назначение на реальное приложение и переписывает источник на свой адрес LAN, чтобы ответ мог вернуться без маршрута в туннель. Выполнение обоих переводов в одном месте оставляет остальную сеть в неведении о псевдониме. Правила ниже — весь механизм:
nft add table ip site_a_nat
nft add chain ip site_a_nat pre '{ type nat hook prerouting priority -100 ; policy accept ; }'
nft add chain ip site_a_nat post '{ type nat hook postrouting priority 100 ; policy accept ; }'
nft add rule ip site_a_nat pre iifname wg0 tcp dport 443 ip saddr 10.66.0.10 ip daddr 10.77.1.20 dnat to 10.10.7.20:443
nft add rule ip site_a_nat post iifname wg0 oifname lan0 tcp dport 443 ip saddr 10.66.0.10 ip daddr 10.10.7.20 snat to 10.10.7.2
Затем политика forward сайта A должна разрешить переведённый поток из интерфейса туннеля в интерфейс LAN, плюс обратное направление. Хук forward видит назначение после перевода назначения и до перезаписи источника, поэтому правило называет адрес реального приложения. Читайте эти два хука в таком порядке, когда правило, кажется, ничему не соответствует. Правило forward, написанное против псевдонима, никогда не сможет совпасть, потому что псевдонима на этом хуке уже нет.
Сайт B повторяет это со своим псевдонимом и своим ключом. Он не должен повторно использовать закрытый ключ сайта A, потому что два пира, разделяющие одну личность, — это ошибка конфигурации, которую трудно увидеть и легко создать копированием файла. Держите оба документа шлюзов идентичными, кроме значений, которые должны отличаться, чтобы разница бросалась в глаза при проверке. Скопированный файл — обычный источник этой неисправности.
Перезапись источника имеет цену. Приложение видит шлюз как своего клиента, поэтому его журналы больше не показывают, какой пользователь подключился. Сохраните аудиторский след шлюза и не ослабляйте вход в приложение, чтобы компенсировать сетевую личность, которую убрал перевод.
Как доказать, что оба опубликованных имени отвечают
Дайте каждому псевдониму имя DNS и настройте приложение и сертификат для этого имени, потому что перевод не переписывает перенаправления или встроенные абсолютные URL. Используйте одно имя на сайт, чтобы несоответствие сертификата никогда не было принято за сбой маршрутизации.
Проверяйте с реального клиента, подтвердите, какой сайт ответил, и проверьте также неразрешённый порт. После любого изменения NAT используйте новые соединения, так как установленное состояние сохраняет старое сопоставление:
wg show wg0 allowed-ips
sudo nft list ruleset
curl -sS -o /dev/null -w "%{http_code}\n" https://site-a.example.net/
Пустой или неожиданный псевдоним в первой команде означает, что выбор пира неверен ещё до того, как любое правило межсетевого экрана станет важным. Если псевдоним верен, а имя всё равно не работает, проблема переместилась в DNS и сертификаты. Как только оба имени отвечают, а третий порт остаётся закрытым, дизайн делает то, что заявляет: две узко опубликованные службы, а не две объединённые сети LAN.