Обновить

WireGuard не хранит AllowedIPs у пира

Уровень сложностиСложный
Время на прочтение14 мин
Охват и читатели9.4K
Всего голосов 7: ↑4 и ↓3+2
Комментарии149

Комментарии 149

А какого черта (извините) вы пиру на сервере указываете не конкретный адрес (/32), а целую сеть (/24)?

AllowedIPs на клиенте и сервере выполняют несколько разные функции.
ЕМНИП, сам wg даже будет сыпать warning, если в секции [peer] на сервере указывать что-то кроме /32

Один вопрос из памяти, без консоли: у вас в конфигах есть два пира с одинаковым AllowedIPs

На сервере - нет.

Спасибо за честную критику!)

но давай разбираться

Про /24 у пира на сервере: само по себе это не ошибка— обычный паттерн "пир = шлюз в сеть, а не хост" (site-to-site, филиал). Даже в каноническом примере из wg-quick(8) "for use on a server" у пиров стоят подсети наравне с /32: AllowedIPs = 10.192.122.4/32, 192.168.0.0/16. В статье есть и обратный пример — у OPNsense три пира на одном /56 давали (none) у двух из трёх, а с разными /62, /63,/64 в trie остаются все три. Подсеть на пира — нормально, пока не совпадает буквально с чужой.

/24 в моём примере выбран специально чтоб показать на одной паре сразу два сценария: exact match (A: /24, B: тот же /24 -> строка A пропадает) и longest-prefix-match (A: /24, B: /32 внутри него -> остаются оба, трафик расходится по длине префикса). На двух /32 с разными адресами пересечения бы просто не возникло, показывать было бы нечего.

Про warning: он есть, но не тот. wg setconf/wg-quick действительно ругаются — в config.c: "Warning: AllowedIP has nonzero host part: %s/%s", — только проверка там не про длину маски и не про роль интерфейса, а про то, не остались ли в хост-части ненулевые биты, то есть согласован ли адрес с собственной маской. 10.13.13.2/24 — warning, 10.100.0.0/24 — нет: у /32 хост-части нет по определению, а .0/24 — корректный адрес сети. Живой пример именно этой путаницы — issue в docker-wireguard: пять пиров получили 10.13.13.2/24…10.13.13.6/24, warning на каждого, а после маскирования все пять — один и тот же 10.13.13.0/24; wg show закономерно показывает (none) у всех, кроме последнего применённого. Это тот же механизм, что в статье, просто пойманный не мной. А между пирами config.c по-прежнему ничего не сравнивает — grep по overlap/duplicate/conflict/collid в tools пуст, роль тут ни при чём.

Про "разные функции": согласен по сути, не по механизму. Роли "клиент/сервер" в коде нет: один и тот же lookup_dst на передаче и lookup_src на приёме отрабатывает одинаково по обе стороны туннеля (receive.c:404-409, device.c:153-155). Разная не функция, а конвенция поверх неё: на хабе с несколькими пирами AllowedIPs держат узким — от этого зависит и обратная маршрутизация, и изоляция пиров друг от друга (чужой src дропается в rx_frame_errors); на споке пир обычно один, изолировать не от кого. Кейс с 0.0.0.0/0 у каждого клиента на сервере из статьи (OpenWrt-тред) — ровно та ошибка, о которой вы предупреждаете, только в чистом виде.

изоляция пиров друг от друга

вот ваша ошибка. Нет никакой изоляции. Приведите конфиг к нормальному виду (с /32 адресами пиров) и убедитесь, что связь между пирами никуда не пропала.

пингуем всех!
пингуем всех!

Если нужна изоляция клиентов, то фаерволл в помощь:
nft add rule inet filter forward iifname "wg0" oifname "wg0" drop

Здесь вы правы, а я нет

Моё изоляция пиров — неудачное выражение, и в том виде, как я его написал, утверждение получается неверное. Связность A <-> B через хаб определяют форвардинг и правила на нём — ровно ваш nft … drop, — а не AllowedIPs. С /32 у каждого пира пакет от A с src 10.100.0.2 на dst 10.100.0.3 проходит проверку источника, отдаётся в стек, форвардится обратно в wg0 и уходит к B: lookup_dst находит его по /32. Пингуется, да.

Что /32 на хабе действительно даёт —это не изоляция, а привязка адреса к ключу: A не может прислать пакет с чужим src. wg_allowedips_lookup_src() вернёт B, routed_peer != peer, и пакет умирает уже после расшифровки, в rx_frame_errors (receive.c:404-420). Плюс однозначный обратный путь и невозможность забрать чужой префикс. Получается, это валидация источника, а не запрет связи. называть это изоляцией не следовало.

В статье, кстати, этого слова нет — оно моё, из комментария. Поправку принял, спасибо. + за то, что объясняете, мне это важно.

О сколько нам открытий чудных
Готовят просвещенья дух,
И опыт, сын ошибок трудных,
И гений, парадоксов друг,
И случай, бог изобретатель…

И случай, бог изобретатель

вот про него, собственно, и текст. Руками два пира на один префикс никто не прописывает, оно получается само: шаблоном, панелью, ротацией ключа.

За правку про изоляцию спасибо, она по делу.

шаблоном, панелью

странные панели, однако)

Самые обычные, к сожалению)

Механизм везде один

форма подставляет адрес клиента вместе с маской интерфейса — 10.13.13.2/24. Человек читает это как "адрес в подсети /24", wg читает как префикс 10.13.13.0/24, и он один на всех.

Случаи, все из статьи. linuxserver/docker-wireguard: пять пиров с 10.13.13.2/24 … 10.13.13.6/24, warning на каждого, в ядре один префикс на всех (https://github.com/linuxserver/docker-wireguard/issues/349). OPNsense: три пира на одном fd00:1234:5678::/56, у двух (none) (https://github.com/opnsense/core/issues/9007). VyOS: «allowed-ips is overwritten», заведён как баг, закрыт как Invalid — потому что это не VyOS (https://vyos.dev/T2735). Тред на форуме OpenWrt: 0.0.0.0/0 каждому телефону, каждый новый гасит предыдущий. netbird в июле этого года чинил refcount, ключованный только по префиксу (https://github.com/netbirdio/netbird/pull/6799).

Ни в одном из них человек не прописывал два одинаковых префикса руками.

Механизм везде один

Юзал wireguard-ui, 3x-ui - ни одна из них не ставила для пиров /24

Таблица, по которой принимается решение, однозначная, в этом всё и дело. После перезаписи в trie не два узла на 10.100.0.2, а один, и он смотрит на B. Ни при вставке, ни при поиске развилки не возникает: ядро точно знает, кому отдавать. Собственно, будь узлов два, префикс показывался бы у обоих пиров — так себя ведёт BoringTun, где у пира лежит своя копия списка. В ядре у A стоит (none) именно потому, что узел один и он ушёл.

Неоднозначно не состояние роутинга, а намерение. два конфига претендуют на один адрес. Ядро снимает эту неоднозначность мгновенно и молча, а инструмент, который мог бы заметить её до применения, в неё не смотрит — ни wg, ни wg-quick пересечения не проверяют.

Простите, я совсем не понял, зачем нужно на два одинаковых peer-а один и тот же net+mask. Как wg поймёт, куда (в какой peer) нужно слать пакет, если у двух (или более) — 10.100.0.0/24? Обычная практика — начиная с бо́льших mask (/32 => /0) проверяется net: если совпало, отправляем туда. Два одинаковых net+mask не позволяют выяснить однозначно destination.

Для каких случаев это (один и тот же net+mask на разные peer-ы) необходимо и как предполагается такая сетевая работа?

Автор просто недопонял принцип работы wireguard и углубился в дебри нейрослопа)
* надеюсь issue еще не успел завести)

Про изоляцию поправку принял, ответил выше. Спасибо

На тезис это не влияет, и его легко проверить. AllowedIPs лежат не у пира, а в одной на всё устройство trie, и второй пир с тем же префиксом перевешивает узел на себя, молча вынимая его у первого.

К /24 это отношения не имеет. На конфиге из одних /32 всё то же самое, просто пересечься двум /32 можно только буквально одним адресом, то есть при ротации ключа или перевыпуске клиента:

wg set wg0 peer $A allowed-ips 10.100.0.2/32

wg set wg0 peer $B allowed-ips 10.100.0.2/32;

echo rc=$? wg show wg0 allowed-ips

rc=0, в dmesg пусто, у A — (none). Минута на проверку. Если у вас получится иначе, покажите вывод — поправлю статью. Буду благодарен если объясните почему не прав

wg set wg0 peer $A allowed-ips 10.100.0.2/32
wg set wg0 peer $B allowed-ips 10.100.0.2/32;

Допустим, пришел пакет с dst 10.100.0.2 Кому отправим? Пиру А или пиру Б?

Пиру B. Последнему записавшему, детерминированно, я это писал двумя комментариями выше и не спорю.

Интереснее вторая половина, про которую вопрос обычно не задают: а что будет с пакетом ОТ A, с src 10.100.0.2?) Он дойдёт, аутентифицируется, расшифруется —и умрёт уже после этого, потому что lookup_src вернёт B, а не A (receive.c:404-409). Счётчик, в который он ляжет, называется rx_frame_errors: кадровая ошибка на интерфейсе, у которого нет ни кадров, ни физического уровня.

Поэтому со стороны сервера A выглядит полностью живым — endpoint на месте, handshake свежий, байты от него приходят и считаются, — а в его конфиге по-прежнему написано 10.100.0.2. На "кому отправим" ответ в одну строку. На "почему у клиента A нет сети при правильном конфиге и свежем handshake" ответа не даёт ни один инструмент, и вот об этом статья.

"почему у клиента A нет сети при правильном конфиге и свежем handshake"

Потому что у вас неоднозначная таблица роутинга. Про модель OSI надеюсь не надо пояснять?

Таблица как раз однозначная, в этом всё и дело. После перезаписи в trie не два узла на 10.100.0.2, а один, и он смотрит на B. Ни при вставке, ни при поиске неоднозначности нет, ядро прекрасно знает, кому отдавать.

Неоднозначно не состояние роутинга, а намерение. два конфига претендуют на один адрес. Ядро снимает эту неоднозначность мгновенно и молча, а инструмент, который мог бы заметить её до применения, в неё не смотрит — ни wg, ни wg-quick пересечения не проверяют.

Про OSI пояснять не надо, она просто не отвечает на вопрос. Криптоключевая маршрутизация не L3: пакет отбрасывается не по таблице маршрутов, а по несовпадению src с ключом, которым он подписан, и уже после расшифровки, пройдя весь путь целиком.

И диагноз неоднозначная таблица вы ставите, уже зная механизм. У человека на входе есть только "у половины клиентов нет сети", правильный конфиг на диске и свежий handshake. Расстояние между этими двумя состояниями и есть статья.

И диагноз неоднозначная таблица вы ставите, уже зная механизм.

Первым же сообщением я указал на ерунду в конфиге, даже не вникая в ваши раскопки в сорцах wg.

У человека на входе есть только "у половины клиентов нет сети", правильный конфиг на диске и свежий handshake.

Человек занимает какую-то IT-должность? Уволить за профнепригодность. Конфиг - неправильный.

Конфиг неправильный— тут спора нет и не было ни разу.

Первым сообщением вы указали, что у пира на сервере /24 вместо /32. Это справедливое замечание к примеру, но не диагноз. на двух одинаковых /32 всё ломается точно так же, мы это выше уже разобрали. Механизм —перевешивание узла при совпадении длины префикса — из "поставьте /32" не выводится.

Про уволить. Наступили на это: репортёр в OPNsense, который в итоге решил, что сам напутал в настройках, и закрыл свой issue; человек, заведший баг в VyOS; разработчики netbird, чинившие ровно этот механизм в продакшн-mesh-VPN 28 июля этого года — refcount был ключован только по префиксу. Cilium и Calico генерируют AllowedIPs списком на узел, и на wg show в кластере не смотрит никто. Это не люди с неправильным конфигом, это люди, которые пишут инструменты.

"Так делать не надо" — верно) и мне в соседней ветке справедливо указали, что это должно стоять в начале статьи, а не в выводах. Спасибо человеку. Только само по себе оно не отвечает на вопрос, почему этого не замечают месяцами.

на двух одинаковых /32

Не может быть два одинаковых /32

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

# ip link add wg-test type wireguard
# A=$(wg genkey | wg pubkey); B=$(wg genkey | wg pubkey)
# wg set wg-test peer $A allowed-ips 10.100.0.2/32
# wg set wg-test peer $B allowed-ips 10.100.0.2/32; echo "rc=$?"
rc=0
# wg show wg-test allowed-ips
2K/oFVzAT40/R1fLGNABLbGRUh/2K8n7pRvPTg5cuyU=    (none)
IgOtO/8kgYYd/YIRMe6advAtthVAeXDfYZn5NCW/Cjg=    10.100.0.2/32

Ядро приняло вторую запись, вернуло 0 и ничего не сказало. У первого пира 10.100.0.2/32 больше нет.

Утверждение стоит разделить надвое. как "так не должно быть" — оно верное, и статья с ним не спорит ни в одной строке. Как "так не бывает" — выше консоль. Между не должно быть и ничто не мешает вся статья и помещается.в конфиге на диске у первого пира по-прежнему написано 10.100.0.2/32, в ядре у него (none), и о расхождении не сообщает никто.

да нельзя разным пирам назначать один и тот же ip!!! Ну что тут может быть неясно??? Ну напишите тогда в конфиг груб что-то типа "ололо, грузи мне линух", и потом недоумевайте - а чо оно не грузится?

Согласен, нельзя. Статья не про то, можно ли, а про то, что будет, если это всё же произошло, и как об этом узнать.

Это не люди с неправильным конфигом, это люди, которые пишут инструменты.

Свой мозг в любом случае надо включать.

Криптоключевая маршрутизация не L3: пакет отбрасывается не по таблице маршрутов, а по несовпадению src с ключом, которым он подписан, и уже после расшифровки, пройдя весь путь целиком.

Садись, два. Шифрование на прикладном уровне. Маршрутизация на сетевом уровне. WG только шифрует пакеты, маршрутизацией занимается ядро.

wg0 — интерфейс без канального уровня вообще. В wg_setup() стоит dev->type = ARPHRD_NONE, dev->addr_len = 0, dev->hard_header_len = 0, флаги IFF_POINTOPOINT | IFF_NOARP. На живом модуле это выглядит так:

# ip link add wg-test type wireguard
3: wg-test: <POINTOPOINT,NOARP> mtu 1420 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/none
# cat /sys/class/net/wg-test/type
65534

link/none, флаг NOARP, и 65534 — это ARPHRD_NONE числом, уже без участия iproute2 в интерпретации. Ни MAC-адреса, ни ARP, ни кадра. Шифровать на L2 там нечего, потому что L2 там нет — на этом построен и эпизод статьи про rx_frame_errors.

Внутрь туннеля едет тоже не кадр. После расшифровки receive.c смотрит версию IP, и если это не IPv4 и не IPv6 — пакет уходит в dishonest_packet_type. Ethernet внутри WireGuard не ходит принципиально, этим он и отличается от tap-режима OpenVPN или от VXLAN. Снаружи всё это лежит в UDP, то есть в payload L4.

А здесь поправлю сам себя: "криптоключевая маршрутизация не L3" я написал неудачно. Точнее будет — не по таблице маршрутов ядра, а по отдельной привязке префикс -> ключ; уровень при этом ровно L3. src и dst берутся из IP-заголовка и сравниваются с префиксами тем же longest-prefix match. Необычен там не уровень, а то, что роль next-hop играет публичный ключ.

Но L5 из этого всё равно не получается. В самой OSI сетевой уровень определён через логическую адресацию и выбор маршрута, а сеансовый — через управление диалогом между приложениями. Выбор пира по dst-адресу подходит под определение L3 буквально. И писал я не «модель неправильная», а что она тут не помогает: как ни разложи туннель по уровням, на вопрос, почему администратор не узнаёт о перевешивании префикса, модель не отвечает.

что-то я уже устал вам объяснять базовые вещи.
allowed ips: 10.100.0.0/24 #На сервере - НЕПРАВИЛЬНО!

wg set wg0 peer $A allowed-ips 10.100.0.2/32
wg set wg0 peer $B allowed-ips 10.100.0.2/32

# по-отдельности нормально, вместе - НЕПРАВИЛЬНО!

Так с этим я и не спорил ни в одном комментарии. Оба конфига неправильные, это я писал прямым текстом не раз.

Разница между нами ровно одна: вы считаете, что на этом разговор кончается, а я — что он с этого начинается)) Ядро такой конфиг принимает, возвращает 0 и не говорит об этом никому, а увидеть последствия можно только в wg show, куда смотрят последним.

Спасибо за ветку, без иронии. Поправку про изоляцию принял, про панели тоже. обе были по делу.

Шифрование на прикладном уровне.

Если под «прикладным» уровнем понимаете Application (L7), то у меня есть некотороые сомнения в этом.

Например, тот же TLS — это Presentation (L6), а не Application, WireGuard же явно ниже. И шифрованием занимается не userspace («application»), а модуль ядра.

-=-

WG только шифрует пакеты, маршрутизацией занимается ядро.

Но wg должен определить destination-peer, потому что у ядра этой информации нет.

На уровне OS действует маршрутизация до попадания в единый wireguard-интерфейс (их может быть много; для простоты возьмём один с несколькими peer-ами), и уже в рамках wireguard-интерфейса определение peer-а определяется по AllowedIps.

Вам рассказывают про «криптоключевую маршрутизацию» (отличающуюся от маршрутизации на Network layer, который как раз L3, о чём Вам сразу сообщили) — как для пакета, уже попавшего из OS в wireguard-интерфейс, найти peer, которому его (предварительно зашифровав) отправить.

А без нейронок уже и мысли свои никак не сформулировать?

"криптоключевая маршрутизация" - а неплохое название для рок-группы)

Вообще то сетевой уровень (маршрутизация) ничего не шифрует. Там одна задача - доставить пакет от точки А в точку Б. Содержимое пакета вообще ни на что не влияет. Дипсик небось такие перлы выдаёт?

Я не пользуюсь нейросетями для написания текстов, в том числе комментариев.

Жаль, что грамотный текст Вы априори воспринимаете как написанный LLM.

тире и ёлочки братиш)

С помощью XCompose[1] я уже много лет очень просто набираю много типографских символов, которых нет напрямую на клавиатуре[2], равно как и некоторые часто используемые последовательности символов и некоторые emoji. Грамотная речь и типографские символы не являются признаком LLM.

-=-

братиш

И ещё: я Вам точно не брат.

-=-

[1] https://linux.die.net/man/3/xcompose (и вообще можно найти в поисковиках много разного на эту тему)

