Шлюзи віртуальних приватних мереж (VPN), що працюють у публічній мережевій інфраструктурі, постійно приймають неавтентифікований трафік. За звичайних умов криптографічні тунелі перевіряють клієнтів за допомогою рукостискань з відкритим ключем. Однак під час розподілених атак типу «відмова в обслуговуванні» (DDoS) зловмисники експлуатують асиметрію обчислювальних витрат: генерація мільйонів підроблених вхідних UDP-пакетів потребує мізерних ресурсів, тоді як сервер-одержувач змушений виконувати складні криптографічні операції та виділяти пам'ять ядра для збереження стану сесій.
Традиційні рішення, як-от OpenVPN чи IPsec IKEv2, у таких умовах зазнають значних труднощів: вони виділяють структури керування або буфери асоціацій безпеки (SA) вже після отримання першого запиту. WireGuard повністю усуває цей вектор загрози. Завдяки дотриманню суворої моделі відсутності стану (stateless) до завершення рукостискання та застосуванню дворівневої системи кодів автентифікації повідомлень (mac1 і mac2) із ротацією криптографічних cookie, система надійно захищає серверні ресурси від перевантаження.
У ядрі Linux цей механізм реалізовано у файлі drivers/net/wireguard/cookie.c. Дослідження його криптографічної моделі демонструє, як швидкісні тунелі зберігають стабільність без виділення динамічної пам'яті для неперевірених джерел.
Асиметрія рукостискання та стійкість без збереження стану
Шаблон Noise IK, інтегрований у WireGuard, вимагає взаємної автентифікації через скалярне множення на еліптичній кривій Curve25519 (X25519). Обчислення точки на кривій вимагає значно більше тактів процесора, ніж звичайна маршрутизація пакетів. Якби сервер обробляв кожен пакет Type 1: Handshake Initiation і відразу генерував спільний секрет, зловмисник зі спуфінгом IP-адрес міг би легко завантажити всі ядра процесора, блокуючи трафік легітимних з'єднань.
Архітектура WireGuard розподіляє валідацію пакетів на послідовні рубежі захисту:
[ Вхідний пакет ініціації ]
│
▼
Перевірка MAC1 (Статичний ключ)
├─ Не збігається ──► Безмовне скидання (Zero Response)
└─ Дійсний
│
▼
Сервер перевантажений? (ratelimiter.c)
├─ Ні ──► Обчислення Curve25519 ScalarMult
└─ Так
│
▼
Перевірка MAC2 (Ротаційний Cookie)
├─ Дійсний ──► Обчислення Curve25519 ScalarMult
└─ Не збігається ──► Відправлення Type 3: Cookie Reply (Stateless)
У штатному режимі, коли навантаження не перевищує порогових значень ratelimiter.c, сервер перевіряє mac1 і переходить до криптографічного обміну. У разі стрибка трафіку активується другий рівень фільтрації, що вимагає наявності коректного токена mac2, згенерованого на базі виданого сервером cookie.
Структура дворівневої перевірки MAC (mac1 та mac2)
Кожен пакет ініціації WireGuard містить два 16-байтові поля в кінці структури: mac1 (байти 116–131) та mac2 (байти 132–147). Вони працюють як взаємодоповнюючі фільтри:
- Фільтр
mac1: Обчислюється якBLAKE2s-128(key = Hash(Label-Mac1 || Responder_PublicKey), message = packet[0..115]). Оскільки ключ створено на базі відкритого ключа сервера, лише клієнти з правильними налаштуваннями можуть створити дійсний тег. Пакети мережевих сканерів скидаються без відповіді, блокуючи виявлення активних портів. - Фільтр
mac2: Обчислюється якBLAKE2s-128(key = Cookie, message = packet[0..131]). Полеmac2охоплює весь пакет, включно з верифікованим блокомmac1. Ключем слугує тимчасовий криптографічний cookie, виданий сервером виключно для підтверджених IP-адрес.
За стандартних умов клієнт передає поле mac2, заповнене шістнадцятьма нульовими байтами (0x00). Коли сервер переходить у захисний режим, пакети з нульовим або некоректним mac2 не допускаються до виконання важких обчислень. Замість виділення пам'яті для сесії сервер запускає протокол виклику та відповіді без збереження стану.
Робота cookie.c в ядрі: виклик та відповідь без стану
Коли модуль ratelimiter.c сигналізує про перевантаження, обробку перебирає функція wg_cookie_checker_create_payload(). Ядро Linux генерує 64-байтовий пакет Type 3: Handshake Cookie Reply:
┌────────────────────────────────────────────────────────┐
│ Тип: 3 (1 байт) │ Резерв: 0 (3 байти) │
├────────────────────────────────────────────────────────┤
│ Індекс одержувача: sender_index клієнта (4 байти) │
├────────────────────────────────────────────────────────┤
│ Nonce XChaCha20: Згенеровано CSPRNG ядра (24 байти) │
├────────────────────────────────────────────────────────┤
│ Зашифрований токен Cookie: XChaCha20-Poly1305 (32 B) │
│ - 16 байтів відкритого cookie │
│ - 16 байтів тегу автентифікації Poly1305 │
└────────────────────────────────────────────────────────┘
Формування цієї відповіді вимагає мінімальної кількості тактів процесора і не задіює динамічне виділення пам'яті (kmalloc = 0). Пакет повертає клієнту його sender_index, щоб зіставити відповідь із очікуваним рукостисканням, а дані cookie шифруються за допомогою XChaCha20-Poly1305.
Симетричний ключ шифрування обчислюється детерміновано:
Cookie_Encryption_Key = BLAKE2s-256(
key = Hash(Label-Cookie || Responder_PublicKey),
message = Initiator_MAC1
)
Оскільки Initiator_MAC1 уже успішно перевірено сервером, це значення діє як криптографічна сіль для поточної спроби з'єднання, виключаючи попередній підбір ключів зловмисниками.
Ротація ентропії секрету та підтвердження володіння IP
Ключовою властивістю безпеки WireGuard є підтвердження приналежності IP-адреси клієнта без збереження записів сесії. Сервер формує cookie на основі локального 32-байтового секрету (cookie_checker->secret), згенерованого функцією ядра get_random_bytes.
Формула пов'язує мережеву адресу ініціатора з виданим токеном:
Cookie = BLAKE2s-128(
key = cookie_checker->secret,
message = Initiator_IP_Address || Initiator_Port
)
Щоб зловмисники не накопичували дійсні cookie під час тривалих атак, сервер оновлює секрет кожні 120 секунд (COOKIE_SECRET_MAX_AGE = 120).
Для захисту від мережевих затримок модуль cookie.c підтримує два покоління секретів:
secret[0]: Поточний секрет для створення нових cookie та валідації свіжих пакетів.secret[1]: Попередній секрет за минулі 120 секунд, що забезпечує стабільну обробку пакетів під час ротації.
Під час атаки зі спуфінгом IP-адрес сервер надсилає пакети Type 3: Cookie Reply на підроблені адреси за протоколом UDP. Оскільки зловмисник не має доступу до транзитних маршрутів цих адрес, він не може отримати вміст зашифрованого cookie.
Легітимний клієнт після отримання відповіді викликає wg_cookie_message_consume(). Він розшифровує токен, додає його в локальний кеш (peer->latest_cookie), встановлює cookie_birthdate = jiffies та негайно надсилає повторний пакет Handshake Initiation із заповненим полем mac2. Сервер перевіряє mac2 за константний час O(1), підтверджує легітимність IP і переходить до обчислень Curve25519.
Паралелізм без блокувань та архітектура RiceGuard
Висока продуктивність cookie.c зумовлена відсутністю блокувань. Читання секретів та обчислення тегів автентифікації виконуються всередині критичної секції rcu_read_lock(). Відмова від глобальних спінлоків у черзі прийому дозволяє всім ядрам процесора обробляти mac1 і mac2 паралельно.
Вхідний пакет ──► [Черга Rx NIC] ──► Читання RCU ──► Валідація BLAKE2s MAC ──► Воркер ядра
У промислових масштабах мережевий рушій RiceGuard від VPNRice поєднує функціонал cookie.c з технологіями eBPF та XDP (eXpress Data Path). Перевірка дворівневого MAC здійснюється безпосередньо у драйвері мережевої карти до створення буферних структур sk_buff. Це дає змогу нейтралізувати флуд-атаки безпосередньо на фізичному рубежі, зберігаючи мінімальний пінг та приватність передачі даних навіть під екстремальним навантаженням.



