Одна програма через WireGuard: прив'язка — це не маршрутизація
Прив'язка програми до wg0 задає лише її адресу джерела. Ядру все ще потрібні таблиця з маршрутами тунелю і правило, яке надсилає це джерело туди.
Налаштування виглядає як перемикач для окремої програми, тому люди прив'язують клієнт до інтерфейсу WireGuard і чекають, що тунель використовуватиме лише цей клієнт. Насправді прив'язка вибирає адресу джерела. Далі ядро шукає маршрут до призначення, знаходить звичайний маршрут за замовчуванням і надсилає пакет через фізичний канал із прикріпленою адресою тунелю. Задум виконує лише маршрут, вибраний для цієї адреси джерела.
Механізм має дві половини: таблицю маршрутизації, якою має користуватися лише трафік тунелю, і правило, яке надсилає туди відповідне джерело.
Прив'язка задає адресу джерела і нічого більше
Linux вибирає вихідний інтерфейс, шукаючи призначення в таблиці маршрутизації, після того як правила політики отримали можливість перенаправити цей пошук. Правила можуть вибирати за адресою джерела, міткою фаєрвола, вхідним інтерфейсом, UID або навіть портом; кожне — це інший спосіб сказати цей трафік, а не весь. Конфігурація VPN-клієнта вибирає таблицю, у якій зберігаються маршрути тунелю; правило вибирає, якому трафіку дозволено до неї звертатися. Сама лише прив'язка не дає ні того, ні іншого.
Саме тому збій виглядає так дивно. Програма безперечно використовує адресу тунелю, тунель безперечно працює, а трафік безперечно виходить через неправильний інтерфейс. Кожне твердження істинне, і разом вони описують систему, у якій ніщо не пов'язало адресу з маршрутом.
Дайте тунелю власну таблицю
wg-quick встановлює маршрути туди, куди вказує профіль. Додавання Table = 40 під [Interface] переносить їх до таблиці 40 замість основної таблиці. Підніміть інтерфейс і переконайтеся, що вони прибули: ip -4 route ls table 40 має показати маршрути тунелю, а ip -4 route get 10.66.127.142 показує, як ядро зараз розв'язує один із них. Коли маршрути в окремій таблиці, основна таблиця продовжує працювати для всього іншого.
Зробіть те саме для IPv6, замість того щоб зіставляти широкий префікс. ip -6 rule add from fd00::2/128 lookup 40 закріплює одну адресу; fc00::/8 може зіткнутися з іншими інтерфейсами на хості й дати правило, яке збігається з більшим, ніж задумано.
Додайте правило джерела і стежте за лічильниками
Потім з'єднайте половини: ip -4 rule add from 10.66.127.142/32 lookup 40, з тією самою адресою, прикріпленою до інтерфейсу WireGuard. Помістіть обидва правила в PostUp, щоб перепідключення їх відновило. Перевірте справжнім запитом, а не налаштуванням: ip route get 1.1.1.1 from 10.66.127.142 має назвати пристрій тунелю, а curl --interface wg0 ifconfig.me має повідомити адресу тунелю, а не домашню.
Якщо все ще не працює, прочитайте ip -s rule. Правило, яке існує, але ніколи не збігалося, показує нульові лічильники, і це відрізняє хибне правило від відсутності правила. Одна ця команда зберігає багато здогадів про те, чи проігнорувала програма своє налаштування інтерфейсу.
Зворотний випадок: тримайте SSH поза тунелем
Протилежна проблема така ж поширена: wg-quick володіє маршрутом за замовчуванням, і SSH-сесія до сервера обривається посередині. Його правила стоять на низькому номері пріоритету, тому виняток має оцінюватися раніше за них. ip rule add dport 22 ipproto tcp table main priority 123 робить це в новому iproute2; там, де селектор недоступний, позначте потік у nftables і зіставляйте мітку.
Перевірте й шлях відповіді. SSH-сесія, яка встановлюється, а потім замовкає, зазвичай має асиметричну маршрутизацію: один напрямок іде через тунель, інший — через фізичний інтерфейс. ip route get для адреси сервера підкаже, куди піде відповідь.
Запишіть номер таблиці, правило джерела і виняток у сам профіль. Усі три належать до одного проєкту і редагуватимуться разом.