WireGuard: быстрый VPN между Linux-хостами
WireGuard — современный VPN, который живёт прямо в ядре Linux (с версии 5.6). Он использует протокол Noise, криптографию Curve25519, ChaCha20-Poly1305 и BLAKE2s, а его код в десятки раз меньше, чем у IPsec или OpenVPN. Отсюда главные плюсы: простота настройки, высокая скорость и «роуминг» — клиент может менять IP-адрес, и соединение восстановится само.
В отличие от IP-IP, WireGuard шифрует и аутентифицирует весь трафик, поэтому подходит не только для связки сетей, но и для удалённого доступа.
Как работает маршрутизация
У WireGuard нет понятий «клиент» и «сервер» в классическом смысле — это одноранговая сеть (peer-to-peer). Каждый узел имеет пару ключей, а маршрутизация задаётся списком AllowedIPs:
- исходящий пакет шифруется и уходит тому пиру, в чей
AllowedIPsпопадает адрес назначения; - входящий пакет принимается, только если адрес источника входит в
AllowedIPsэтого пира.
Такую схему называют cryptokey routing.
Схема сети
Офис A: 10.0.1.0/24 Офис B: 10.0.2.0/24
| |
[ Router A ] ---------- WireGuard --------- [ Router B ]
WAN: 203.0.113.10 WAN: 198.51.100.20
wg0: 10.77.0.1/24 wg0: 10.77.0.2/24
- Router A: внешний адрес
203.0.113.10, внутренняя сеть10.0.1.0/24. - Router B: внешний адрес
198.51.100.20, внутренняя сеть10.0.2.0/24. - Для туннеля выделим подсеть
10.77.0.0/24.
Что понадобится
- Два Linux-хоста с ядром 5.6+ (модуль
wireguard) или установленныйwireguard-dkms. - Пакет
wireguard-tools(утилитыwgиwg-quick).
apt install wireguard-tools # Debian/Ubuntu
dnf install wireguard-tools # Fedora/RHEL
Шаг 1. Генерируем ключи
На каждом маршрутизаторе создаём приватный ключ и из него — публичный. Приватный ключ никуда не передаём.
На Router A:
umask 077
wg genkey | tee /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key
cat /etc/wireguard/public.key
То же самое на Router B. Сохраните вывод cat .../public.key — эти ключи понадобятся в конфигах напротив.
Шаг 2. Пишем конфигурацию
Файл /etc/wireguard/wg0.conf на Router A:
[Interface]
Address = 10.77.0.1/24
ListenPort = 51820
PrivateKey = <приватный-ключ-A>
[Peer]
PublicKey = <публичный-ключ-B>
Endpoint = 198.51.100.20:51820
AllowedIPs = 10.77.0.2/32, 10.0.2.0/24
PersistentKeepalive = 25
Файл /etc/wireguard/wg0.conf на Router B:
[Interface]
Address = 10.77.0.2/24
ListenPort = 51820
PrivateKey = <приватный-ключ-B>
[Peer]
PublicKey = <публичный-ключ-A>
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.77.0.1/32, 10.0.1.0/24
PersistentKeepalive = 25
Разберём параметры:
Address— адрес интерфейсаwg0в туннельной подсети.ListenPort— UDP-порт, на котором слушает узел.PrivateKey— приватный ключ этого узла.[Peer]— описание соседа.PublicKey— публичный ключ соседа.Endpoint— внешний адрес соседа. На стороне со статичным адресом его можно не указывать.AllowedIPs— адреса, которые разрешено принимать от этого пира и отправлять ему.PersistentKeepalive— отправлять keepalive каждые N секунд. Нужен стороне за NAT, чтобы держать проброс открытым.
Файл содержит приватный ключ, поэтому ограничьте права:
chmod 600 /etc/wireguard/wg0.conf
Шаг 3. Включаем маршрутизацию
Чтобы трафик ходил между wg0 и локальной сетью, включаем форвардинг (на обоих маршрутизаторах):
echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-ip-forward.conf
sysctl --system
Шаг 4. Поднимаем туннель
wg-quick up wg0
Проверяем:
wg show
ip addr show wg0
ping -c 3 10.77.0.2
wg show выводит ключи, endpoint и статистику, а строка latest handshake показывает, что рукопожатие прошло.
Шаг 5. Маршруты до удалённых сетей
Благодаря AllowedIPs WireGuard сам добавляет маршруты на удалённые подсети, когда интерфейс поднимается. Проверить можно так:
ip route
На Router A должна появиться запись вида 10.0.2.0/24 dev wg0 scope link. Добавлять её вручную не нужно — wg-quick делает это из AllowedIPs.
Шаг 6. Настраиваем nftables
Разрешаем UDP-порт WireGuard на внешнем интерфейсе и форвардинг между локальной сетью и туннелем. На Router A (/etc/nftables.conf):
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
iif "lo" accept
ct state established,related accept
# WireGuard только от соседнего маршрутизатора
iifname "wan0" ip saddr 198.51.100.20 udp dport 51820 accept
# SSH для управления (сузьте источник под себя)
iifname "wan0" ip saddr 203.0.113.0/24 tcp dport 22 accept
counter drop
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
# Разрешаем трафик между локальной сетью и туннелем
iifname "lan0" oifname "wg0" accept
iifname "wg0" oifname "lan0" accept
counter drop
}
}
Применяем:
nft -f /etc/nftables.conf
Если одна из сетей «за NAT» и у неё нет обратного маршрута, добавьте маскарадинг:
table inet nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
iifname "lan0" oifname "wg0" masquerade
}
}
Шаг 7. Автозапуск
wg-quick поставляется с systemd-юнитом, который поднимает интерфейс по имени:
systemctl enable --now wg-quick@wg0
Сделайте это на обоих узлах. Конфиг, sysctl и правила nftables при этом уже должны быть на месте.
Полезные команды
wg show # общая информация и пиры
wg show wg0 latest-handshakes # когда был последний handshake
wg-quick down wg0 # опустить интерфейс
Безопасность
- Приватный ключ храните в
/etc/wireguardс правами600и не копируйте его на другие машины без необходимости. - Ограничивайте источник
udp dport 51820адресом соседа (как в примере) — это не замена криптографии, но отсекает мусор. AllowedIPsдержите узкими: именно они определяют, какие адреса вы доверяете этому пиру.
Заключение
WireGuard даёт шифрованный туннель с минимумом настроек: сгенерировал ключи, описал пиров, поднял wg-quick — и готово. Для связки сетей это сегодня, пожалуй, лучший баланс между простотой и безопасностью.