WireGuard: самый элегантный VPN в истории
Привет, параноик! 👋
WireGuard - это не просто ещё один VPN-протокол. Это переосмысление того, каким должен быть сетевой код: ~4000 строк против 400 000 у OpenVPN. Меньше кода = меньше уязвимостей = меньше поверхности атаки.
Но большинство статей про WireGuard - это туториалы “скопируй конфиг и беги”. Сегодня разбираем как он работает изнутри, какие параметры реально влияют на приватность и скорость, и почему Линус Торвальдс назвал его произведением искусства.
1. Почему WireGuard - это инженерный прорыв
Размер имеет значение
Посмотрим на цифры:
Протокол Строк кода Аудит безопасности
────────────────────────────────────────────────────
OpenVPN ~400 000 Сложно, дорого
IPsec/StrongSwan ~200 000 Очень сложно
OpenSSH ~150 000 Регулярно
WireGuard ~4 000 Легко, проводился
────────────────────────────────────────────────────
В 2020 году WireGuard был включён в ядро Linux 5.6. Линус Торвальдс написал в письме:
“Can I just once again state my love for it and hope it gets merged soon? Maybe the code isn’t perfect, but I’ve skimmed it, and compared to the horrors that are OpenVPN and IPSec, it’s a work of art.”
Это не маркетинг. Это архитектурное решение - взять минимум криптографических примитивов, доказанных временем, и больше ничего не добавлять.
Принцип опиниэйтед дизайна
WireGuard не даёт выбора алгоритмов шифрования. И это фича, а не баг:
Компонент Алгоритм Почему
───────────────────────────────────────────────────────────
Обмен ключами Curve25519 Быстро, безопасно
Шифрование ChaCha20-Poly1305 Нет padding oracle
Хеширование BLAKE2s Быстрее SHA-256
Ключевой вывод HKDF RFC 5869
───────────────────────────────────────────────────────────
IPsec предлагает десятки комбинаций алгоритмов - и каждая неправильная конфигурация открывает уязвимость. WireGuard говорит: “У тебя нет выбора, потому что мы уже выбрали лучшее”.
2. Как работает WireGuard изнутри
Handshake: 1-RTT аутентификация
Клиент Сервер
| |
| Initiator Handshake Message |
| - Ephemeral public key (Curve25519) |
| - Encrypted static public key |
| - Encrypted timestamp |
|---------------------------------------->|
| |
| Responder Handshake Message |
| - Ephemeral public key |
| - Empty encrypted payload |
|<----------------------------------------|
| |
| ======= Encrypted Data (ChaCha20) ====>|
| |
Время: 1 round-trip (RTT)
Session keys ротируются каждые 3 минуты или 2^64 пакетов
Что важно: WireGuard использует Noise Protocol Framework - формально верифицированный протокол обмена ключами. Это не самодельная криптография.
Модель идентификации
Вместо сертификатов, CA, PKI - просто пары публичных ключей:
# Каждый peer - это просто публичный ключ
[Peer]
PublicKey = <base64-encoded-curve25519-public-key>
AllowedIPs = 10.0.0.2/32
Сервер знает клиента по публичному ключу. Клиент знает сервер по публичному ключу. Никаких сертификатов, никакого CA, никакого OCSP.
Roaming из коробки
Телефон VPN-сервер
| |
| UDP пакет с IP 192.168.1.10 |
|--------------------------------->|
| | Запоминает endpoint
| Переключение на LTE |
| |
| UDP пакет с IP 10.20.30.40 |
|--------------------------------->|
| | Обновляет endpoint автоматически
|<================================>|
WireGuard не рвёт соединение при смене IP
3. Параметры приватности: что реально важно
PrivateKey и PublicKey
[Interface]
PrivateKey = <ваш-приватный-ключ>
Address = 10.0.0.1/24
Приватный ключ - это всё. Кто владеет им - тот и есть “вы” в сети WireGuard.
# Генерация ключей
wg genkey | tee privatekey | wg pubkey > publickey
# Проверяем права доступа к файлу с приватным ключом
chmod 600 /etc/wireguard/privatekey
Что может пойти не так: если приватный ключ утёк - смените его. Старые сессии при этом не компрометируются (forward secrecy), но злоумышленник сможет подключиться как вы.
DNS: ахиллесова пята большинства VPN
[Interface]
PrivateKey = ...
Address = 10.0.0.1/24
DNS = 1.1.1.1 # ← Этот параметр критичен
Без DNS в конфиге - ваш трафик идёт через VPN, но DNS-запросы могут утекать провайдеру. Это DNS leak.
Без DNS в конфиге:
DNS-запрос → провайдер (утечка!)
Трафик → WireGuard → сервер ✓
С DNS = 10.0.0.53:
DNS-запрос → VPN-туннель → ваш DNS-резолвер ✓
Трафик → WireGuard → сервер ✓
Лучшая практика: поднять собственный DNS (Unbound, AdGuard Home) на VPN-сервере и указать его внутренний IP.
AllowedIPs: маршрутизация трафика
[Peer]
PublicKey = ...
AllowedIPs = 0.0.0.0/0, ::/0 # Весь трафик через VPN (full tunnel)
# Или:
AllowedIPs = 192.168.1.0/24 # Только корпоративная сеть (split tunnel)
Разница для приватности:
Full tunnel (0.0.0.0/0):
Весь трафик → VPN-сервер
Провайдер видит только: IP сервера, объём трафика
Минус: скорость зависит от сервера
Split tunnel (конкретные подсети):
Часть трафика → VPN
Часть трафика → напрямую
Провайдер видит прямые соединения
Плюс: скорость выше для локального трафика
PersistentKeepalive: невидимость vs соединение
[Peer]
PublicKey = ...
PersistentKeepalive = 25 # Отправлять keepalive каждые 25 секунд
По умолчанию WireGuard не отправляет никаких пакетов, когда нет трафика. Это отличная новость для приватности - туннель невидим в пассивном состоянии.
Но есть нюанс: если вы за NAT (роутер, корпоративный firewall), соединение может пропасть. PersistentKeepalive = 25 решает это.
Без PersistentKeepalive:
Нет трафика → нет пакетов → NAT-таблица устаревает → туннель рвётся
Зато: в idle-состоянии вы невидимы
С PersistentKeepalive = 25:
Каждые 25 секунд → 1 UDP пакет → NAT держится
Зато: активность видна по трафику
Вывод: для мобильных устройств и NAT-окружений - ставьте 25. Для сервер-сервер с прямым IP - можно не ставить.
PreSharedKey: дополнительный слой защиты
[Peer]
PublicKey = ...
PresharedKey = <32-байтный-симметричный-ключ> # Опционально
AllowedIPs = 0.0.0.0/0
PostQuantum защита: Curve25519 уязвим к квантовым компьютерам в теории. PresharedKey добавляет симметричный ключ поверх асимметричного обмена - если квантовый компьютер сломает Curve25519, PresharedKey всё ещё защищает трафик.
# Генерация PresharedKey
wg genpsk
Использовать или нет: если паранойя высокая - да. Для обычного использования - опционально.
4. Параметры скорости: что реально влияет
MTU: самый недооценённый параметр
[Interface]
PrivateKey = ...
Address = 10.0.0.1/24
MTU = 1420 # ← Часто забывают
Почему это важно:
Стандартный MTU Ethernet: 1500 байт
WireGuard overhead:
- UDP header: 8 байт
- IP header: 20 байт (IPv4) или 40 байт (IPv6)
- WireGuard header: 32 байта
────────────────────────────────
Итого overhead: 60 байт (IPv4)
Оптимальный MTU для WireGuard поверх IPv4:
1500 - 60 = 1440 байт
С запасом (на туннели поверх туннелей):
MTU = 1380-1420 байт
Симптом неправильного MTU: сайты открываются, но большие файлы не скачиваются. Видео буферизуется. SSH работает, но scp зависает.
# Диагностика MTU
ping -M do -s 1400 <vpn-server-ip>
# Увеличивайте -s пока пакеты не начнут теряться
ListenPort: UDP vs TCP
[Interface]
ListenPort = 51820 # По умолчанию
WireGuard работает только на UDP. Это намеренное решение:
UDP:
✓ Нет overhead на установку соединения
✓ Нет head-of-line blocking
✓ Нет повторных передач на уровне транспорта
✓ WireGuard сам управляет надёжностью
TCP:
✗ Double TCP ack overhead (если ваш VPN поверх TCP)
✗ TCP-over-TCP проблема: retry storms
✗ Higher latency
Если UDP заблокирован (корпоративный файрвол): используйте udp2raw или wstunnel для инкапсуляции WireGuard в TCP/WebSocket. Но это крайний случай.
Шифрование на аппаратном уровне
ChaCha20-Poly1305 специально выбран для программной реализации без аппаратного ускорения:
Процессор без AES-NI (старые ARM):
AES-GCM (OpenVPN): ~200 Mbps
ChaCha20 (WireGuard): ~800 Mbps ← 4x быстрее
Процессор с AES-NI (современные x86/ARM):
AES-GCM (OpenVPN): ~1 Gbps
ChaCha20 (WireGuard): ~1.2 Gbps ← сопоставимо
Вывод: на мобильных устройствах (ARM без AES-NI) WireGuard значительно быстрее OpenVPN. На серверах - примерно одинаково.
5. Полный конфиг с объяснениями
Конфиг клиента
[Interface]
# Приватный ключ - храни в безопасности
PrivateKey = <client-private-key>
# IP адрес клиента в VPN-сети
Address = 10.0.0.2/32
# DNS - критично для предотвращения утечек
DNS = 10.0.0.1
# MTU - оптимизация для скорости
MTU = 1420
# Опционально: выполнить команды при подключении/отключении
# PostUp = echo "WireGuard connected" | logger
# PreDown = echo "WireGuard disconnecting" | logger
[Peer]
# Публичный ключ сервера
PublicKey = <server-public-key>
# Дополнительный слой защиты (опционально)
PresharedKey = <preshared-key>
# Endpoint сервера: IP:порт
Endpoint = vpn.example.com:51820
# 0.0.0.0/0 = весь IPv4 трафик через VPN
# ::/0 = весь IPv6 трафик через VPN
AllowedIPs = 0.0.0.0/0, ::/0
# Keepalive для NAT (если вы за роутером - нужно)
PersistentKeepalive = 25
Конфиг для split tunnel (только рабочая сеть)
[Interface]
PrivateKey = <client-private-key>
Address = 10.0.0.2/32
MTU = 1420
# Без DNS - используем локальный резолвер
[Peer]
PublicKey = <server-public-key>
Endpoint = office-vpn.example.com:51820
# Только корпоративные подсети
AllowedIPs = 192.168.1.0/24, 10.0.0.0/8
# Интернет-трафик идёт напрямую - максимальная скорость
PersistentKeepalive = 25
6. Сравнение с конкурентами
Параметр WireGuard OpenVPN IPsec
──────────────────────────────────────────────────────────
Строк кода ~4 000 ~400 000 ~200 000
Handshake latency 1-RTT 2-RTT 2-RTT
Шифрование ChaCha20 AES/RC4 AES
Аутентификация Public key PKI/PSK PKI/PSK
UDP поддержка Только UDP UDP + TCP UDP
Ядро Linux Да (5.6+) Userspace Встроен
Аудит кода Лёгкий Сложный Очень сложный
Мобильный battery Отличный Плохой Хороший
Настройка Минуты Часы/дни Дни
──────────────────────────────────────────────────────────
7. Диагностика: что делать когда не работает
Типичные проблемы и решения
# Посмотреть статус туннеля
sudo wg show
# Вывод:
# interface: wg0
# public key: ...
# private key: (hidden)
# listening port: 51820
#
# peer: <server-pubkey>
# endpoint: 1.2.3.4:51820
# allowed ips: 0.0.0.0/0
# latest handshake: 23 seconds ago ← Это хороший знак
# transfer: 1.23 GiB received, 456 MiB sent
# Проблема: нет handshake
# Причина: firewall блокирует UDP порт
# Решение:
sudo ufw allow 51820/udp
# Проблема: handshake есть, но трафик не идёт
# Причина: IP forwarding выключен на сервере
# Решение (на сервере):
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p
# Проблема: DNS утечки
# Проверка:
curl https://dnsleaktest.com/test
Мониторинг туннеля в реальном времени
# Смотрим статистику каждую секунду
watch -n 1 sudo wg show
# Или через системный журнал
journalctl -fu wg-quick@wg0
8. Безопасность ключей: не наступи на грабли
Ротация ключей
# Скрипт ротации ключей (запускать через cron)
#!/bin/bash
NEW_PRIVATE=$(wg genkey)
NEW_PUBLIC=$(echo $NEW_PRIVATE | wg pubkey)
# Обновляем конфиг
wg set wg0 private-key <(echo $NEW_PRIVATE)
# Сохраняем новый приватный ключ
echo $NEW_PRIVATE > /etc/wireguard/privatekey
chmod 600 /etc/wireguard/privatekey
echo "New public key: $NEW_PUBLIC"
# Теперь обновите PublicKey у сервера!
Хранение конфига
# Никогда не храни конфиг WireGuard в git без шифрования
# Используй age или GPG:
age -e -r <your-age-public-key> wg0.conf > wg0.conf.age
# Права доступа к конфигу
chmod 600 /etc/wireguard/wg0.conf
chown root:root /etc/wireguard/wg0.conf
Вывод: почему WireGuard - это важно
WireGuard важен не потому что “быстрый VPN”. Он важен потому что доказывает принцип: сложные задачи можно решать простым кодом. 4000 строк, которые проверяемы, понятны и безопасны - лучше 400 000 строк, которые никто не может полностью проверить.
Параметры приватности (в порядке важности):
🔑 PrivateKey - держи в секрете, ротируй регулярно
🌐 DNS - обязательно, иначе утечки
🛡️ AllowedIPs = 0.0.0.0/0 - full tunnel для максимальной приватности
🔒 PresharedKey - для post-quantum параноиков
Параметры скорости:
⚡ MTU = 1420 - устанавливай всегда
📡 UDP - не пытайся заменить TCP без крайней необходимости
🔄 PersistentKeepalive = 25 - для NAT-окружений
Золотое правило:
WireGuard не делает вас анонимным. Он шифрует трафик между вами и сервером. То, что делает сервер с вашим трафиком - другой вопрос. Выбирайте VPN-провайдера так же тщательно, как выбираете врача.
P.S. Используете WireGuard? Поделитесь своим конфигом (без ключей, разумеется) в комментариях! 🚀
# Дополнительные ресурсы:
# - WireGuard Paper: https://www.wireguard.com/papers/wireguard.pdf
# - Noise Protocol Framework: https://noiseprotocol.org/
# - Linux Kernel inclusion: https://lkml.org/lkml/2019/12/8/16