[2] В качестве примера — прямо с клавиатуры могу набрать как «x² = x × x», «H₂O» и прочие, в том числе ударе́ния, если они вдруг нужны.

серьёзно? Я вообще дизайнер. И все кейкоды знаю, и на лин и на вин, только вот почему-то их не применяю в так сказать «обычной» переписке. Странно, ага?

Я не знаю кодов символов. Я просто набираю на клавиатуре так, как должен выглядеть хороший текст: и по содержанию, и по виду.

Если Вы этого не делаете (по любой возможной причине) — это Ваше дело.

Но если я привык составлять текст грамотно и пользовался этим задолго до того, как появились LLM, то странно объявлять мои сообщения составленным LLM только на этом основании.

я вполне вижу что ваш “текст„ создан нейронкой

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

Если хотите, давайте продолжим общение в привычном ключе. Какую тему или вопрос вы бы хотели обсудить дальше?

Раз уж Вы изменили своё сообщение, на дополнение отвечу отдельным сообщением.

  1. Да, маршрутизация OS ничего не шифрует.

  2. Нет, у маршрутизации OS нет задачи «доставить пакет от точки А в точку Б» [1].

  3. Нет, содержимое[2] пакета влияет на маршрутизацию OS, потому что, как минимум, содержит адрес назначения. Возможно, Вы имееете в виду, что payload не влияет на маршрутизацию в OS — в таком виде соглашусь.

-=-

Но в исходном сообщении речь была про маршрутизацию между wireguard-интерфейсом (уже после маршрутизации OS, потому что она уже определила, что пакет будет отправлен через этот интерфейс) и peer-ом. L3-маршрутизация (в OS) к этому никакого отношения не имеет.

-=-

[1] Задача маршрутизации в OS — определить для текущего пакета интерфейс, через который он будет отправлен, не более того. Маршрутизация OS [кроме специальных случаев] не знает, откуда пакет был отправлен. Маршрутизация OS не знает, что будет с пакетом после отправки в интерфейс (на следующем hop-е). Тем более у маршрутизации OS нет возможности доставки «в точку Б».

[2] «A packet consists of control information and user data» и «network packets requires two network addresses, … and the destination address of the receiving host»

https://en.wikipedia.org/wiki/Network_packet#Contents

Да пофиг) У меня всё работает) И пинг с телефона, и на телефон)

Ни для каких, так делать не надо. Статья не про то, зачем так делают, а про то, что происходит, когда так вышло случайно, и почему это потом трудно опознать.

Алгоритм вы описали правильно, в коде ровно он. спуск по trie, found перезаписывается на каждом уровне, побеждает самый длинный совпавший префикс (allowedips.c:116-120). Обычный longest-prefix match.

Тонкость в другом: при одинаковой длине выбирать не из чего, потому что второго узла и не появляется. node_placement() выставляет exact только когда parent->cidr == cidr, и тогда вставка не создаёт узел, а перевешивает существующий:

rcu_assign_pointer(node->peer, peer);
list_move_tail(&node->peer_list, &peer->allowedips_list);
return 0;

это allowedips.c:199-203

Узел уходит последнему записавшему, а из списка первого пира его вынимает list_move_tail. Поэтому у первого в wg show становится (none), хотя в файле на диске префикс никуда не делся. И return 0, то есть wg set отработал успешно и dmesg пустой.

