Ви піднімаєте тунель WireGuard. Handshake виглядає здоровим. Лічильники байтів трохи інкрементуються. І все одно трафік, який ви хотіли пропустити через VPN,
йде не тим інтерфейсом, або ще гірше: зникає в тиху прірву, де ping-и втрачають надію.
Дев’ять разів з десяти корінь проблеми — не криптографія, не MTU і не «інтернет впав». Це нерозуміння того, що означає
AllowedIPs: хто його використовує і як воно перетворюється на реальні маршрути в операційній системі.
Ментальна модель: AllowedIPs — це політика маршрутизації, а не ACL
Якщо винести з цього одне: AllowedIPs — це вхідний параметр для прийняття рішення про маршрутизацію.
Це не правило фаєрволу. Це не «хто може підключатися».
І це не «які IP існують на іншому боці».
У WireGuard у кожного peer є список AllowedIPs. Цей список виконує дві функції:
-
Вибір вихідного peer. Коли локальний хост хоче відправити пакет до певного IP-адреси призначення,
WireGuard обирає peer, чиїAllowedIPsмістить цей призначення. Фактично це відповідає на питання
«який peer має нести цей пакет». -
Вхідна валідація джерела. Коли пакет приходить від peer, WireGuard перевіряє, чи IP-адреса
джерела пакета належить доAllowedIPsцього peer. Якщо ні — WireGuard відкидає пакет.
Оце й усе. Воно не програмує магічно всю вашу мережу, якщо щось інше (зазвичай wg-quick)
не перетворює ці префікси на маршрути ядра. І навіть тоді це лиш маршрути.
Ось режим відмови, що утримує інженерів SRE у строю: люди припускають, що AllowedIPs — це «віддалені підмережі».
Іноді так і є. Іноді це «віддалені підмережі плюс default route, бо я хочу повний тунель».
А іноді це «IP самого peer». Але жодне з цього не є універсально правильним. Це — наміри,
а наміри потребують відповідної маршрутизації, форвардингу, NAT і поведінки DNS.
Ще один ключовий момент: WireGuard не говорить «маршрути» напряму. Він говорить «peer-и та дозволені префікси».
Ядро ОС говорить «маршрути». wg-quick — перекладач і іноді хаос-обслуговувач.
Жарт №1: AllowedIPs названо «Allowed», бо «PleaseStopSendingMyPacketsIntoTheWrongTunnelIPs»
не вмістився б у конфігураційний файл.
Цікаві факти та історичний контекст
Трохи контексту робить дивні речі менш особистими.
- WireGuard потрапив у ядро Linux у 2020 році (Linux 5.6). До того він жив поза деревом ядра, що сформувало його мінімалістичний інтерфейс.
- WireGuard навмисно уникає «перемикання режимів», як багато традиційних VPN (transport vs tunnel). Натомість він завжди L3: ви маршрутизуєте IP-пакети.
wg(інструмент) не додає маршрути. Це було свідоме розділення: драйвер VPN знає peer-ів; ОС знає маршрути.wg-quick— це обгортка для зручності, що використовує стандартні інструменти (ip,resolvconf/systemd-resolved,iptables/nft) для створення «досвіду VPN».- AllowedIPs нагадує стисну таблицю маршрутів: фактично це еквівалент «ці префікси належать сюди», плюс перевірка проти підробки джерела.
- WireGuard використовує концепцію «cryptokey routing»: замість вибору тунелю за інтерфейсом він обирає peer за публічним ключем і відповідністю префікса призначення.
- Linux підтримує кілька таблиць маршрутизації і правила (policy routing).
wg-quickвикористовує їх для повних тунелів, щоби не псувати основну таблицю. - Перекриття префіксів легальні у конфігу WireGuard, але правила вибору (longest prefix match) можуть здивувати у багатопірних розгортаннях.
- IPv6 часто ламається першим, бо люди уважно налаштовують AllowedIPs для IPv4 і забувають про IPv6 або AAAA-відповіді DNS. Тунель працює; браузер — ні.
Як пакети насправді рухаються: маршрути ядра, WireGuard та wg-quick
Три рівні маршрутизації, з якими ви маєте справу (хочете ви того чи ні)
Коли ви запускаєте WireGuard на Linux, існують три релевантні точки прийняття рішення:
- Маршрутизація Linux: вибирає вихідний інтерфейс і next hop для IP-адреси призначення, використовуючи таблиці маршрутів і правила політики.
-
Вибір peer у WireGuard: всередині інтерфейсу WireGuard обирає, який peer має нести пакет, базуючись на збігу призначення з
AllowedIPs. - Маршрутизація/форвардинг на віддаленому боці: віддалений кінець має знати, що робити з пакетом далі (доставити локально, переслати, зробити NAT тощо).
Якщо будь-який з цих трьох рівнів не погоджується, ви отримаєте класичний досвід «handshake, але немає трафіку».
Успішний handshake лише доводить, що UDP може дістатися peer і ключі валідні. Це нічого не каже про
зворотний шлях, NAT або форвардинг.
Що насправді робить wg-quick (і чому варто читати його вивід)
wg-quick up wg0 зручний. Він також упереджений:
- Створює інтерфейс (
ip link add wg0 type wireguard). - Призначає адреси інтерфейсу з
Address =. - Налаштовує peer-ів WireGuard і їхні
AllowedIPs. - Додає маршрути для кожного префікса AllowedIPs (якщо не використовується policy routing).
- Для повного тунелю (0.0.0.0/0 або ::/0) часто додає policy routing правила і окрему таблицю.
- Може налаштувати DNS (залежно від
DNS =та вашого стеку резолвера). - Може додавати правила фаєрволу, якщо ви використовуєте
PostUp/PostDownабо інтеграції дистрибутива.
Критична частина: ці маршрути не «WireGuard-маршрути». Це маршрути ядра.
Якщо ви пізніше підправите AllowedIPs і не перезапустите wg-quick (або вручну не підправите маршрути),
таблиця маршрутів ядра може не відповідати вашому наміру. Ця невідповідність тонка і поширена.
Шаблон «дефолтний маршрут через WireGuard»
Встановлення AllowedIPs = 0.0.0.0/0, ::/0 для peer каже:
для будь-якої IP-адреси призначення обирайте цього peer. Але це не обов’язково означає, що ядро буде відправляти
кожний пакет на інтерфейс wg0. Це залежить від правил маршрутизації.
На Linux wg-quick зазвичай реалізує повний тунель так:
- Додає дефолтний маршрут в окремій таблиці маршрутів (часто таблиця 51820).
- Додає
ip ruleзаписи так, щоб трафік, що походить з IP інтерфейсу WireGuard
(або пакети з певною міткою), використовував ту таблицю. - Додає винятки так, щоб IP-адреса endpoint peer-а все ще йшла через реальний uplink, а не через тунель
(щоб уникнути тунелювання тунелю).
Це виняток — чому іноді «мій VPN піднявся і відразу вбив сам себе»: якщо виняток для endpoint
неправильний (через зміни DNS, конфуз з двостеком або ви за NAT-провайдером з змінним endpoint),
ваші UDP-пакети до endpoint потрапляють у тунель. Тунель тоді не може досягнути endpoint.
Це самосаботаж з ідеальною логікою.
Швидкий план діагностики
Ви хочете швидкість. Ви також хочете уникнути класичної пастки, коли міняють п’ять речей одночасно і потім не знають, що саме це виправило.
Спочатку: визначте, чи пакети входять і виходять через інтерфейс WireGuard
- Перевірте handshake і лічильники передачі.
- Перевірте рішення ядра щодо маршруту до призначення з
ip route get(IPv4 і IPv6). - Зробіть захоплення трафіку на
wg0і на фізичному інтерфейсі, щоби побачити куди пакети йдуть насправді.
По-друге: підтвердіть, що логіка вибору peer відповідає вашим наміром AllowedIPs
- Перелічіть peer-ів і їхні AllowedIPs.
- Пошукайте перекриття (наприклад, два peer-и претендують на 10.0.0.0/8).
- Перевірте, чи IP-адреси джерела вхідних пакетів належать до AllowedIPs відправника (перевірка проти підробки).
По-третє: перевірте форвардинг/NAT і зворотний шлях на віддаленому боці
- Чи увімкнено IP forwarding на «сервері» або шлюзі peer-а?
- Чи віддалена мережа знає маршрут назад до підмережі VPN клієнта?
- Якщо ви покладаєтеся на NAT — чи він застосовується до правильного інтерфейсу й діапазону джерел?
По-четверте: розв’яжіть нудних вбивць: MTU та DNS
- Тестуйте PMTU з
ping -M do(IPv4) і реалістичними розмірами пакетів. - Підтвердіть, що DNS-сервери і search-домени ті, яких ви очікуєте після підняття тунелю.
- Переконайтесь, що IPv6 не просочується або не потрапляє в чорну діру (особливо при повному тунелі лише для IPv4).
Практичні завдання: команди, виводи та рішення
Це частина, яку ви запускаєте під час інциденту. Кожне завдання містить: команду, що означає її вивід,
і яке рішення приймати далі.
Завдання 1: Підтвердіть, що WireGuard бачить peer-а і обмінюється пакетами
cr0x@server:~$ sudo wg show
interface: wg0
public key: 9zHhYw...redacted...
private key: (hidden)
listening port: 51820
peer: 2YpG3h...redacted...
endpoint: 203.0.113.50:54022
allowed ips: 10.40.0.2/32, 10.99.0.0/16
latest handshake: 28 seconds ago
transfer: 14.20 MiB received, 18.77 MiB sent
persistent keepalive: every 25 seconds
Значення: handshake свіжий і лічильники ненульові. WireGuard може дістатися endpoint і розшифровувати/шифрувати трафік.
Але це ще не доказ того, що ОС маршрутизує трафік у wg0, або що віддалий кінець його форвардить.
Рішення: якщо handshake застарілий (хвилини+) або transfer лишається нульовим, зосередьтеся на доступності UDP, адресі endpoint
і фаєрволі/NAT. Якщо все здорово — переходьте до перевірки маршрутизації.
Завдання 2: Перевірте адреси інтерфейсу (крок «якою IP я взагалі користуюсь»)
cr0x@server:~$ ip -brief address show dev wg0
wg0 UP 10.40.0.1/24 fd00:40::1/64
Значення: wg0 має IPv4 і IPv6 адреси. Якщо ваш peer очікує /32, але ви налаштували /24 — це не автоматично помилка,
але впливає на ваші припущення щодо маршрутизації і NAT.
Рішення: підтвердіть, що обидва боку згодні щодо схеми адресації (host routes vs subnets), і що
AllowedIPs включає адреси джерел peer-а.
Завдання 3: Запитайте ядро, куди воно відправить певне призначення
cr0x@server:~$ ip route get 10.99.10.25
10.99.10.25 dev wg0 src 10.40.0.1 uid 0
cache
Значення: ядро відправить пакети до 10.99.10.25 через wg0. Добре.
Якщо воно показує dev eth0 або інший інтерфейс, таблиця маршрутів ядра не відповідає вашому наміру.
Рішення: якщо маршрутизація неправильна, перевірте маршрути і правила політики (ip route, ip rule).
Не чіпайте конфіг WireGuard одразу.
Завдання 4: Зробіть те саме для IPv6 (бо двостек може газлайтити вас)
cr0x@server:~$ ip -6 route get 2606:4700:4700::1111
2606:4700:4700::1111 via fe80::1 dev eth0 src 2001:db8:12::10 metric 1024 pref medium
Значення: IPv6 трафік виходить через eth0, а не wg0. Якщо ви очікували повний тунель, ймовірно забули
::/0 в AllowedIPs або правила policy routing для IPv6 не були додані.
Рішення: вирішіть, чи хочете ви тунелювати IPv6, блокувати його чи залишити як є. «Не знаю» — шлях до напівтунелю і збіса.
Завдання 5: Перевірте маршрути, додані для AllowedIPs
cr0x@server:~$ ip route show table main | grep -E 'wg0|10\.99\.|10\.40\.'
10.40.0.0/24 dev wg0 proto kernel scope link src 10.40.0.1
10.99.0.0/16 dev wg0 scope link
Значення: у вас є прямий маршрут до 10.99.0.0/16 через wg0. Це відповідає AllowedIPs peer-а.
Якщо маршрут відсутній, система навіть не спробує покласти трафік у тунель.
Рішення: якщо маршрути відсутні, перевірте, чи ви використовували wg (він не додає маршрути) проти wg-quick (додає маршрути),
або чи NetworkManager/systemd-networkd керує маршрутами в іншому місці.
Завдання 6: Перевірте правила policy routing (повний тунель і «чому лише частина трафіку йде через wg0»)
cr0x@server:~$ ip rule show
0: from all lookup local
32764: from all fwmark 0xca6c lookup 51820
32765: from all lookup main suppress_prefixlength 0
32766: from all lookup main
32767: from all lookup default
Значення: трафік з міткою fwmark 0xca6c використовує таблицю 51820.
Правило suppress_prefixlength 0 — звичайний прийом wg-quick, щоб уникнути циклів маршрутизації.
Рішення: якщо ви очікували повний тунель, але не бачите цих правил, wg-quick ймовірно їх не застосував
(або інший інструмент їх видалив). Виправляйте маршрути перш ніж безладно розширювати AllowedIPs.
Завдання 7: Перевірте специфічну таблицю маршрутів WireGuard (якщо використовується)
cr0x@server:~$ ip route show table 51820
default dev wg0 scope link
203.0.113.50 via 192.0.2.1 dev eth0
Значення: у таблиці 51820 дефолт йде через wg0, але endpoint 203.0.113.50 явно маршрутизовано через шлюз eth0.
Це «не тунелюй тунель» виняток.
Рішення: якщо виняток для endpoint відсутній або вказує не на той шлюз, тунель може флапати або ніколи не встановитись.
Виправте маршрут до endpoint, особливо якщо endpoint — це hostname, що резолвиться в кілька IP.
Завдання 8: Перевірте, чи увімкнено форвардинг на gateway peer
cr0x@server:~$ sysctl net.ipv4.ip_forward net.ipv6.conf.all.forwarding
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
Значення: цей вузол не форвардить пакети між інтерфейсами. Якщо він повинен діяти як site-to-site шлюз,
це критична перепона.
Рішення: увімкніть форвардинг (і зробіть це персистентним), якщо цей хост має маршрутизувати трафік; інакше переробіть дизай: залиште його як хост-ендпоінт тунелю.
Завдання 9: Підтвердіть, що правила фаєрволу/NAT відповідають дизайну (приклад iptables)
cr0x@server:~$ sudo iptables -t nat -S | grep -E 'POSTROUTING|wg0|eth0'
-A POSTROUTING -s 10.40.0.0/24 -o eth0 -j MASQUERADE
Значення: пакети з джерелом 10.40.0.0/24, які виходять через eth0, будуть маскувані. Якщо ваші клієнти в 10.40.0.0/24 і ви хочете, щоб вони
виходили в інтернет через цей сервер — це, ймовірно, потрібно.
Рішення: якщо вам потрібен site-to-site без NAT, не маскуйте. Натомість додайте маршрути у віддаленій мережі назад до 10.40.0.0/24.
NAT «працює», поки не зламає аудит, вхідні підключення і ваше бажання жити.
Завдання 10: Зробіть захоплення трафіку, щоб побачити неправильний поворот
cr0x@server:~$ sudo tcpdump -ni wg0 host 10.99.10.25
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on wg0, link-type RAW (Raw IP), snapshot length 262144 bytes
12:11:02.100112 IP 10.40.0.1 > 10.99.10.25: ICMP echo request, id 2101, seq 1, length 64
12:11:03.110144 IP 10.40.0.1 > 10.99.10.25: ICMP echo request, id 2101, seq 2, length 64
Значення: пакети входять у wg0. Якщо немає відповідей, проблема, ймовірно, за межами цього хоста:
віддалена маршрутизація, віддалений фаєрвол або AllowedIPs валідація джерела на віддалому боці.
Рішення: зробіть захоплення на віддаленому боці також. Якщо віддалений кінець отримує, але не відповідає — перевірте його маршрути та фаєрвол.
Якщо він ніколи не отримує — перевірте endpoint NAT і чи ви надсилаєте на правильного peer-а.
Завдання 11: Перевірте перекриття AllowedIPs між peer-ами (скритий фатальний фрагмент)
cr0x@server:~$ sudo wg show wg0 allowed-ips
2YpG3h...redacted... 10.40.0.2/32
2YpG3h...redacted... 10.99.0.0/16
7QkLm1...redacted... 10.99.10.0/24
Значення: є перекриття: один peer претендує на 10.99.0.0/16, інший — на 10.99.10.0/24.
Longest prefix match означає, що 10.99.10.x піде до /24 peer-а, а не до /16 peer-а.
Рішення: вирішіть, чи це навмисно. Якщо ні — видаліть або звузьте один з префіксів.
Якщо так — задокументуйте це голосно й протестуйте поведінку при відмові, бо під час onboarding легко зламати.
Завдання 12: Перевірте, що wg-quick насправді застосував (аудит «перекладача»)
cr0x@server:~$ sudo wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.40.0.1/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 10.99.0.0/16 dev wg0
[#] resolvconf -a tun.wg0 -m 0 -x
[#] iptables -A FORWARD -i wg0 -j ACCEPT
[#] iptables -A FORWARD -o wg0 -j ACCEPT
[#] iptables -t nat -A POSTROUTING -s 10.40.0.0/24 -o eth0 -j MASQUERADE
Значення: це реальний набір змін. Ви бачите MTU, маршрути, зміни DNS і правила iptables.
Якщо ваша ментальна модель відрізняється від цього виводу — ваша ментальна модель неправильна.
Рішення: якщо вивід wg-quick показує зміни, яких ви не хотіли (маніпуляції DNS, NAT, форвардинг),
перестаньте покладатися на неявну поведінку і зафіксуйте конфіг явно або керуйте ним своїми оркестраційними засобами.
Завдання 13: Перевірте reverse path filtering (тихий режектор)
cr0x@server:~$ sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.wg0.rp_filter
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.wg0.rp_filter = 1
Значення: строгий reverse path filtering може відкидати пакети, якщо ядро вважає, що зворотний маршрут для IP-адреси джерела
не повертався б тим самим інтерфейсом. З policy routing і асиметричними шляхами це може креативно ламати WireGuard-трафік.
Рішення: на шлюзах з важкою маршрутизацією розгляньте можливість встановлення rp_filter в режим loose (2) там, де це доречно,
але робіть це усвідомлено і з оглядом на безпеку.
Завдання 14: Швидка валідація MTU/PMTU (не гадати, а виміряти)
cr0x@server:~$ ping -c 3 -M do -s 1372 10.99.10.25
PING 10.99.10.25 (10.99.10.25) 1372(1400) bytes of data.
1380 bytes from 10.99.10.25: icmp_seq=1 ttl=62 time=31.2 ms
1380 bytes from 10.99.10.25: icmp_seq=2 ttl=62 time=30.9 ms
1380 bytes from 10.99.10.25: icmp_seq=3 ttl=62 time=31.0 ms
--- 10.99.10.25 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
Значення: якщо це проходить на реалістичному розмірі, проблем з фрагментацією менше.
Якщо воно падає з «Message too long», десь MTU занадто велике.
Рішення: підкоригуйте MTU на wg0 (або дайте wg-quick його встановити), особливо через PPPoE, LTE або вкладені тунелі.
Завдання 15: Підтвердіть маршрути на віддаленому боці (бо зворотні шляхи існують)
cr0x@server:~$ ssh ops@remote-gw 'ip route show | grep 10.40.0.0/24'
10.40.0.0/24 dev wg0 scope link
Значення: віддалий бік знає, що 10.40.0.0/24 досяжний через wg0, тож відповіді можуть повернутися.
Якщо віддалий — не endpoint, а маршрутизатор у LAN за ним, вам потрібні маршрути й там.
Рішення: якщо зворотний маршрут відсутній — додайте його (переважно) або використовуйте NAT (в останню чергу), залежно від потреб у безпеці і спостережуваності.
Вибір peer і пастка «найдовший префікс перемагає»
Вибір peer у WireGuard детермінований і простий: він знаходить peer з записом AllowedIPs,
що найкраще співпадає з IP призначення, використовуючи longest-prefix match. Це фактично як працюють таблиці маршрутів.
Простота — це добре, поки ви випадково не створите політику, якої не хотіли.
Що відбувається при перекриттях
Уявіть, що у вас:
- Peer A:
AllowedIPs = 10.0.0.0/8(«catch-all для корпоративної мережі») - Peer B:
AllowedIPs = 10.42.0.0/16(специфічний site-to-site)
Трафік до 10.42.x.y іде до Peer B. Трафік до 10.99.x.y — до Peer A.
Це нормально, якщо так задумано. Це також причина інцидентів «чому продакшен-трафік пішов у лаб VPN», коли хтось копіює конфіг.
Перекриття також впливають на вхідну валідацію: якщо Peer B відправляє пакет з джерелом 10.99.1.10,
але Peer B не має 10.99.0.0/16 у своєму AllowedIPs, отримувач відкидає пакет.
Тому перекриття можуть спричиняти як вихідне спрямування, так і вхідні скидання залежно від того, хто у що вірить.
Не трактуйте AllowedIPs як «рекламовані маршрути», якщо ви не будуєте систему навколо цього
У деяких VPN-системах control plane динамічно розповсюджує маршрути. WireGuard цього не робить.
Якщо ви хочете чогось, схожого на розподіл маршрутів, треба будувати це самим (через управління конфігами, шаблонізацію
або власний контролер) і тоді обробляти перекриття як першокласну проблему.
Прагматичне правило: уникайте перекриттів, якщо ви не робите це навмисно і не можете пояснити причину в одному реченні.
Якщо не можете — ви накопичуєте майбутні відмови.
Розділений тунель проти повного тунелю: що змінюється і що ламається
Розділений тунель: лише певні підмережі йдуть через wg0
Це здоровий за замовчуванням підхід для більшості корпоративних і site-to-site випадків. Приклад наміру:
«Лише 10.99.0.0/16 має йти через WireGuard».
Шаблон конфігурації:
- AllowedIPs клієнта включає лише віддалені приватні діапазони і, можливо, IP сервера у wg.
- Таблиця маршрутів ядра для тих діапазонів вказує на wg0.
- Дефолтний маршрут залишається на звичайній мережі.
Режими відмови:
- Віддалений використовує DNS-імена, що резолвляться у публічні IP; трафік іде через дефолтний маршрут, а не тунель.
- AAAA-записи IPv6 обходять ваш IPv4-тільки тунель.
- Додаток «прикріплює» IPи; ваше припущення «лише підмережа X» виявляється неправильним щодо розгортання сервісу.
Повний тунель: маршрут інтернету через VPN
Це корисно для незахищених мереж, контролю egress або послідовного джерела IP. Але саме тут
плутанина з AllowedIPs стає дорогою.
Шаблон конфігурації:
- AllowedIPs клієнта включає
0.0.0.0/0і зазвичай::/0. - Застосовується policy routing так, щоб IP endpoint-а був винятком і система не тунелювала керуючий трафік у тунель.
- Сервер/шлюз має NAT або маршрутувати трафік клієнта далі.
Режими відмови:
- DNS ламається: ваш резолвер доступний лише через тунель, але тунель залежить від DNS для знаходження endpoint-а.
- Endpoint змінює IP; ваш виняток маршруту вказує на вчорашню адресу; тунель вмирає під час перепідключення.
- Тунелюється лише IPv4, IPv6 тече назовні або потрапляє в чорну діру, браузери отримують «випадкові» тайм-аути.
Жарт №2: Повні тунелі VPN — як переїзд: ви дізнаєтесь, скільки в вас речей, коли мусите все це маршрутизувати.
Три корпоративні історії з реального життя
Інцидент: неправильне припущення, що «AllowedIPs — це ACL»
Середня компанія розгорнула WireGuard для доступу підрядників. Дизайн був простий: підрядники мають доступ до кількох внутрішніх
сервісів (Git-сервер, система трекінгу, кеш збірки). Команда безпеки наполягла: «Дозволити лише ці IP-и».
Мережна команда погодилась і налаштувала клієнтські конфіги з вузьким списком AllowedIPs: кілька /32 для сервісів.
Тунель піднявся. Handshake виглядав добре. Підрядники все одно не могли стабільно дістатись сервісів. Гірше, відмови були переривчасті,
що найдорогоцінніше, бо всі витрачають час на доказ, що не вони винні.
On-call SRE перевірив статистику WireGuard: трафік виходив із клієнта, приходив на сервер, а потім… нічого.
Корінь проблеми був тонким, але передбачуваним: внутрішні сервіси були за load balancer-ами і іноді відповідали з інших IP-адрес.
Підрядники підключалися до VIP (одного з /32), але наступні підключення переадресовувались на бекенди з IP-адресами, яких не було в AllowedIPs.
Ядро клієнта маршрутизувало ці бекенд-запити через звичайний інтерфейс, а не через wg0. З погляду сервера, ці пакети взагалі не приходили. З погляду користувача — «VPN нестабільний».
Виправлення не було «розширити все до 0.0.0.0/0». Виправлення — вирівняти наміри маршрутизації з топологією сервісу:
вони перейшли від per-host /32 до конкретних внутрішніх підмереж, що фактично містили load balancer і бекенди, плюс зафіксували DNS
для тих сервісів на внутрішні адреси, доступні через тунель. Вони також задокументували правило: AllowedIPs керує маршрутизацією,
а не авторизацією. Авторизацією мають займатися фаєрволи.
Урок: якщо ви намагаєтесь використати AllowedIPs як межу безпеки, ви створите ненадійну межу. Помістіть контроль доступу туди, де йому місце: фаєрволи, identity-aware proxy і автентифікація сервісів. Нехай VPN буде транспортом.
Оптимізація, що повернулась бумерангом: «спростимо» шляхом згортання AllowedIPs
Інша організація мала десятки site-to-site peer-ів, що йшли в центральний хаб. Кожен peer мав акуратно обмежені AllowedIPs:
сайт A мав 10.10.0.0/16, сайт B мав 10.20.0.0/16 і т.д. Працювало, в основному.
Потім хтось запропонував «прибирання»: «Давайте просто поставимо 10.0.0.0/8 для кожного сайту, щоб не оновлювати конфіги при появі нових підмереж».
Зміна пройшла рев’ю, бо звучала зручно. Вона також створила перекриття AllowedIPs усюди.
WireGuard потім робив детерміновані вибори: який би peer не мав найспецифічнішого маршруту для даного призначення — той і вигравав, але тепер багато peer-ів мали однакову специфічність.
Вибір хаба став чутливим до порядку конфігів і часу оновлень.
Інцидент, що стався, не був повним відключенням. Він був гірший: деякі підмережі час від часу маршрутизувались не туди, куди треба, і firewall чужого сайту їх відкидав.
Додатки, що робили ретраї — «інколи працювали». Моніторинг світнув, але в патернах, що виглядали як проблеми з латентністю.
Мережна команда провела дні у погоні за фантомною заторністю.
Відкат не був миттєвим, бо деякі нові сайти вже розгорталися, припускаючи «спрощену» конфіг, і їхня маршрутизація на неї покладалася.
Остаточне виправлення — відновити унікальну власність префіксів: кожен сайт отримав явні префікси, керовані через infrastructure-as-code.
«Оптимізація» обійшлася дорожче за ручну роботу спочатку.
Нудна, але правильна практика, що врятувала день: явні тест маршрути в CI
Найстійкіша команда, з якою я працював, ставилася до конфігів WireGuard як до коду, а не як до артізанських сніжинок.
Кожна зміна AllowedIPs або endpoint йшла через pipeline, який виконував невеликий набір перевірок маршрутів.
Нічого вишуканого. Просто невблаганно.
Вони мали тест-харнес, що піднімав легку мережеву namespace-середу, застосовував запропонований конфіг і запускав
ip route get перевірки для набору критичних призначень. Він також інспектував wg show allowed-ips на предмет перекриттів.
Якщо новий префікс перекривав існуючий без затвердженого винятку, pipeline падав.
Одного п’ятничного дня добре намірений інженер випадково вніс 10.0.0.0/8 замість 10.80.0.0/16.
На типічній команді це стало б інцидентом на вихідні. Тут CI відхилив зміну одразу з читабельним diff-ом: «Перекриття з peer-ом X; перенаправив би трафік для цих призначень».
Нічого героїчного не трапилось. Pager не задзвонив. Ось у чому суть.
Практика не була гламурною, але робила зміни маршрутизації нудними — а нудне — це найбільш недооцінена властивість у продакшн-системах.
Одна цитата досі актуальна: Надія — не стратегія
— генерал Гордон Р. Салліван.
Поширені помилки: симптом → причина → виправлення
1) «Handshake працює, але я не можу пропінгувати нічого за peer-ом»
Симптом: wg show показує свіжий handshake; ping до віддалених LAN-хостів не проходить.
Причина: відсутній форвардинг на віддаленому шлюзі або відсутній зворотний маршрут від віддалого LAN до вашої VPN-підмережі.
Виправлення: увімкніть IP forwarding на шлюзі і додайте маршрут на віддалому LAN-маршрутизаторі, що вказує вашу VPN-підмережу через WireGuard-шлюз. Використовуйте NAT лише якщо немає іншого виходу.
2) «Трафік все ще йде через мій звичайний інтернет» (сюрприз розділеного тунелю)
Симптом: очікували, що внутрішній сервіс піде через wg0, але ip route get показує dev eth0 (або Wi‑Fi).
Причина: відсутній маршрут ядра для призначення або IP призначення не входить у AllowedIPs жодного peer-а.
Виправлення: переконайтесь, що префікси призначення є в AllowedIPs peer-а і що маршрути існують (через wg-quick або вручну ip route add).
3) «Увімкнув повний тунель, і тепер тунель не піднімається»
Симптом: після встановлення AllowedIPs = 0.0.0.0/0 handshake падає або флапає.
Причина: трафік до endpoint-а маршрутизують у wg0 (тунелювання тунелю), часто через відсутність або неправильність виняткового маршруту для endpoint-а.
Виправлення: використовуйте підхід wg-quick з policy routing або додайте явний маршрут для IP endpoint-а через фізичний шлюз. Уникайте hostname-ів endpoint-а, які змінюються без обробки оновлень.
4) «Деякі підмережі працюють, а одна чомусь йде до неправильного peer-а»
Симптом: трафік до 10.99.10.0/24 йде кудись несподівано; інші 10.99.x мережі поводяться інакше.
Причина: перекриття AllowedIPs з довшим префіксом, що обирає іншого peer-а.
Виправлення: приберіть перекриття або зробіть їх навмисними і задокументуйте; перевірте з wg show allowed-ips.
5) «Працює для IPv4, але IPv6 зламаний (або просочується)»
Симптом: IPv4 дістає внутрішні ресурси, але браузери зависають на деяких сайтах; ip -6 route get показує вихід не через VPN.
Причина: ви тунелювали 0.0.0.0/0, але не ::/0, або DNS повертає AAAA, що йде іншим шляхом.
Виправлення: або повноцінно підтримуйте IPv6 через тунель (AllowedIPs + маршрути + фаєрвол), або явно відключіть/помістіть IPv6 у чорну діру на цьому хості при роботі через VPN.
6) «Пакети входять у wg0, але ніколи не приходять відповіді»
Симптом: tcpdump на wg0 показує вихідні пакети; немає вхідних відповідей.
Причина: віддалений бік відкидає вхідні, бо IP джерела не в його AllowedIPs (перевірка проти підробки), або віддалий фаєрвол їх блокує.
Виправлення: переконайтесь, що AllowedIPs віддаленого peer-а включає ваш діапазон джерела. Потім перевірте, що віддалий фаєрвол дозволяє трафік із VPN-підмережі.
7) «Все повільно, особливо великі завантаження»
Симптом: маленькі ping-и проходять; великі передачі гальмують або зависають.
Причина: MTU/PMTU mismatch призводить до втрати фрагментації або блокування ICMP «fragmentation needed».
Виправлення: зменшіть MTU на wg0; тестуйте з ping -M do; переконайтесь, що ICMP не блокується на шляху.
8) «Після перезавантаження мережі маршрути неправильні, поки я не перезапущу wg0»
Симптом: WireGuard увімкнений, але маршрути ядра/правила політики відсутні або змінені після змін лінків.
Причина: конфліктуючі менеджери мережі (NetworkManager, systemd-networkd, кастомні скрипти) перезаписують маршрути і правила.
Виправлення: оберіть одного автора. Якщо ви використовуєте wg-quick, упевніться, що саме він застосовує правила; інакше керуйте маршрутами явно у вашому стеку мережі.
Чеклісти / покроковий план
Чекліст A: Побудуйте коректний розділений тунель (site-to-site або внутрішній доступ)
- Визначте реальні префікси призначень (підмережі, а не бажані /32). Включіть load balancer-и, бекенди і DNS-резолвери при потребі.
- Встановіть AllowedIPs peer-а на клієнті точно для цих префіксів. Тримайте їх максимально без перекриттів.
- Підніміть тунель за допомогою wg-quick і підтвердіть наявність маршрутів у
ip route. - Використайте
ip route getщоб підтвердити рішення ядра для кожного критичного призначення. - Перевірте зворотні маршрути на віддалому боці для підмережі клієнта (або налаштуйте NAT свідомо).
- Задокументуйте власність: який peer «володіє» якими префіксами. Трактуйте це як IPAM, бо фактично так і є.
Чекліст B: Побудуйте коректний повний тунель (egress інтернет через WireGuard)
- Вирішіть: лише IPv4 чи dual-stack. Якщо ви не тунелюватимете IPv6, явно відключіть його або блокайте, щоб уникнути витоків і дивних зависань.
- Встановіть AllowedIPs на дефолтні маршрути:
0.0.0.0/0і зазвичай::/0. - Підтвердіть наявність policy routing:
ip ruleі виділена таблиця маршрутів з винятком для endpoint-а. - Підтвердіть серверний forwarding та NAT, якщо сервер надає egress.
- Перевірте поведінку DNS: упевніться, що резолвери доступні після підняття тунелю та перед його зниженням при перепідключенні.
- Проганяйте PMTU тести і явно задавайте MTU, якщо середовище змінне (LTE, PPPoE, вкладені VPN-и).
Чекліст C: Налаштування hub-а з багатьма peer-ами без сюрпризів
- За замовчуванням робіть AllowedIPs без перекриттів. Перекриття вимагають явної дизайнерської нотатки і тестів.
- Використовуйте управління конфігами, щоб генерувати конфіги послідовно. Люди копіюють/вставляють помилки швидше, ніж виправляють їх.
- Запускайте автоматичне виявлення перекриттів, використовуючи валідацію виводу
wg show allowed-ips. - Тестуйте вибір маршруту за допомогою
ip route getі захоплення пакетів для щонайменше однієї IP з кожного префікса. - Плануйте досяжність endpoint-ів (статичні IP, стабільний DNS або явні винятки маршрутів для endpoint-ів).
Питання та відповіді
1) Чи є AllowedIPs фаєрволом?
Ні. Він впливає на вибір вихідного peer-а і валідує вхідні IP-адреси джерела. Використовуйте правила фаєрволу для авторизації.
Якщо ви «захищаєте» доступ лише звуженням AllowedIPs, ви в основному зламаєте маршрутизацію.
2) Чому мій інтерфейс WireGuard має IP, якого немає в AllowedIPs?
Адреси інтерфейсу (Address =) і AllowedIPs — різні концепти. Адреса інтерфейсу — це IP, який локальний хост використовує як джерело.
AllowedIPs — це те, що кожен peer заявляє для маршрутизації і валідації джерела. Вони мають бути сумісні, але це не одне й те саме керування.
3) Чи потрібно додавати маршрути вручну, коли я змінюю AllowedIPs?
Якщо ви використовуєте wg напряму: так, бо він не торкається маршрутів. Якщо ви використовуєте wg-quick: він додасть маршрути при up,
але зміна AllowedIPs на льоту може не оновити маршрути ядра так, як ви думаєте. Обробляйте зміни як такі, що потребують контрольованого повторного застосування.
4) Чому у мене «handshake але немає трафіку»?
Бо handshake лише доводить, що UDP і ключі працюють. Трафік все одно може падати через відсутність маршрутів ядра, відсутній форвардинг, відсутній NAT,
віддалений фаєрвол або валідацію джерела AllowedIPs на приймаючому боці.
5) Що робиться, якщо два peer-а мають однаковий префікс AllowedIPs?
Longest prefix match обирає найспецифічніший префікс. Якщо специфічність рівна, поведінка залежить від внутрішнього порядку і може бути нестабільною під час оновлень.
Не покладайтеся на нічию; зробіть власність явною.
6) Чи ставити 0.0.0.0/0 в AllowedIPs на сервері?
Зазвичай ні. На «сервері», що приймає багато клієнтів, ви зазвичай даєте кожному клієнту /32 (і, можливо, підмережі, що належать клієнту).
Додавання 0.0.0.0/0 в запис сервера для клієнта каже серверу надсилати весь трафік цьому клієнту. Це рідко бажано.
7) Чому додавання ::/0 ламає речі, навіть коли IPv6 «не використовується»?
Бо додатки активно використовують IPv6, якщо воно існує. Якщо ви маршрутизуєте IPv6 через тунель, але не надаєте IPv6 форвардинг/DNS/egress на віддаленому боці,
ви створюєте чорну діру. Приймайте рішення свідомо: повністю підтримувати або відключати.
8) Чи можу я використовувати AllowedIPs для «тільки ці сервіси йдуть через VPN»?
Лише якщо ці сервіси чітко відповідають IP-префіксам, якими ви керуєте. Якщо сервіс використовує CDN, динамічні бекенди або редиректи,
IP-и призначення змінюватимуться і ваше намір маршрутизації порушиться. У таких випадках використовуйте контролі на рівні додатка і трактуйте VPN як транспорт.
9) Чому ping працює, а TCP ні?
MTU і PMTU проблеми — класична відповідь. ICMP невеликий; TCP з MSS/PMTU проблемами може зависати. Також перевірте stateful фаєрволи і асиметричну маршрутизацію.
Підтвердіть тестами PMTU і захопленням пакетів.
10) Чи «поганий» wg-quick?
Ні. Це надійний інструмент. Але це також обгортка, що змінює маршрути, правила, DNS і іноді стан фаєрволу.
У продакшні або стандартизуйтесь на ньому і тестуйте його поведінку, або замініть його явною оркестрацією.
Найгірша опція — «ми думаємо, що використовуємо wg-quick, але інші інструменти його відмотують».
Висновок: практичні кроки далі
Якщо WireGuard-трафік не йде туди, куди ви очікуєте, перестаньте пильнувати handshake.
Сприймайте AllowedIPs як вхід для політики маршрутизації, а потім перевіряйте шари системи в порядку:
рішення ядра про маршрут, вибір peer у WireGuard і віддалений зворотний шлях.
Наступні кроки, які ви можете зробити сьогодні:
- Виберіть одне невдале призначення і запустіть
ip route get(таip -6 route get) для нього. Повірте виводу. - Запустіть
wg show allowed-ipsі шукайте перекриття. Якщо знайдете непідтверджене — приберіть його. - На gateway peer-ах перевірте форвардинг і зворотний маршрут. Виправляйте це перед тим, як чіпати MTU або DNS.
- Вирішіть розділений чи повний тунель явно, включаючи поведінку IPv6. Напівзаходи породжують повні відмови.
- Запишіть власність префіксів. Серйозно. Ваше майбутнє «чому прод так пішов у стейджинг» я вам дякуватиме.