Вкладений WireGuard: середній стрибок не може відповісти на власний порт
Коли середній стрибок надсилає все в один тунель, відповіді на його вхідну службу виходять неправильним шляхом. Правило порту джерела на інтерфейсі це виправляє.
Три стрибки WireGuard досягають мережі без публічної адреси, і тоді середня машина перестає відповідати на порті, на якому мала слухати. Тунель далі все ще працює, віддалений хост все ще відповідає, і лише сам середній сервер замовк ззовні. Ця комбінація збиває з пантелику саме тому, що вона створена ним самим: середній стрибок підкоряється власній таблиці маршрутизації, а таблиця надсилає його відповіді не в той бік.
Ланцюг, який працює, доки ви не заговорите із середньою скринькою
Візьмімо ілюстративний ланцюг: клієнт A досягає сервера B через UDP 500, а B іде далі до сервера C через UDP 500. C приймає трафік лише з адреси свого єдиного партнера, що робить його листком і зберігає його поведінку простою. B не листок, і все цікаве в цій несправності відбувається саме там.
Ця різниця важлива, бо ролі листка й середини тягнуть у протилежні боки. Листок може надсилати все в один тунель без наслідків, оскільки від нього більше нічого не залежить. Середній стрибок одночасно є кінцевою точкою для клієнтів і клієнтом у бік далекої сторони, і ці дві роботи хочуть різної маршрутизації.
Чому відповідь виходить через тунель
На B інтерфейс, повернутий до C, несе AllowedIPs = 0.0.0.0/0 разом з відповідним маршрутом за замовчуванням. Тому кожен пакет, який створює B, потрапляє в цей тунель, якщо щось конкретніше не отримає пріоритет.
Пакет від A, адресований B, надходить і приймається, бо B є призначенням і локальна доставка відбувається першою. Відповідь — цілком новий локально створений пакет, спрямований на публічну адресу A, і це призначення збігається з маршрутом за замовчуванням у тунель. C отримує його, не володіє цією адресою і відкидає його.
Симптом читається як проблема міжмережевого екрана або невідповідність ключів, і тут не є правдою ні те, ні інше. Перехоплення на тунелі в бік C показує, що відповіді йдуть не в той бік. Це одне спостереження економить більше часу, ніж будь-яка кількість читання конфігурації, тож запишіть його, перш ніж почати змінювати ключі. Будь-хто, хто не бачив перехоплення, вважатиме, що зламано сам тунель.
Вузьке виправлення: правила політики, що збігаються з портом джерела
Відповіді зі слухаючого сокета B виходять із портом джерела 500. Правило політики може збігтися з цим і повернути ці пакети до звичайної таблиці:
ip rule add sport 500 table main priority 100
ip rule add sport 8080 table main priority 101
Правила політики переглядаються в порядку пріоритету, а головна таблиця розташована на 32766, тому правило на 100 запитується раніше, ніж маршрут за замовчуванням тунелю матиме що сказати. Додайте один запис для кожного порту, який має залишатися доступним ззовні, і більше нічого, щоб перенаправлений трафік і далі використовував тунель, для якого його побудовано.
Знайте межу цього прийому. Селектор sport збігається з локально створеними пакетами, що покриває саме випадок відповіді й навмисно не покриває трафік, який B лише перенаправляє за когось іншого. Служба, розміщена за B, усе ще потребує власної відповіді, а твердження протилежного дає виправлення, яке працює для першої служби й зазнає невдачі для другої.
Дайте правилу власника
Правило, набране в командному рядку, зникає наступного разу, коли інтерфейс перебудовується. У налаштуванні WireGuard помістіть його туди, де інтерфейс створюється й видаляється:
PostUp = ip rule add sport 500 table main priority 100
PostDown = ip rule del sport 500 table main priority 100
Якщо інтерфейсом володіє NetworkManager, хук дистрибутива або точка входу контейнера, пара належить тому власнику, а не файлу, відредагованому вручну. Перевіряйте чинні правила після повторного підключення, а не лише після першого запуску, бо перший запуск — це випадок, який завжди працює.
Порівняйте альтернативи чесно
Правило порту джерела — один із чотирьох виходів, і найвужчий із них. У нього є перевага: воно взагалі не торкається політики тунелю, тому зазвичай переживає пізніші зміни. Вузькі виправлення також гучно ламаються, коли перестають діяти, тоді як широкий виняток продовжує тихо працювати ще довго після того, як причина зникла. Ця різниця важливіша за елегантність, коли через два роки машиною володіє хтось інший.
| Approach | What it costs | Where it breaks |
|---|---|---|
| Source-port policy rule | One rule per exposed port | Anything B forwards rather than originates |
| Destination exception route | A route pinned to specific client addresses | Clients whose address changes |
| Stop full-tunnelling on B | Loses the catch-all path toward C | Designs that depend on it |
| Split into two tunnels | More interfaces and more policy | Nothing, at the cost of complexity |
Перевірте з обох кінців після вибору. З боку A почніть з’єднання до порту B і потримайте його зайнятим деякий час. На B змусьте ядро обґрунтувати шлях відповіді чимось на кшталт ip route get <address-of-A> sport 500 і підтвердьте, що воно називає інтерфейс WAN, а не тунель. Після цього перевірте, що щось за C усе ще відповідає через ланцюг, що доводить: виняток залишився вузьким, а не перетворився на обхід. Нарешті запишіть порти в проєктних нотатках, інакше наступний оператор виявить це знову на іншій службі.