Случайно это получается обычно тремя способами. Самый частый — 0.0.0.0/0 прописан каждому клиенту в конфиге сервера: каждый новый телефон забирает префикс себе, предыдущий замолкает. Второй — не обнулена хост-часть: пять пиров с 10.13.13.2/24 … 10.13.13.6/24, wg на каждого честно печатает Warning: AllowedIP has nonzero host part, а в ядро уходят пять одинаковых 10.13.13.0/24 (https://github.com/linuxserver/docker-wireguard/issues/349). Третий - ротация ключа: новый пир с тем же AllowedIPs забирает префикс сразу, ещё до хендшейка.

А на вопрос буквально: никак не поймёт и никому не скажет. insert возвращает 0, проверок на пересечения нет ни в ядре, ни в wg, ни в wg-quick.

0.0.0.0/0 прописан каждому клиенту

Нет. это клиентские роуты. Вписываются тоже на клиенте.

Так и есть, и в статье написано ровно это: место 0.0.0.0/0 в конфиге клиента, а не сервера. Я не говорю, что так надо. Я говорю, что так делают.

Механизм ошибки очень человеческий: в конфиге клиента секция [Peer] описывает сервер и содержит AllowedIPs=0.0.0.0/0. Дальше её зеркалят на сервер как есть, потому что выглядит симметрично, либо заполняют поле Allowed IPs в веб-морде тем же значением, которое только что видели у клиента.

Чем кончается, видно по треду на форуме OpenWrt, он так и называется — "only latest configured peer works": https://forum.openwrt.org/t/troubles-with-wireguard-only-latest-configured-peer-works-solved/183422 дословно:

"whenever I add a mobile phone as a peer, the phones added previously are no longer able to connect"

Каждый новый телефон забирает 0.0.0.0/0 себе, предыдущий замолкает

Ну и вопрос не в том, что кто-то не знает, где место у 0.0.0.0/0. Вопрос в том, что за эту ошибку ничего не ругается: ни wg, ни wg-quick, ни dmesg.

Каждый новый телефон забирает 0.0.0.0/0 себе, предыдущий замолкает

Это уже за гранью непонимания
Это уже за гранью непонимания

Три "телефона", добавлены подряд, каждому 0.0.0.0/0:

# ip link add wg-test type wireguard
# P1=$(wg genkey|wg pubkey); P2=$(wg genkey|wg pubkey); P3=$(wg genkey|wg pubkey)
# for k in $P1 $P2 $P3; do wg set wg-test peer $k allowed-ips 0.0.0.0/0; echo "rc=$?"; done
rc=0
rc=0
rc=0
# wg show wg-test allowed-ips
etTtVHw4PGJ2vl19pTgSHcTou+7oVyhQXYjouM3qdRk=    (none)
+pg0ZEzTL3v1SG/L7CHjZlwQ9a4SOz35UmQio7GjQHc=    (none)
NiSaM97l9E21aQ54rlnybJDb0eHXXMDqI5UGBoJuQhw=    0.0.0.0/0

Три успешных wg set подряд, префикс остался у последнего, у первых двух — (none).

Механизм тот же самый, что двумя комментариями выше на /32, и от длины префикса он не зависит: 0.0.0.0/0 — это точно такое же exact match, просто с cidr = 0. Тот же node_placement(), та же перезапись указателя.

Тред на форуме OpenWrt, откуда взят пример, называется only latest configured peer works, а жалоба в нём дословно такая: "whenever I add a mobile phone as a peer, the phones added previously are no longer able to connect". https://forum.openwrt.org/t/troubles-with-wireguard-only-latest-configured-peer-works-solved/183422 Человек описал этот вывод за два года до статьи, своими словами и не открывая исходников.

В конфиг сервера 0.0.0.0/0 ? Ну извините, дальше я материться буду.

Так и я о том же — это неправильно. Только это не моя выдумка, а конфиг из треда по ссылке выше (вот он https://forum.openwrt.org/t/troubles-with-wireguard-only-latest-configured-peer-works-solved/183422), где человек именно так и сделал.

Это клиентский конфиг, а не серверный.

Нет, серверный. Вот он из того треда, дословно:

config interface 'vpn'
        option proto 'wireguard'
        option private_key '...'
        option listen_port '...'
        list addresses '10.11.11.1/24'

config wireguard_vpn
        option description 'mobile1'
        list allowed_ips '0.0.0.0/0'
        list allowed_ips '10.0.0.0/8'

config wireguard_vpn
        option description 'mobile2'
        list allowed_ips '10.0.0.0/8'
        list allowed_ips '0.0.0.0/0'

private_key, listen_port и адрес 10.11.11.1/24 на интерфейсе — это хаб. Секции wireguard_vpn — телефоны в роли пиров, и у каждого 0.0.0.0/0 плюс 10.0.0.0/8. Два одинаковых префикса на двух пирах, ровно тот случай.

И принятое в треде решение ( https://forum.openwrt.org/t/troubles-with-wireguard-only-latest-configured-peer-works-solved/183422 ) — ровно то, о котором вы всё это время говорите: на роутере каждому пиру свой /32 (10.11.11.101/32, .102/32, .103/32), а 0.0.0.0/0 остаётся только в конфигах телефонов.

Так что расходимся мы не в том, как правильно, а в том, стоит ли писать о том, как делают неправильно.

За заборе х** написано, а там дрова...

10.0.0.0/8 вообще-то замечательно входит в 0.0.0.0/0
И если кто-то себе выдумал, что это серверный конфиг - я хочу ему бить по рукам

Входит, конечно — как множество адресов. Но в trie это два разных узла разной длины, и живут они порознь. Прогнал прямо сейчас, ядро 6.18.33.2-microsoft-standard-WSL2:

# 1. один пир(оба префикса сразу)
mobile1 0.0.0.0/0 10.0.0.0/8

# 2. /0 одному пиру,/8 другому
mobile1 0.0.0.0/0
mobile2 10.0.0.0/8

# 3. конфиг из того треда: у КАЖДОГО из трёх пиров оба префикса
mobile1 (none)
mobile2 (none)
mobile3 0.0.0.0/0 10.0.0.0/8

# 4. а тут возвращаем mobile1 только 0.0.0.0/0
mobile1 0.0.0.0/0
mobile2 (none)
mobile3 10.0.0.0/8

Четвёртый шаг и есть ответ. /0 переехал к mobile1, а /8 остался у mobile3 — поглощался бы, уехал бы вместе с ним. и в том конфиге столкновений было два, по одному на каждую длину префикса, последний пир забрал оба узла, первые два остались вообще без ничего.

Мобильная связь - впн - сервер - машина в локалке сервера
Мобильная связь - впн - сервер - машина в локалке сервера

Предвижу вопросы про маленький пинг. Мобильный и проводной провайдер у меня один и тот же. Сервер на проводном висит.

Так и должно быть, конфиг корректный. Спор был не о том, работает ли WireGuard, а о том, что видно, когда два пира претендуют на один префикс.

ничего не будет видно. У вас неоднозначный роутинг и wg об этом не знает - он на другом слое OSI.

Почти)видно ровно одну вещь — у обокраденного пира в wg show стоит (none). Больше ничего: код возврата 0, dmesg пуст, конфиг на диске не тронут. Так что "ничего не будет видно" и "wg об этом не знает" — это и есть тезис статьи, почти дословно. Здесь мы с вами наконец сошлись.

у обокраденного пира в wg show стоит (none)

Я никаких (none) никогда не видел. Wg юзал года четыре наверное.

И это согласуется. Проверил прямо сейчас, ядро 6.18.33.2-microsoft-standard-WSL2: (none) означает просто «у этого пира сейчас нет ни одного префикса», и прийти к этому можно тремя путями.

# 1. пир заведён вообще без allowed-ips
peerA   (none)

# 2. префикс выдан и очищен явно, allowed-ips ''
peerA   (none)

# 3. префикс отобран вторым пиром, wg set вернул 0
peerA   (none)
peerB   10.100.0.2/32

Первые два вы делаете руками и про них знаете. Третий это тот, о котором статья: его никто не делал специально и никто о нём не сообщил. Так что четыре года без (none) означают ровно то, что конфиги были правильные.

Заодно вылезло то, чего у меня про Linux в статье не было (Опять же спасибо за коммент — подчерпнул это для себя). Если удалить пира, который забрал префикс, первому он не возвращается:

# 4.peerB удалён
peerA   (none)

Узел просто исчезает с устройства. Чтобы оживить peerA, нужно заново применить ему тот же самый префикс, который и так написан у него в конфиге.

Ни для каких, так делать не надо.

Этого в статье вообще нет (как минимум, в начала; или я не нашёл).

-=-

Статья не про то, зачем так делают, а про то, что происходит, когда так вышло случайно,

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

-=-

и почему это потом трудно опознать.

Ну, не знаю… Два одинаковых AllowedIps в config-е и/или отсутствие нужного AllowedIps для неработающего peer-а явно должно намекать на то, что на peer не идёт трафик (идёт на другой, на котором есть этот net+mask), из-за чего есть проблема.

Кажется, для этого не нужно лезть в исходный код ядер (или их модулей).

-=-

Моё недовольство — в том, что статья начинает идти в дебри и разбор исходного кода ядер (модулей), при том, что в самом начале нет информации, что это «археологические раскопки» и просто не надо так делать с объяснением (с точки зрения формальной логики) почему это логическая ошибка на уровне построения сети + быстрое описание «как найти ошибку» по wg dump или разборе config-а grep/awk (нахождение одинаковых AllowedIps).

-=-

P.S.:

при одинаковой длине выбирать не из чего

Я спрашивал про логическую часть, а не про детали реализации: если бы net+mask оставался на обоих peer-ах — как бы предполагалась работа routing-а внутри wg и какие бы статьи были написаны про debug такого состояния?

По первым двум пунктам вы правы, принимаю. "Один префикс — один пир на всём интерфейсе" у меня стоит в выводах, то есть в самом конце, а должно стоять в начале. И жанр не объявлен: читатель сам догадывается, разбор это, багрепорт или предупреждение. Это редакторская ошибка, спорить не буду, надо будет поучиться лучше писвть, благодарен за такое

А про должно намекать поспорю — не логикой, а тем, как оно выглядело у людей. Автор issue в OPNsense закрыл его сам, решив, что напутал в настройках, механизм он так и не узнал. В VyOS завели как баг и закрыли как Invalid. На Server Fault ответ свёлся к "уберите подсети", без объяснения, почему у одних пиров пропадает, а у других нет. netbird чинил это в проде в июле этого года. Логика тут действительно несложная, беда в том, что до неё не доходят.

И grep по конфигу ловит не всё. Вот два способа на одном файле, где у пяти пиров стоит 10.13.13.2/24 … 10.13.13.6/24:

grep -h AllowedIPs wg0.conf | tr -d ’ ’ | sed ‘s/AllowedIPs=//’ | tr ‘,’ ‘\n’ | sort | uniq -d -> пусто, все строки разные

grep -hoP ‘AllowedIPs\s*=\s*\K.*’ wg0.conf | tr -d ’ ’ | tr ‘,’ ‘\n’ | python3 -c ‘import sys,ipaddress for l in sys.stdin: l = l.strip() if l: print(ipaddress.ip_network(l, strict=False))’ | sort | uniq -d -> 10.13.13.0/24

Вся разница в strict=False: он обнуляет хост-часть ровно так же, как это делает wg перед отправкой в ядро. Первый вариант — тот самый grep/awk, о котором вы пишете, и этот случай он не видит.

Со стороны ядра детектор короче:

wg show wg0 allowed-ips | grep ‘(none)’

Пир с (none) — это пир, у которого префикс забрали. Пустой вывод и есть проверка для CI.

Про P.S. Это не гипотетика, такая реализация существует. В BoringTun у пира лежит своя копия списка, и по коду UAPI отдаёт префикс обоим пирам сразу (peer.rs:25 -> api.rs:188); на настоящем типе из крейта это воспроизводится харнессом, сквозного прогона у меня нет, в статье это оговорено. Маршрутизация от такого хранения двойной не становится,пакет всё равно уходит в один туннель, выбор делает общая таблица. То есть "оставить у обоих"— не альтернативная семантика роутинга, а альтернативная семантика отображения, и она хуже. В ядре хотя бы (none) намекает, что что-то произошло; там не намекает ничто. RouterOS ведёт себя так же, и там сверх того удаление второго пира не возвращает префикс первому — нужен повторный set тем же самым значением. вот это уже замерено, на CHR 7.20.2.

А про должно намекать поспорю — не логикой, а тем, как оно выглядело у людей.

Да, я понимаю, что люди не читают досконально документацию (особенно, если инструмент не относится ни к их предметной области, ни к основной деятельности) и периодически «наступают на грабли» вплоть до создания issues.

Но опять-таки, [вообще по миру] довольно много issues с «Not a bug» и «Wouldn’t fix», я бы не сказал, что это очень серьёзная причина, чтобы на неё ссылаться как на аргумент: те многие, кто настраивал правильно, не создают issues, и посчитать, какова статистика проявляения проблемы, не получится.

-=-

10.13.13.2/24

Я сейчас специально посмотрел: такого в статье нет, чтобы было a.b.c.X/24, а не a.b.c.0/24!

Это действительно можно посчитать проблемой и обратить внимание в начальной части статьи (если будет описание проблем и решений, и только потом — «раскопки» source-ов), но я бы отнёс к проблеме parser-а userspace-части (который разрешает биты вне маски в AllowedIps), и для этого, опять-таки, не нужно раскапывать ядро.

-=-

Про P.S.

Ну, далеко ходить не надо: в обычном routing-е (который ip r) тоже можно сделать одинаковые net+mask, просто работать корректно это будет только в случае очень нетривиальной настройке всей сети для reliability / fault tolerance.

Но это явно не для wg, который, напомню, был сделан как простой механизм для создания защищённых туннелей.

-=-

То есть “оставить у обоих” — не альтернативная семантика роутинга, а альтернативная семантика отображения

Не обязательно. Возможно, там будет различное поведение, если один из peer-ов (разделяющих один и тот же net+mask) будет недоступен. Но надо смотреть на документацию или тестировать, а мне сейчас не до того… 😇

Про статистику — принимаю, тут вы правы. из трекеров нельзя вывести, часто это или редко: кто настроил верно, никуда не пишет. Считать по issues распространённость действительно нельзя.

Но аргумент у меня был про другое: не сколько людей наступают, а что они после этого видят. Показателен как раз VyOS: там это завели как "WireGuard cannot configure multiple peers - allowed-ips is overwritten", мейнтейнер механизм назвал верно, задачу закрыли с резолюцией Invalid — и тут же предложили добавить проверку пересечений, правда только в 1.3, чтобы не трогать LTS. То есть даже там, где причину поняли сразу, вывод был aka инструмент должен это проверять. netbird из другой корзины: там это чинили в коде продукта, а не разбирали как ошибку настройки.

Про 10.13.13.2/24 — вы правы, и это моя ошибка в комментарии. В статье нет ни этого примера, ни вообще сюжета про маскирование хост-части: я наткнулся на него уже по ходу обсуждения, а написал все из статьи. Приписал тексту то, чего в нём не было. Извиняюсь (

Про парсер —почти) wg как раз ругается: parse_allowedips зовёт validate_netmask(), та проверяет ненулевые биты хост-части, и при неудаче печатается Warning: AllowedIP has nonzero host part. Беда в том, что предупреждение не фатальное и о последствиях молчит. Ядро копать для этого не нужно — оно нужно на шаг дальше, чтобы понять, что после маскирования пять одинаковых префиксов отберут узел друг у друга и никто об этом не скажет.

Про ip r — хорошее уточнение, и оно, по-моему, работает на ту же мысль. Несколько записей на один префикс таблица маршрутов позволяет, но требует сказать это явно: метрикой, отдельной таблицей, списком nexthop. А голый дубль отвергает — ip route add идёт с NLM_F_CREATE|NLM_F_EXCL и получает File exists; чтобы перебить запись, надо попросить об этом отдельным глаголом, replace. То есть там есть и способ выразить намерение, и отказ, когда оно не выражено. В wg set нет ни того, ни другого: любая запись — replace, и всегда молча.

Про P.S. По коду отказоустойчивости нет ни в каком виде: AllowedIPs не пересматриваются по живости пира вообще. В timers.c нет ни одного обращения к allowedips, а когда попытки handshake исчерпаны, код чистит очередь пакетов и обнуляет ключи — префиксы пира при этом не трогает никто. Отдельно я это мерил на RouterOS, где префикс тоже виден у обоих пиров: удаление второго пира первому префикс не вернуло, понадобился повторный set с тем же самым значением. Так что даже явное удаление не переключает, а недоступность — тем более.

Спасибо за фидбек добрый человек!

Чтобы не было недопониманий:
AllowedIPs в секции [Peer] сервера определяет, какой внутренний адрес ТОЧНО соответствует клиенту. Только /32! Никаких повторений!
AllowedIPs в секции [Peer] клиента определяет, какие адреса уйдут в туннель. Тут можете писать что угодно. 0.0.0.0/0 - это "весь" интернет.

С road-warrior частью согласен полностью: для телефонов и ноутбуков на сервере именно /32 и нужен.

Кроме "только /32". Пир не обязан быть хостом. Если за ним сеть, его AllowedIPs обязаны её включать — иначе обратный трафик туда не уедет, а его собственный src на приёме будет отброшен. Это ровно то, что стоит в каноническом примере wg-quick(8), в разделе EXAMPLES, прямо под словами "For use on a server, the following is a more complicated example involving multiple peers":

[Peer]
AllowedIPs = 10.192.122.3/32, 10.192.124.1/24

[Peer]
AllowedIPs = 10.192.122.4/32, 192.168.0.0/16

И живой случай в этой же ветке — у Abyss777 за пирами несколько подсетей, которые анонсирует OSPF. В /32 их не записать.

По второй половине тоже уточню: на клиенте AllowedIPs не только выбирают, что уйдёт в туннель, но и проверяют src входящего. "Что угодно" работает, пока пир один. Появится второй — будут ровно те же столкновения: ролей в коде нет, lookup один и тот же с обеих сторон.

на клиенте AllowedIPs не только выбирают, что уйдёт в туннель, но и проверяют src входящего.

неа) проверяется Address. А в туннель - всё что угодно.

Address это директива wg-quick, а не WireGuard, и до ядра она не доезжает вообще. Прогнал, ядро 6.18.33.2-microsoft-standard-WSL2:

#исходный конфиг
[Interface]
PrivateKey = <скрыт>
Address = 10.100.0.2/24
ListenPort = 51820

# wg-quick strip — то, что реально уходит в wg setconf
[Interface]
PrivateKey = <скрыт>
ListenPort = 51820

# а чт если скормить wg конфиг вместе с Address
Line unrecognized: `Address=10.100.0.2/24'
Configuration parsing error
rc=1

wg-quick забирает Address себе под ip address add ( вместе с DNS, MTU, Table и хуками). В WG_CONFIG они не попадают, а cmd_strip() печатает именно WG_CONFIG. Парсер самого wg знает в [Interface] ровно три ключа: PrivateKey, ListenPort и FwMark.

Проверка src на приёме живёт в другом месте wg_allowedips_lookup_src() ищет пира по адресу отправителя внутри туннеля и сравнивает с тем, чьим ключом пакет расшифрован. Не совпало — dishonest_packet_peer и rx_frame_errors (receive.c:404-420). Второго механизма в receive.c нет.

Про "в туннель что угодно" —это про передачу, и тут вы правы т к AllowedIPs выбирают, куда шифровать. просто список один и тот же, и на приёме он же работает фильтром. Одна структура, две работы)

Address это директива wg-quick, а не WireGuard

Что, простите?? Вы там нейронкой не обкурились случайно?

Всм? Это написано в манах, обе страницы открываются в браузере.

man wg-quick(8), секция CONFIGURATION, первая же фраза: https://manpages.debian.org/testing/wireguard-tools/wg-quick.8.en.html

The configuration file adds a few extra configuration values to the format
understood by wg(8) in order to configure additional attributes of an
interface.

Address — одна из этих extra values, в одном списке с DNS, MTU, Table, PreUp/PostUp и SaveConfig, и описана как "a comma-separated list of IP (v4 or v6) addresses (optionally with CIDR masks) to be assigned to the interface". То есть это то, что уходит в ip address add.

man wg(8), секция CONFIGURATION FILE FORMAT: https://manpages.debian.org/testing/wireguard-tools/wg.8.en.html

Там сказано

The Interface section may contain the following fields

и полей ровно три PrivateKey, ListenPort, FwMark. Address среди них нет ну и поэтому wg setconf на эту строку и отвечает Line unrecognized, вывод стоит комментарием выше.

Хорош уже бредить. setconf налету перезаписывает ключи, а адрес интерфейса он конечно же не меняет.

Ну так так и есть, оба пункта верны. Прогнал, ядро 6.18.33.2-microsoft-standard-WSL2:

# 1. пирA, и на интерфейсе висит адрес
peerA   10.100.0.2/32
    inet 10.100.0.1/24 scope global wg-s

# 2. wg setconf с конфигом, где ТОЛЬКО пир B
rc=0
peerB   10.100.0.3/32

# 3. wg addconf с конфигом, где ТОЛЬКО пир A
rc=0
peerB   10.100.0.3/32
peerA   10.100.0.2/32

# 4. адрес интерфейса после обеих операций
    inet 10.100.0.1/24 scope global wg-s

setconf заменил набор пиров целиком, addconf добавил, адрес интерфейса не шелохнулся. Ровно как написано в wg(8): setconf "sets the current configuration of to the contents of ", addconf — "appends the contents … to the current configuration". https://manpages.debian.org/testing/wireguard-tools/wg.8.en.html

А адрес он не трогает именно потому, что о нём не знает: в формате wg(8) ключа Address нет вовсе, он живёт в wg-quick(8) и уходит в ip address add. Это единственное, что я и утверждал.

Кажется, у wireguard нет протокольного разделения на клиентскую и серверную часть (только логическая), и поэтому нет разделения на серверный и клиентский «AllowedIPs».

AllowedIPs в секции [Peer] сервера определяет, какой внутренний адрес ТОЧНО соответствует клиенту. Только /32!

Ну нет же. Просто обычно клиент — это устройство частного лица, и исходящие пакеты будут с его addr/32, для чего, чтобы пакеты попали обратно, нужен этот самый addr/32. Но совсем не обязательно. Легко может быть хоть net/8 у обоих (впрочем, в нарушение RFC, но это на совести администратора).

/24 это 256 адресов - на какой из них отправляем пакеты?
/32 это один адрес.

На любой из них. Это просто основы routing-а. Router не отправляет «на адрес», он просто пересылает пакет по определённым правилам в определённый сетевой интерфейс.

Это задача получателя: определить — для него ли пакет или отправить дальше (по routing table).

На любой из них.

на 8.8.8.8 не пересылает почему-то))

Кажется, Вы вырываете фразы из контекста и спорите со своими собственными мыслями.

Я нигде не упоминал про «8.8.8.8». Навряд ли у Вас этот адрес указан как «8.8.8.8/24».

-=-

Моё утверждение было в том, что разделение на «серверный» и «клиентский» config — исключительно логическое. Один и тот же host (с одним config-ом) может быть «сервером» для одних соединений и «клиентом» для других (мой реальный случай).

И AllowedIps работает одинаково для обеих сторон именно по причине отсутствия различий («client»/«server») на протокольном уровне.

«client»/«server» работает от наличия строки Endpoint в конфиге.

Не совсем. В данном случае «Endpoint» — это всего лишь указание на то, надо ли инициировать соединение с peer-ом, не более того. После установления соединения стороны друг с другом работают одинаково.

Концепция «client»/«server» означает разделение поведения сторон. https://en.wikipedia.org/wiki/Client–server_model

вот как раз endpoint и разделяет поведение сторон.
Программы-серверы ожидают от клиентских программ запросы
Без endpoint никакого запроса не произойдёт.

Наличие endpoint - клиент.
Нет endpoint - сервер.

Основное отличие Client и server — не столько в инициации соединения, сколько в их поведении[1]. В данном случае этого нет.

Я могу только повторить то, что сообщал выше:

И AllowedIps работает одинаково для обеих сторон именно по причине отсутствия различий («client»/«server») на протокольном уровне.

-=-

[1] Скажем, старые FTP-серверы в том числе соединялись с клиентами — в active mode, но FTP-server не становился от этого FTP-клиентом:

https://en.wikipedia.org/wiki/FTP#Communication_and_data_transfer

И AllowedIps работает одинаково для обеих сторон именно по причине отсутствия различий («client»/«server») на протокольном уровне.

Нет) оттуда и непонимание у автора темы. Нейроночка выдала нейрослоп и понеслось - статья на хабр, ишью в гит, 100500 бесполезных комментов - а всего-то нужно было конфиг поправить

Даже повангую: автор спрашивает у нейронки "типа так?" Нейронка говорит "всё так". Нихрена не вышло, теперь автор бугуртит на wireguard, типа все вы челядь, а он - Дартаньян. А ман вдумчиво прочитать - не может по скудоумию. Параллельно вворачивая какую-то нейрослопную жуть типа OSPF и UAPI. Я ваще хз такие аббревиатуры.

Нет) оттуда и непонимание у автора темы.

У автора вроде не было ничего про то, что AllowedIps работает по-разному на разных концах peer-соединения.

AllowedIps чуть по-разному работает (применяется к src или dst) для входящих и исходящих пакетов (на всякий случай сообщу, что соединения обеспечивается обоими типами пакетов), но и те и другие есть на каждом из концов peer-соединения вне зависимости от того, кто являлся иницииатором peer-соединения.

У автора вроде не было ничего про то, что AllowedIps работает по-разному на разных концах peer-соединения.

Да неужто? Читай пост. Автор лепит ОДИНАКОВЫЕ айпишники на серверной стороне и удивляется - "а чегой-то ничешуя не работает?"

И да, работают они по-разному.

Вы, кажется, не читаете [полностью] сообщения, на которые отвечаете, хотя я даже выделил ключевое слово: «на разных концах peer-соединения».

Автор пишет только про одну сторону, про config на одном и том же устройстве. Я же в своём комментарии выше говорю про то, что

AllowedIps работает одинаково для обеих сторон

т.е. одинаково работает на разных устройствах, между которыми установлен wg-туннель, что именно поэтому нет понятия «сервер» и «клиент» в протоколе WireGuard (из-за чего отличалась бы обработка пакетов), и поведение (после установления соединения) не зависит от того, у кого был прописан «Endpoint» в блоке «Peer».

AllowedIps работает одинаково для обеих сторон

НЕТ! НЕ РАБОТАЕТ!!!

нет понятия «сервер» и «клиент» в протоколе WireGuard (из-за чего отличалась бы обработка пакетов), и поведение (после установления соединения) не зависит от того, у кого был прописан «Endpoint» в блоке «Peer».

не пропишите Endpoint - не получите соединения.
пропишите Endpoint на двух устройствах - получите петлю.

Вы как будто не читаете то, что Вам пишут и на что Вы отвечаете!

@dextor [1] указал на замечательное сообщение в списке рассылки самого WireGuard:

https://lists.zx2c4.com/pipermail/wireguard/2018-December/003704.html

На это сообщение Вы ответили[2], если сократить и привести к вежливому виду, «само собой разумеется» (полностью согласившись с текстом по ссылке).

Но в том сообщении прямо написано:

To quote the WireGuard homepage: “when sending packets, the list of allowed IPs behaves as a sort of routing table, and when receiving packets, the list of allowed IPs behaves as a sort of access control list.”

То есть нет разделения на сервер и клиент, есть входящие (отправляемые) и исходящие (получаемые) пакеты от peer-а (а эти peer-ы есть у обеих сторон туннеля — собственно, его концы), к которым AllowedIPs на каждой из сторон применяется одинаково (что не означает, что на обеих сторонах должно быть одинаковое значение).

Кстати, с официального сайта[3] под процитированной выше есть и такая фраза (выделение — с сайта, но совпадает с тем, как бы выделил и я):

This is what we call a Cryptokey Routing Table: the simple association of public keys and allowed IPs.

Это как раз и есть «таблица криптоключевой маршрутизации», которую Вы высмеивали в другой[4] части комментариев.

-=- [1] https://habr.com/en/articles/1071026/comments/#comment_30338952

[2] https://habr.com/en/articles/1071026/comments/#comment_30338974

[3] https://www.wireguard.com/

[4] https://habr.com/en/articles/1071026/comments/#comment_30337934

В рамках одного config-а — нельзя, об этом статья автора.

На разных концах wireguard-туннеля — можно[1], почему нет?

-=-

[1] Хоть это странно, и с осторожностью: например, если у одного локальная сеть «10.0.0.0/25», а у другого — «10.0.0.128/25»

На разных концах wireguard-туннеля — можно[1], почему нет?

ну в таком случае куда пакет отошлём? На оба конца сразу?

Вы активно ругаете всех за то, что всё очевидно и все всё не понимают, но Вы сами не разобрались в том, как WireGuard работает, а так же — как применяется AllowedIPs.

WireGuard создавался не для того, чтобы одно устройство имело возможность выйти в Интернет от адреса другого устройства (это частный случай настройки WireGuard и сетевой части), он создавался для того, чтобы можно было связывать разные подсети между собой через защищённый туннель.

Касательно Вашего вопроса: отправитель отошлёт пакет, если его dst попадает в 10.0.0.0/24, в peer, тот проверит, что src пакета попадает в 10.0.0.0/24 и отправит в netfilter от имени wireguard-интерфейса.

Это вполне рабочий вариант для обоих направлений, потому что оба входят в 10.0.0.0/24:

  • 10.0.0.0/2510.0.0.128/25

  • 10.0.0.128/2510.0.0.0/25

Вы сами не разобрались в том, как WireGuard работает, а так же — как применяется AllowedIPs.

я то уж разобрался) Это у вас и у ТС какие-то несостыковки)

  • Почему мне шиш показывают, когда я два идентичных пира указываю?

  • Я не разбираюсь в клиент-серверной архитектуре, но у меня есть нейронка и она умная.

он создавался для того, чтобы можно было связывать разные подсети между собой через защищённый туннель.

ну расскажи тогда: есть три подсети 192.168.0.0/24, 10.0.0.0/8 и 172.16.0.0/28. Связывай. Маскарадинг запрещаю.

Server A:

# Local network: 172.16.0.0/28
[Peer]
AllowedIPs = 192.168.0.0/24, 10.0.0.0/8

Server B:

# Local networks: 192.168.0.0/24 and 10.0.0.0/8
[Peer]
AllowedIPs = 172.16.0.0/28

ну попингуй. Хост - 172.16.0.2, цель - 10.100.0.222

Ну так в чём проблема-то?

Этот setup будет работать, AllowedIPs выставлен корректно.

Если Вы заявляете, что здесь есть ошибка, укажите, пожалуйста.

Этот setup будет работать, AllowedIPs выставлен корректно.

Ну проверьте на витруалках)

Вы заявляете, что здесь есть ошибка, укажите, пожалуйста.

Вы уверены, что хост 172 знает о подсети 10?

Ну такая простая проверочка. Вы её не прошли)

Вы уверены, что хост 172 знает о подсети 10?

Это не зона ответственности wireguard. Если задача, чтобы сети видели друга через wireguard (и контекст разговора — настройка wireguard), то предполагается, что устройства в обеих подсетях настроены на routing до своих Server A и Server B и оставшаяся часть — настройка wireguard-config-ов.

То есть достаточно того, чтобы у host-ов в 192.168.0.0/24 и 10.0.0.0/8 был default via server_b_addr, а в 172.16.0.0/28default via server_a_addr.

-=-

Ну такая простая проверочка.

Вы странно задаёте проверки, и непонятно, что проверяете. Если в setup-е есть ошибка — сообщите, почему не будет работать, а не устраивайте тесты, пожалуйста.

wg0 — интерфейс без канального уровня вообще

по космосу видимо пакеты летают. Увы. OSI такая штука, что там вообще нельзя выкинуть какой-то слой или сделать вид что его не существует.

И да, садись - два)

Покажите, пожалуйста, какие пакеты (их поля) на канальном уровне для wg0.

WG - UDP - IP - Ethernet(канальный уровень!)

На этапе «IP» это уже будет не wg-интерфейс. Это будет просто пакет в очереди netfilter и routing (OS), после чего при маршрутизации будет выбран другой интерфейс (не wg0), у которого (а не у самого wg0) и будет (возможно, но необязательно) канальный уровень.

На первом этапе это вообще не пакет, а сырые данные.

Вы теряете нить разговора.

Вам указали, что у wg-интерфейсов нет канального уровня, в том числе потому что интерфейс в OS — это не OSI вообще (OSI — модель стека протоколов, а интерфейс — абстракция в OS, связанная с сетевыми настройками и обработкой сетевых пакетов).

Потом на Ваше сообщение с издёвкой я попросил показать, какие поля в канальном (L2) пакете для wireguard. Вы выдали некорректную информацию, на что я и указал (что канальный уровень, указанный Вами, не относится к wg-интерфейсу).

Сейчас Вы делаете заявление, вообще не касающееся канального уровня, уводя разговор в сторону. Почему Вы так делаете?

Wireguard пакеты по какому каналу связи передаются?

PS Нейроночка ваша лжёт)

Это уже просто IP-пакеты, не связанные с интерфейсом wg0.

Канал может быть разный — Ethernet, Token Ring, PPP, 802.11 (WiFi) и прочие (или даже другой туннель). У каждого — свой набор полей на канальном уровне.

Ииии? WG вполне себе проскакивает через всю эту мишуру. Еще раз для твоей непонятливой тыковки: все данные обязательно проходят все слои OSI.

Вы не читаете то, что Вам пишут: у wg-интерфейса нет своего канального уровня, потому что он сам не реализует непосредственно отправку через физический канал (посредством соответствующего устройства), а пользуется другими интерфейсами (теми, какие будут выбраны OS).

Таким образом, данные (между приложениями) действительно проходят по всему уровню OSI, но конкретно у wireguard-овского интерфейса канального уровня нет.

открываем wireshark на ethernet. OMG! Там внезапно явным текстом - Wireguarg package!!! Олололо нет канального уровня!! Держите меня семеро!!

Так Wireshark смотрит не только на перехваченный (скопированный) пакет, но и декодирует payload настолько, насколько может.

Это не означает, что сам wg-интерфейс использует канальный уровень Ethernet!

То, что Вы видите — это канальный уровень условного eth0, а не wg0.

ну как я могу спорить с такой тупой нейросеткой) Победа твоя)

Это не означает, что сам wg-интерфейс использует канальный уровень Ethernet!

эфир космоса видимо использует, не иначе))

Внимание! У нас тут восьмой слой OSI!! Чистый космос!!

но конкретно у wireguard-овского интерфейса канального уровня нет.

да и в рот его с заковыркой) Это важно? Или ты тут самоутверждаешься, мой юный тролль?

Это важно?

Вы вводите в заблуждение читателей, попутно оскорбляя других комментирующих и выдавая[1] своё невежество. Более того, Вы по нити обсуждения настаивали на своей точке зрения, но не отвечали на конкретные просьбы показать конкретику, которая должна была бы существовать, исходя из Ваших заявлений.

[1] https://habr.com/en/articles/1071026/comments/#comment_30339058

по секрету, братишка) мне ваще похую)) Особенно на твой беспардонный нейрослоп.

иди жалуйся

Вам указали

Это ты тут указал какую-то ерунду. Тире и кавычки, братишка) Не сношай мне моск, ёлочка-лапочка)

не пропишите Endpoint - не получите соединения. пропишите Endpoint на двух устройствах - получите петлю.

Пожалуйста, не ленитесь читать то, что Вам пишут: сервер и клиент не определяются тем, кто инициировал соединение.

По Вашей логике — если, для peer-записей одного туннеля, у устройства A будет AllowedIPs = X, а у устройства B будет AllowedIPs = Y, то при удалении «Endpoint» у одного устройства и прописывании соответствующего «Endpoint» у другого устройства (т.е. при смене инициатора wg-туннеля, и, в Вашей терминологии, «смены client-а и server-а между собой»), туннель перестанет работать без внесения изменений в AllowedIPs, но это не так: всё продолжит работать с теми же AllowedIPs.

##### сервер:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = WUzNjM...

[Peer]

PublicKey = Jn/4...
AllowedIPs = 10.8.0.2/32

##### клиент:
[Interface]
Address = 10.8.0.2/32

[Peer]
PublicKey = FsrX...
Endpoint = X.X.X.X:YYY
AllowedIPs = 0.0.0.0/0


В чём тут у вас сложности?

да пропиши уже нулиAllowedIPs на обоих концах туннеля и наслаждайся эффектом. Достал уже со своей тупой нейронкой.

Спроси там свою нейроночку, что будет если на сети из двух узлов wg прописать каждому эндпоинт другого узла.

exact match (A: /24, B: тот же /24 -> строка A пропадает)

А траффик куда должен идти при двух ИДЕНТИЧНЫХ роутах? К господу богу, чтобы он сам там разрулил этот несчастный траффик?

Никуда больше и не должен, я с этим не спорю) Едет он последнему записавшему, и это не как бог решит, а вполне детерминированно. Я как раз с обратным и спорю: defguard в своём разборе AllowedIPs пишет "undefined behavior" и "could go to either peer", хотя ничего неопределённого там нет.

Вопрос не в том, куда поедет трафик, а в том, узнает ли об этом администратор. Сравните с обычной таблицей маршрутов: ip route add на занятый префикс отвечает RTNETLINK answers: File exists, и чтобы перебить запись, надо явно попросить — ip route replace. У wg set второго режима нет вообще, любая запись это replace. Код возврата 0, dmesg пуст, в .conf у первого пира префикс по-прежнему стоит, а в ядре его уже нет.

И наружу это выходит не как конфликт конфигурации. Пир не отваливается, endpoint на месте, handshake свежий, байты от него приходят и считаются. Выглядит как "у половины клиентов почему-то пропал доступ", причём в конфиге, куда идут смотреть первым делом, всё написано правильно.

Статья про этот разрыв, а не про то, что trie обязана разрулить два одинаковых префикса

trie обязана разрулить два одинаковых префикса

и как вы себе это представляете?

Никак не представляю, вы процитировали ровно то, что я отрицал. Полная фраза была "статья про этот разрыв, а не про то, что trie обязана разрулить два одинаковых префикса". Разруливать там нечего, и я этого нигде не требую

Требуется другое. сказать вслух, что произошла замена. Дешевле всего это делается даже не в ядре, а в userspace. wg setconf получает весь список пиров разом, и проверка пересечений там стоит одного прохода по такому же дереву — warning в stderr, рядом с уже существующим про nonzero host part. Половины историй с "allowed ips: (none)" после этого просто не было бы. В ядре хватило бы ratelimited-строки в лог при перевешивании узла: механизм рядом уже стоит, им печатается unallowed src IP.

А без правок апстрима работает то, что у меня в выводах: после применения сравнивать wg showconf с тем, что на диске. Разошлось — значит префикс кто-то забрал.

Так вот почему у меня ECMP (equal cost multiple path) не работает через wireguard… И все выше спросившие почему там не /32 на каждом пире: да потому что там много подсетей! Которые рулятся через OSPF. И пакет из дальней подсети может прийти как через один пир, так и через другой, и вообще через оба одновременно.

Как это обойти? Кроме как не использовать wireguard?

еще один вундеркинд. Если за wg у тебя еще есть локальные сетки - в iptables/nftables прописывай маскарадинг.

Маскарадинг эту задачу не решает.

Он не про тот конец) проблема на передаче: 10.50.0.0/24 в trie принадлежит ровно одному пиру, и NAT этого не меняет — выбор пира по dst остаётся единственным, а значит второго пути как не было, так и нет. Маскарадинг переписывает src у трафика, уходящего в туннель, то есть лечит в лучшем случае проверку источника на приёме, попутно скрывая от центра настоящие адреса дальних сетей.

И заодно ломает то, ради чего там OSPF: NAT работает только для соединений, инициированных изнутри. Достучаться из центра до хоста в дальней подсети после маскарадинга уже нельзя, а анонсировать эти подсети незачем — за NAT их всё равно не видно.

Рабочий вариант это по wg-интерфейсу на соседа и ECMP в таблице маршрутов ядра. Тогда и multipath настоящий, и адресация сквозная и OSPF работает как обычно.

Достучаться из центра до хоста в дальней подсети после маскарадинга уже нельзя

Ну видимо мой телефон работает на магии) Всю локальную сетку могу пинговать из под мобильной связи)

Направление другое. т к вы проверяете ход изнутри наружу — он работает и с маскарадингом, и без него, ровно для этого NAT и делают. Утверждение было про обратный: дотянуться из центра до хоста в дальней подсети, который сам ничего не инициировал.

Есть ли у вас на этом пути NAT вообще, из пинга не видно, и смотреть надо не пингом. Запустите tcpdump на 192.168.0.33 и гляньте, с каким src приходят пакеты телефона. Туннельный адрес телефона — маскарадинга нет, адресация сквозная. Адрес сервера — есть, и тогда обратный ход с 192.168.0.33 до телефона по его туннельному адресу не пройдёт.

И топология у Abyss777 не ваша.у него за пирами локальные сети, которые анонсирует OSPF, и нужен второй равноценный путь до одной и той же подсети. NAT второго пути не создаёт, куда его ни поставь, а там, где он прячет сеть, она перестаёт быть достижимой по своим адресам — анонсировать её после этого незачем.

из центра до хоста в дальней подсети, который сам ничего не инициировал.

Всмысле? "Центр" это типа сервер? Ну дык он сам ничего и не инициировал.

Ну смотри, центр — это хаб, куда сходятся туннели. Дальняя подсеть это локальная сеть за пиром, на котором вы предлагали включить маскарадинг.

Направление такое.хост из дальней подсети инициирует соединение — до центра оно доходит, NAT по дороге создал трансляцию. Центр инициирует первым — не доходит, транслировать ещё нечего. Инициатором в моей фразе был центр, а "сам ничего не инициировал" относилось к дальнему хосту.

Это и несовместимо с задачей Abyss777: ему нужно, чтобы любая точка была достижима из любой, а не только та, которая заговорила первой.

чтобы любая точка была достижима из любой, а не только та, которая заговорила первой.

ну а я о чём? Телефон за провайдерским NAT пинганул машину, которая тоже находится за NAT.

Это ж два разных NAT на двух разных уровнях.

Провайдерский работает с внешними UDP-пакетами туннеля. вг ровно для этого и умеет persistent-keepalive: трансляция держится открытой, а endpoint пира подхватывается оттуда, откуда пришёл авторизованный пакет. внутренние адреса при этом не прячутся ни от кого — 10.100.0.2 остаётся 10.100.0.2 по обе стороны туннеля.

Маскарадинг, который вы предлагали Abyss777, другой: он переписывает src уже внутри туннеля. После него дальняя сеть перестаёт существовать под своими адресами, и анонсировать в OSPF становится нечего.

И инициатором в вашем примере всё равно был телефон. Обратный ход — чтобы 192.168.0.33сам, первым, позвал телефон, — вы не показывали. А Abyss777 нужен именно он, в обе стороны и по настоящим адресам. Плюс второго равноценного пути до одной подсети NAT не создаёт ни на каком уровне, а задачка была про это.

Мда уж. Слышал звон, да не знал, где он...

Так тут адрес назначения не тот. А вопрос был про обратный ход чтобы машина из локалки первой позвала телефон по его туннельному адресу. Здесь назначение 192.168.0.32, снова хост в локалке. Откуда бы этот пинг ни шёл, на заданный вопрос он не отвечает.

И отдельно: ttl=128 без декремента означает, что пакет не прошёл ни одного роутера. Через туннель так не бывает т к wg0 нельзя включить в бридж, у него нет канального уровня вовсе (ARPHRD_NONE, замер парой комментариев выше), поэтому любой путь через WireGuard маршрутизируемый и TTL уменьшает. Ответ Windows-машины, прошедший через хаб, приехал бы со 127.

Так тут адрес назначения не тот.

Всмысле не тот? Это машина с .33 На телефоне .32

пакет не прошёл ни одного роутера

А он и не прошел) Это одна сетка.

Так всм, если пакет не прошёл ни одного роутера, значит через туннель он и не шёл же. wg0 в бридж не включить, канального уровня у него нет, поэтому путь из локалки к телефону по туннелю обязательно маршрутизируемый и TTL уменьшает. Ноль декрементов означает, что .33 и .32 в этот момент были в одном сегменте то есть телефон сидел в той же локалке по Wi-Fi.

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

телефон сидел в той же локалке по Wi-Fi.

а ты видел значок вайфая на скриншоте?

Я еще и вундеркинд… А если мне не нужен маскарадинг? Мне нужна нормальная отказоустойчивая маршрутизируемая сеть, чтоб из каждой точки любая другая точка была доступна по своему IP, а не через SNAT/DNAT.

Именно) и маскарадинг тут даже не про тот конец задачи, он переписывает src у того, что уходит в туннель, а пир выбирается по dst т е 10.50.0.0/24 в trie всё равно принадлежит одному пиру, второго пути от NAT не появляется. то есть он лечит в лучшем случае проверку источника на приёме, ценой ровно той сквозной адресации, ради которой всё и строилось.

он переписывает src у того, что уходит в туннель

и еще двойка. Маскарадинг вешается на исходящий интерфейс. переписывает src у того, что ВЫХОДИТ из туннеля.

Подождите, как давно в обычном network setup-е появился «исходящий интерфейс», чтобы на него «вешался маскарадинг»??? Значит, есть и отдельный от него «входящий интерфейс»?

Можно сказать «интерфейс с публичным адресом» или «интерфейс, соединённый с Интернетом»…

https://ru.wikipedia.org/wiki/Маскарадинг «англ. Masquerading — притворяться: выдавать себя за кого-либо», ключевое слово — «себя».

Если пакет «ВЫходит» из туннеля (в очередь обработки пакетов), это чужой пакет. Когда он уже назначен на отправку через определённый интерфейс, на нём уже нет метки «свой» или «чужой», по правилу masquerading-а надо в исходящем пакете поменять src на адрес, назначенный для интерейса.

P.S.: пожалуйста, не разбрасывайтесь фразами типа «еще двойка». Это не выглядит, как нормальный разговор с Вашей стороны.

не надо жонглировать понятиями. В рамках темы интерфейс wg представлен как "локальная сеть" или "внутренний интерфейс". Выход в глобальный интернет - это соответственно, "внешний интерфейс". Маскарадинг в таком случае применяется именно к "внешнему" интерфейсу.

P.S.: пожалуйста, не разбрасывайтесь фразами типа «еще двойка». Это не выглядит, как нормальный разговор с Вашей стороны.

Вообще вся эта тема не выглядит нормальной. Автор вместо того чтобы просто исправить свой конфиг полез в какую-то нейрослопочную муть с претензией типа "я пишу в конфиг - работай сука, а оно меня посылает на три буквы и не работает."

Я просто удивился используемым терминам. При определённых настройках может быть отправка пакетов через один интерфейс (тогда он будет «исходящим»), а приём — через другой (тогда он будет «входящим»). Такое бывает, но редко.

Если использовать «внешний» и «внутренний» — будет чуть получше, но всё равно не точно (при определённом построении сетей). Но давайте использовать эти термины для упрощения.

Тем не менее, это предложение всё равно неточно:

Маскарадинг вешается на исходящий интерфейс. переписывает src у того, что ВЫХОДИТ из туннеля.

Я бы переформулировал Ваше предложение так (как я понял после разъяснений): «Маскарадинг настраивается на внешнем интерфейсе и переписывает src у того пакета, который ранее был получен из туннеля.»

Мне кажется, разница заметна и значительна, потому что никто не запрещает настроить masquerading на любом интерфейсе, в том числе на связанном с wg. В этом случае те пакеты из локальной сети, которые смаршрутизируются (уйдут) в интерфейс wg, при отправке туда поменяют свой src на адрес wg-интерфейса, а что с ними будет делать та сторона, зависит от её настроек. Но с её стороны пакеты придут от этого host-а (с его addr), а не из его подсети (подсетей).

Но с её стороны пакеты придут от этого host-а (с его addr), а не из его подсети (подсетей).

В рамках wg пакеты придут как положено, не сомневайтесь)

Мне нужна нормальная отказоустойчивая маршрутизируемая сеть, чтоб из каждой точки любая другая точка была доступна по своему IP, а не через SNAT/DNAT.

через SNAT/DNAT как раз она и будет доступна) Но нужен хотя бы один хост с белым ip.

Да, это оно. И вы описали случай точнее, чем я в статье: у вас не ошибка конфигурации, а требование, которое AllowedIPs выразить не может в принципе.

Причина ровно та, что в разборе: trie одна на устройство, и у узла ровно один указатель на пира (device.h:48-50). "Сеть доступна через двух пиров" в такой структуре не записывается — второй wg set просто заберёт префикс себе. Приём ломается симметрично: пакет из дальней подсети, пришедший от «неправильного» пира, отбрасывается как dishonest_packet_peer, потому что lookup_src вернёт другого. ECMP внутри одного wg-интерфейса невозможен не по недосмотру, а по устройству.

Лечится подъёмом ECMP на уровень выше — в таблицу маршрутов ядра, которая multipath умеет: по интерфейсу на соседа, у каждого ровно один пир и AllowedIPs = 0.0.0.0/0. Проверил на живом модуле, ядро 6.18.33.2-microsoft-standard-WSL2. Сразу оговорюсь:это проверка конфигурации и таблицы маршрутов, трафик через туннели я не гонял — эндпоинтов и хендшейка в этом прогоне нет

#1. ОДИН интерфейс, два пира, обоим 10.50.0.0/24 — оба wg set вернули 0
V0lRScKAfhrQ3jlPWb4Htxmy7aV30umdROXVVjVIIQ0=    (none)
CYO+1ZvA00gHXFji14e9U/7WTVnmGV8bC3cOyQEeQgk=    10.50.0.0/24

# 2. ДВА интерфейса, по пиру на каждом, обоим 0.0.0.0/0
wg-a: V0lRScKAfhrQ3jlPWb4Htxmy7aV30umdROXVVjVIIQ0=    0.0.0.0/0
wg-b: CYO+1ZvA00gHXFji14e9U/7WTVnmGV8bC3cOyQEeQgk=    0.0.0.0/0

# 3. ip route add 10.50.0.0/24 nexthop dev wg-a weight 1 nexthop dev wg-b weight 1
10.50.0.0/24
        nexthop dev wg-a weight 1
        nexthop dev wg-b weight 1

# 4. ip route get
10.50.0.7 dev wg-b src 10.0.2.1 uid 0
10.50.0.8 dev wg-a src 10.0.1.1 uid 0
10.50.0.9 dev wg-a src 10.0.1.1 uid 0
10.50.0.10 dev wg-b src 10.0.2.1 uid 0
10.50.0.11 dev wg-b src 10.0.2.1 uid 0

Один и тот же префикс на одном устройстве даёт (none) у первого пира, а на двух устройствах два 0.0.0.0/0 живут спокойно, и ядро раскладывает по ним адреса. Trie у каждого устройства своя — то есть работает это по той же причине, по которой ломалось. Криптоключевая маршрутизация при этом вырождается в " от этого пира принимаем всё", что для транзитного линка и требуется, а путь выбирают ядро и OSPF поверх обычных p2p-интерфейсов. Цена — по интерфейсу и по UDP-порту на соседа, listen-port два wg-устройства не делят.

Что теряется: проверка src средствами AllowedIPs — с /0 она не фильтрует ничего. Если анти-спуфинг нужен, он переезжает в rp_filter или nftables. Со строгим rp_filter при multipath, кстати, всё нормально, вопреки распространённому мнению: __fib_validate_source() зовёт fib_info_nh_uses_dev(), а тот обходит все нексхопы маршрута и принимает пакет, пришедший с любого из них. Strict ломается не на ECMP как таковом, а на настоящей асимметрии, когда обратный маршрут — не тот же multipath.

Если очень хочется остаться на одном интерфейсе — тогда не гонять маршрутизацию через AllowedIPs вообще: WireGuard как транспорт с /32 между эндпоинтами, а внутрь GRE или VXLAN, и OSPF уже на них. тяжелее и с накладными расходами, зато никаких ограничений на динамическую маршрутизацию.

завязывай с нейрослопом. Это уже за гранью. У WG всего-то 200 строчек кода, а нейронка и готова уже "разползтись мысию по древу"

Млять. Конечно этого нет в документации. Ну это как бы на каждой вилке было б написано - не тыкать в глаз

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации