WireGuard на Linux: VPN между сервером и офисом
Когда нужно связать сервер с офисом, многие по привычке тянутся к IPsec или OpenVPN и получают в нагрузку горы конфигов, сертификаты, десятки параметров и невнятные логи. WireGuard решает ту же задачу иначе: туннель описывается парой десятков строк, ключи это короткие строки в base64, а код входит в ядро Linux с версии 5.6. В этой статье поднимем VPN между сервером srv-linux01 (он будет концентратором) и офисным шлюзом rtr-edge01, чтобы офисная сеть 192.168.20.0/24 видела нашу лабораторную сеть 192.168.10.0/24 и наоборот.
Схема такая. Туннельная сеть: 10.99.0.0/24. Сервер srv-linux01 получает адрес 10.99.0.1 и слушает UDP-порт 51820. Офисный шлюз получает 10.99.0.2 и сам инициирует соединение, потому что находится за NAT. За сервером стоит сеть 192.168.10.0/24, за офисом 192.168.20.0/24.
Шаг 1. Установка и генерация ключей
На Ubuntu и Debian пакет называется wireguard, на RHEL-семействе wireguard-tools. Модуль ядра уже есть в современных версиях.
sudo apt install wireguard
# RHEL / Rocky: sudo dnf install wireguard-tools
sudo modprobe wireguard
lsmod | grep wireguard
Ключи генерируются на каждой стороне отдельно. Приватный ключ никогда не покидает свой хост, наружу уходит только публичный.
umask 077
wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub
wg genpsk > /etc/wireguard/office.psk
ls -l /etc/wireguard/
Команда umask 077 гарантирует, что файлы создадутся с правами только для владельца. Третья команда создаёт необязательный предварительно разделяемый ключ (PSK): это дополнительный слой симметричного шифрования поверх обычного обмена, он страхует на случай появления квантовых атак на кривую. Один PSK на пару узлов, значение копируется на обе стороны безопасным каналом. В дальнейших конфигах мы используем только плейсхолдеры вместо ключей, а настоящие значения подставляй из своих файлов.
Шаг 2. Конфигурация сервера
Конфиг лежит в /etc/wireguard/wg0.conf и состоит из секции Interface (сам хост) и секций Peer (по одной на каждого партнёра).
# /etc/wireguard/wg0.conf на srv-linux01
[Interface]
Address = 10.99.0.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>
SaveConfig = false
[Peer]
# офисный шлюз
PublicKey = <OFFICE_PUBLIC_KEY>
PresharedKey = <PRESHARED_KEY>
AllowedIPs = 10.99.0.2/32, 192.168.20.0/24
Самый важный и самый частый источник путаницы это параметр AllowedIPs. Он работает двояко. Для исходящих пакетов это таблица маршрутизации: пакет к адресу из этого списка уйдёт именно этому пиру. Для входящих это фильтр: пакет от пира будет принят, только если его исходный адрес входит в список. Поэтому для офисного пира мы перечисляем и его туннельный адрес, и всю офисную подсеть.
Права на конфиг должны быть строгими, так как внутри приватный ключ:
sudo chmod 600 /etc/wireguard/wg0.conf
sudo chown root:root /etc/wireguard/wg0.conf
Шаг 3. Конфигурация офисной стороны
На офисном шлюзе (допустим, это тоже Linux, rtr-edge01) конфиг зеркальный. Отличие в том, что здесь указан Endpoint сервера и параметр поддержания соединения.
# /etc/wireguard/wg0.conf на rtr-edge01
[Interface]
Address = 10.99.0.2/24
PrivateKey = <OFFICE_PRIVATE_KEY>
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
PresharedKey = <PRESHARED_KEY>
Endpoint = vpn.itpages.lab.local:51820
AllowedIPs = 10.99.0.1/32, 192.168.10.0/24
PersistentKeepalive = 25
Параметр PersistentKeepalive = 25 заставляет офисный шлюз отправлять пустой пакет раз в 25 секунд. Без него NAT на офисном роутере забудет запись о соединении, и сервер не сможет достучаться до офиса первым. Для хоста с публичным адресом этот параметр не нужен, но для узлов за NAT он обязателен.
В поле Endpoint можно указать и IP, и имя. Учти, что имя разрешается один раз при старте интерфейса, поэтому при смене адреса сервера клиенту придётся перезапустить туннель.
Шаг 4. Маршрутизация и файрвол
Сам туннель это только половина дела. Чтобы офисные клиенты ходили в лабораторную сеть, srv-linux01 должен пересылать пакеты, а файрвол должен их пропускать. Включаем форвардинг навсегда:
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forward
Теперь правила файрвола. Пример на nftables в отдельном файле, который подключается к основному набору:
table inet wg_filter {
chain input {
type filter hook input priority 0; policy accept;
udp dport 51820 accept
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
iifname "wg0" ip daddr 192.168.10.0/24 accept
iifname "eth0" oif "wg0" ip saddr 192.168.10.0/24 accept
}
}
Для варианта с UFW или firewalld логика та же: открыть UDP 51820 на входе и разрешить пересылку между wg0 и локальной сетью. Главное, чтобы политика пересылки не была слишком широкой: пускай трафик идёт только между нужными подсетями, а не отовсюду куда угодно.
Обычно хостам в лабораторной сети нужно знать, куда слать пакеты для офиса. Есть два пути. Либо на шлюзе по умолчанию (на нашем rtr-edge01 в роли локального роутера) добавляется статический маршрут 192.168.20.0/24 via 192.168.10.10, где 192.168.10.10 это адрес srv-linux01. Либо шлюзом по умолчанию служит сам srv-linux01. Второй вариант проще, но делает сервер узким местом.
# на роутере сети 192.168.10.0/24
ip route add 192.168.20.0/24 via 192.168.10.10
Шаг 5. Запуск и автозагрузка
Интерфейс управляется через wg-quick и systemd. Одна команда включает и запуск, и автозагрузку:
sudo systemctl enable --now wg-quick@wg0
sudo wg show
ip -br addr show wg0
ip route | grep wg0
Команда wg show показывает интерфейс и пиры. Главный индикатор успеха это строка latest handshake: пока рукопожатия не было, туннель не работает. Если там написано «1 minute, 3 seconds ago», обмен ключами проходит. Обязательно смотри также на счётчики transfer: если принято 0 байт при ненулевой отправке, пакеты уходят, но ответ не возвращается.
Проверяем сквозную связность с офисного шлюза, а затем с хоста в офисной сети:
ping -c 3 10.99.0.1
ping -c 3 192.168.10.10
ping -c 3 srv-dns01.itpages.lab.local
Если пинг по IP проходит, а по имени нет, то проблема в DNS: офисным клиентам нужно выдать адрес DNS-сервера 192.168.10.2 и суффикс поиска itpages.lab.local. Об этом поговорим в одной из следующих статей.
Шаг 6. Добавление пира и ротация ключей
Одно из удобств WireGuard в том, что пиров можно добавлять на лету, без перезапуска и без обрыва остальных соединений. Допустим, появился ещё один удалённый сотрудник:
sudo wg set wg0 peer <NEW_PEER_PUBLIC_KEY> allowed-ips 10.99.0.10/32
sudo wg-quick save wg0
sudo wg show wg0 peers
Не используй save при включённом SaveConfig, если редактируешь файл руками: интерфейс при остановке перезапишет конфиг своим состоянием. Мы выбрали ручное редактирование, поэтому оставляем SaveConfig = false и вносим изменения прямо в файл, затем применяем без обрыва связи:
sudo wg syncconf wg0 <(wg-quick strip wg0)
Для ротации ключей генерируй новую пару на одной стороне, передавай публичный ключ партнёру, обновляй конфиг и перезапускай туннель в согласованное окно. Делай это не реже раза в год или сразу после ухода сотрудника, у которого был доступ к файлам ключей. Список выданных пиров держи в отдельном учётном документе: сам WireGuard не ведёт журнал подключений, и по умолчанию кто и когда подключался, ты не узнаешь.
Шаг 7. Диагностика туннеля
Когда что-то не работает, у WireGuard есть одна особенность: он молчит. Он не отвечает на пакеты с неверным ключом и не пишет ошибок в журнал, поэтому диагностика строится на наблюдении. Начинай с состояния интерфейса:
sudo wg show wg0
sudo wg show wg0 latest-handshakes
sudo wg show wg0 transfer
sudo journalctl -u wg-quick@wg0 -n 30 --no-pager
Отсутствие рукопожатия означает, что либо пакеты до сервера не доходят (порт, NAT, файрвол), либо ключи не совпадают. Проверь, видны ли входящие пакеты на порту, и уходят ли ответы:
sudo tcpdump -i eth0 -nn udp port 51820
sudo ss -ulpn | grep 51820
sudo nft list ruleset | grep -n 51820
Если пакеты приходят, а рукопожатия нет, сравни публичные ключи на обеих сторонах: самая частая причина это перепутанная пара ключей или лишний пробел при копировании. Вычислить публичный ключ из приватного можно командой wg pubkey и сверить с тем, что записано у партнёра. Если же рукопожатие есть, а трафик не ходит, проверяй маршруты и форвардинг:
ip route get 192.168.20.5
sudo sysctl net.ipv4.ip_forward
sudo nft list chain inet wg_filter forward
sudo tcpdump -i wg0 -nn icmp
Захват на интерфейсе wg0 показывает уже расшифрованный трафик, и по нему легко понять, доходят ли эхо-запросы до туннеля, а возвращаются ли ответы. Если запросы уходят, а ответов нет, проверь, знает ли удалённый хост обратный маршрут к туннельной сети и подсети за туннелем. Очень часто хосты в офисе не знают, что сеть 192.168.10.0/24 лежит за шлюзом, и отправляют ответы в интернет через провайдера. Решение: статический маршрут на офисном роутере или включение маскарадинга на выходе туннеля как временная мера.
Пара слов про безопасность и масштабирование. WireGuard не имеет понятия пользователя и пароля: доступ определяется ключом, поэтому потеря устройства с приватным ключом равна потере доступа. Удаляй такой пир немедленно командой sudo wg set wg0 peer <PEER_PUBLIC_KEY> remove и фиксируй событие. Для ограничения доступа внутри сети не полагайся только на AllowedIPs, добавляй правила файрвола, которые разрешают пиру только нужные хосты и порты, например только SSH к srv-linux01 и ничего больше.
sudo nft add rule inet wg_filter forward iifname "wg0" ip saddr 10.99.0.10 ip daddr 192.168.10.10 tcp dport 22 accept
Когда пиров становится больше десятка, ручное ведение конфигов начинает мешать. Тогда выноси генерацию в скрипт или систему управления конфигурацией, а адреса туннельной сети выдавай по таблице. И не забывай про второй туннель на случай отказа: офисы часто подключают двумя каналами, и маршруты с разными метриками дают простой автоматический резерв.
Частые ошибки и как их избежать
- Неверные AllowedIPs. Люди пишут туда только туннельный адрес пира и удивляются, что трафик в офисную подсеть не идёт. Помни: этот параметр одновременно маршрут и фильтр. Перечисляй все подсети, которые лежат за пиром, и проверяй результат командой
ip route get 192.168.20.5. Не используй0.0.0.0/0там, где не нужен весь трафик через туннель. - Забытый форвардинг или закрытый порт. Туннель поднялся, рукопожатие есть, а между сетями тишина. Причина почти всегда в отключённом
net.ipv4.ip_forwardлибо в политике drop цепочки forward. Не забудь и про UDP 51820 во внешнем файрволе, и про проброс порта на NAT, если сервер стоит не на публичном адресе. Помни, что WireGuard молчит на неверные пакеты, поэтому закрытый порт определяется только по отсутствию рукопожатия. - Проблемы с MTU. Туннель добавляет накладные расходы (по умолчанию MTU интерфейса выставляется 1420), и если путь провайдера уже сжат, большие пакеты теряются: ping проходит, а SSH и HTTPS подвисают. Проверяй
ping -M do -s 1392через туннель, а при необходимости указывай в секции Interface параметрMTU = 1380и фиксируй MSS на файрволе.
Что дальше
Администрирование Linux — это набор связанных навыков, вот соседние темы серии: