Как настроить WireGuard VPN на своем VPS
Инструкция по установке WireGuard на Linux. Разбор конфигурации wg0.conf, настройки IP forwarding, NAT и решение проблем с handshake и правами доступа к ключам.
Что вы создаете
Настройка WireGuard VPN на собственном сервере занимает около сорока строк конфигурации: одна пара ключей, один файл интерфейса, один параметр sysctl, одно правило NAT и одно разрешение в firewall. Процесс установки прост, поэтому основная часть данного руководства посвящена типичным проблемам: правам доступа к ключам, AllowedIPs, пересылке трафика (forwarding) и DNS.
WireGuard — это туннель Layer 3 в ядре. Он включен в основной состав ядра начиная с версии Linux 5.6, поэтому в Ubuntu 24.04 и Debian 13 он поставляется без внешних модулей. В протоколе нет согласования шифров, центров сертификации (CA) или этапа ввода логина/пароля: пир (peer) определяется как публичный ключ плюс IP-адреса, которые этот ключ может использовать. Пакет, не прошедший проверку MAC, отбрасывается без ответа, поэтому порт не отвечает на сканирование. Обратная сторона: сервер аутентификации отсутствует, поэтому для отзыва доступа необходимо удалить пира на устройстве.
Сначала проверьте тип виртуализации
Для работы WireGuard требуется ядро, в которое можно загрузить модуль. В VPS на базе KVM это работает сразу. При использовании контейнерной виртуализации с общим ядром хоста — OpenVZ, LXC — первая команда завершится ошибкой RTNETLINK answers: Operation not supported. В этом случае будет использована реализация в userspace wireguard-go. Сначала проверьте тип виртуализации с помощью sudo modprobe wireguard && echo ok.
Генерация ключей без утечки данных
Доступ к файлу /etc/wireguard/server.key для всех пользователей системы равносилен отсутствию VPN. Стандартная команда umask 077 && wg genkey | sudo tee ... ненадежна, так как sudo применяет собственный umask к файлу, который создает tee. Устанавливайте права доступа явно.
sudo apt update && sudo apt install -y wireguard nftables
sudo install -d -m 700 /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub'
sudo chmod 600 /etc/wireguard/server.keyГенерируйте пару ключей для клиента аналогичным способом. wg genpsk позволяет добавить необязательный предварительно распределенный ключ (pre-shared key) — по одной строке в каждом конфигурационном файле.
Интерфейс сервера: /etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>
[Peer]
PublicKey = <laptop public key>
PresharedKey = <psk, optional>
AllowedIPs = 10.8.0.2/32chmod 600 это предупреждение при запуске о том, что файл доступен для всех пользователей; это означает, что вы пропустили этот шаг. Address — это адрес сервера внутри туннеля, представляющий всю подсеть VPN. Выбирайте диапазон, который не используется в обычных сетях — 192.168.1.0/24 совпадает с половиной домашних роутеров ваших клиентов, из-за чего туннель будет незаметно проигрывать локальному маршруту.
AllowedIPs пира на стороне сервера — это /32, единственный туннельный адрес, принадлежащий клиенту. Если назначить двум пирам один и тот же allowed IP, конфигурация применится к тому, который был настроен последним; первый пир перестанет получать трафик без вывода ошибок. Оставьте SaveConfig без значения, иначе wg-quick down перезапишет этот файл текущим состоянием системы.
Преобразование сервера в роутер
Linux-сервер отбрасывает пакеты, адреса которых не соответствуют его собственному адресу. По умолчанию функции пересылки (forwarding) и source NAT отключены.
printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' \
| sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forwardЧистая установка sysctl -w будет работать только до следующей перезагрузки, после чего функции перестанут работать. Для NAT требуется исходящий (egress) интерфейс — сетевая карта, имеющая выход в интернет, а не wg0. Не полагайтесь на eth0; определите ваш интерфейс с помощью ip route show default, так как в текущих образах используются имена вида enp1s0 или ens3.
Firewall: порт и путь пересылки
Один файл nftables содержит настройки фильтрации и NAT. Выполнение команды /etc/nftables.conf удаляет текущий набор правил; пропустите этот шаг, если на сервере уже используется ufw или Docker.
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iif lo accept
tcp dport 22 accept
udp dport 51820 accept
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname "wg0" oifname "enp1s0" accept
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.8.0.0/24 oifname "enp1s0" masquerade
}
}Примените конфигурацию с помощью sudo systemctl enable --now nftables, оставив вторую SSH-сессию открытой: ошибка в правиле SSH в команде policy drop приведет к потере доступа к серверу. Обратите внимание на ограничения в цепочке forward — запрещен трафик от wg0 до wg0. Узлы имеют доступ в интернет, но не друг к другу; добавьте iifname "wg0" oifname "wg0" accept для организации P2P VPN. Эта же цепочка определяет доступ узлов к самому серверу. Это важно, если сервер используется как удаленная среда разработки с Claude Code в tmux и вы не хотите открывать этот функционал для публичного доступа.
Для систем с ufw: выполните ufw allow 51820/udp, DEFAULT_FORWARD_POLICY="ACCEPT" в /etc/default/ufw и добавьте правило *nat POSTROUTING MASQUERADE в начало /etc/ufw/before.rules.
Настройка через systemd
sudo systemctl enable --now wg-quick@wg0
sudo wg showwg-quick создает интерфейс, добавляет адреса и устанавливает маршруты на основе AllowedIPs. enable --now — это критический аспект: ручной запуск wg-quick up wg0 не сохранится после перезагрузки, а обновление ядра требует перезагрузки системы.
Конфигурация клиента и настройка, в которой все ошибаются
[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1
[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25AllowedIPs выполняет две разные функции одновременно. Смешивание этих функций является основной причиной путаницы в WireGuard.
Для исходящего трафика это таблица маршрутизации. Пакет, назначение которого совпадает с AllowedIPs пира, шифруется и отправляется этому пиру. 0.0.0.0/0, ::/0 направляет через туннель весь трафик — это полный туннель, где сервер является маршрутом по умолчанию. Split tunnel (раздельный туннель) — это более узкий список: AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 передает VPN-трафик и одну частную сеть за сервером, а весь остальной трафик использует локальные маршруты. Этот узкий список позволяет полностью исключить сервисы из публичного интернета: частный экземпляр Nextcloud на VPS, привязанный к адресу туннеля, или виртуальные машины в среде nested-virtualisation, запущенные на том же узле, остаются доступными для пиров и невидимыми для всех остальных.
Для входящего трафика это список контроля доступа (ACL). Расшифрованный пакет от пира, чей адрес источника не указан в AllowedIPs этого пира, отбрасывается. Именно поэтому сервер содержит 10.8.0.2/32 для ноутбука: запись 0.0.0.0/0 в этом списке позволит клиенту подменять любой адрес внутри туннеля.
PersistentKeepalive предназначен для клиентов за NAT, где роутер удерживает UDP-сопоставление открытым только во время передачи пакетов. Когда время ожидания истекает, сервер больше не может связаться с клиентом. PersistentKeepalive = 25 удерживает сопоставление открытым — настройте этот параметр на клиенте, а не на сервере с публичным IP.
DNS и утечка, которую никто не замечает
При наличии AllowedIPs = 0.0.0.0/0 и отсутствии строки DNS = клиент использует resolver, полученный из локальной сети — роутер в кафе по адресу 192.168.1.1. Этот маршрут более специфичен, чем маршрут по умолчанию, поэтому DNS-запросы уходят через локальный канал в открытом виде, в то время как весь остальной трафик идет через туннель. Трафик защищен, но список запрашиваемых имен — нет.
Есть два надежных варианта. Настройте DNS на публичный resolver (DNS = 9.9.9.9), тогда запросы пойдут через туннель и выйдут с вашего сервера, хотя этот resolver все равно их увидит. Или запустите unbound или dnsmasq, привязанные к 10.8.0.1, настройте DNS = 10.8.0.1 и добавьте udp dport 53 iifname "wg0" accept в input chain — если настроить эту строку, DNS-разрешение работать не будет.
На Linux-клиентах wg-quick применяет DNS через resolvconf; если этот параметр отсутствует, вы получите resolvconf: command not found. Установите openresolv или настройте PostUp = resolvectl dns %i 10.8.0.1 в клиенте systemd-resolved.
Добавление и удаление пиров без разрыва туннеля
Перезапуск интерфейса для добавления пользователя приведет к разрыву соединений у всех подключенных клиентов. Добавьте блок [Peer] к wg0.conf, затем перезагрузите набор пиров без остановки интерфейса.
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'wg-quick strip выводит конфигурацию без ключей wg-quick-only (Address, DNS, PostUp), а syncconf применяет изменения без разрыва активных сессий. Команда обновляет только пиров: для изменения Address все еще требуется полный перезапуск интерфейса (down/up). Чтобы отозвать доступ, используйте sudo wg set wg0 peer <public key> remove, а затем удалите блок из файла, иначе он появится при следующей перезагрузке.
Режимы сбоев и соответствующие сообщения
Handshake не завершается. wg show указывает на пира без latest handshake, а в логах клиента отображается:
Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)Данные не поступают или не принимаются. Проверьте следующее: открыт ли UDP 51820 на firewall VPS и в сетевом firewall вашего провайдера (это отдельная настройка в большинстве панелей); верны ли адрес и порт Endpoint; правильно ли распределены ключи. В блоке [Peer] клиента должен быть указан публичный ключ сервера, и наоборот — использование приватного ключа или публичного ключа самого клиента приводит именно к такому результату. Команда sudo tcpdump -ni any udp port 51820 на сервере покажет, поступают ли пакеты вообще. По умолчанию модуль ядра не ведет логов; сообщения WireGuard появляются в dmesg только после включения dynamic debug (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control). При включенном режиме ошибка в ключе отображается как invalid-MAC drop.
Handshake работает, интернета нет. ping 10.8.0.1 проходит успешно, но ping 1.1.1.1 завершается по таймауту: отсутствует форвардинг или NAT. Проверьте, читает ли sysctl net.ipv4.ip_forward файл 1, затем отслеживайте счетчики во время выполнения ping с клиента с помощью sudo nft list ruleset или sudo iptables -t nat -L POSTROUTING -n -v. Нулевое значение пакетов в правиле masquerade означает, что указано неверное имя исходящего интерфейса; рост счетчика без получения ответов указывает на политику forward chain.
Интернет работает, имена не разрешаются. ping 1.1.1.1 проходит успешно, но curl https://example.com возвращает Could not resolve host. Строка DNS отсутствует или указывает на резолвер, недоступный внутри туннеля.
Некоторые HTTPS-сайты зависают. SSH и ping работают корректно; загрузка тяжелых страниц прерывается. Это проблема path MTU: туннель добавляет служебную информацию, и промежуточный узел сбрасывает пакеты избыточного размера без отправки ICMP-сообщения. Уменьшите MTU в конфигурации [Interface] на клиенте — попробуйте 1420, затем 1380, затем 1280.
Интерфейс не запускается. Address already in use означает, что порт UDP 51820 занят другим процессом. Cannot find device wg0 после неудачного up обычно означает, что конфигурация была отклонена; проверьте journalctl -u wg-quick@wg0 -n 50.
Миграция со Streisand или OpenVPN
Проект Streisand не поддерживается, а его репозиторий переведен в архив. Использование VPN на базе заброшенной системы автоматизации создает риски безопасности в долгосрочной перспективе. Обновление текущей установки невозможно. PKI в OpenVPN не совместима с WireGuard: в WireGuard нет сертификатов, центров сертификации (CA) и срока действия ключей, поэтому каждому клиенту требуется новая пара ключей.
Выполняйте миграцию параллельно: WireGuard на UDP 51820 может работать одновременно с OpenVPN на 1194 на одном и том же сервере. Разверните wg0, поочередно перенесите клиентов, затем остановите старую службу. Модель аутентификации по логину/паролю и механизм отзыва сертификатов OpenVPN не переносятся; если вам требуются учетные записи или журнал аудита, реализуйте этот функционал поверх WireGuard.
Резервное копирование, обновление и проблемы масштабирования
/etc/wireguard — это сервер. Сделайте его резервную копию (sudo tar czf wg-backup.tgz -C /etc wireguard, права 600, храните вне сервера), и вы сможете развернуть систему на новом VPS за считанные минуты. Если вы потеряете приватный ключ сервера, вам придется перевыпускать конфигурации всех клиентов, так как клиенты используют публичный ключ сервера для проверки. Обновление — это обычный apt upgrade плюс перезагрузка для обновления ядра; wg-quick@wg0 восстановится автоматически, если эта функция была включена.
Состояние каждого пира (peer) занимает мало места, а криптография выполняется в ядре. Поэтому ограничением выступают мощность CPU и пропускная способность вашего VPS, а не параметры в данном конфиге. Измеряйте нагрузку с помощью iperf3 через туннель, а не полагайтесь на опубликованные данные. При масштабировании основной проблемой становится администрирование. Каждому пиру требуется уникальный IP-адрес в туннеле. Ручное редактирование шестидесяти блоков [Peer] приводит к появлению дублирующихся AllowedIPs: генерируйте конфигурации с помощью скрипта. Один сервер — это одна UDP-конечная точка и единая точка отказа. У WireGuard нет механизмов кластеризации: для обеспечения отказоустойчивости требуется второй сервер с собственными ключами. Ротация ключей остается ручной, поэтому зафиксируйте, у кого какой ключ и как аннулировать конкретный ключ.
Для всего этого необходим подконтрольный вам Linux-сервер: публичный IP-адрес, ядро с возможностью загрузки модуля и полный контроль над межсетевым экраном.
FAQ
Почему не завершается рукопожатие (handshake) WireGuard?
Если wg show указывает пира без latest handshake, это означает, что пакеты не доходят или не принимаются. Проверьте UDP 51820 на firewall вашего VPS и в сетевом firewall провайдера. Убедитесь в правильности Endpoint хоста и порта. Проверьте соответствие ключей: в блоке [Peer] клиента должен быть указан public ключ сервера. Команда sudo tcpdump -ni any udp port 51820 на сервере показывает, доходят ли пакеты. Команда dmesg сообщает об ошибках рукопожатия WireGuard только после включения динамической отладки (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control); в этом случае несоответствие ключей отображается как ошибка invalid-MAC.
Туннель подключен, но интернет отсутствует. Что не так?
Если ping 10.8.0.1 работает, а ping 1.1.1.1 выдает timeout, проблема в forwarding или NAT. Убедитесь, что sysctl net.ipv4.ip_forward возвращает 1 и что этот параметр задан в /etc/sysctl.d/, а не только через sysctl -w, который сбрасывается после перезагрузки. Затем проверьте правила masquerade: они должны использовать ваш реальный интерфейс выхода из ip route show default — enp1s0 или ens3, реже eth0.
Нужно ли добавлять строку DNS = в конфиг клиента?
При использовании полного туннеля без строки DNS = клиент использует DNS-резолвер из локальной сети. Такие запросы передаются в открытом виде через локальный канал, хотя весь остальной трафик идет через туннель. Направьте DNS на публичный резолвер или запустите unbound/dnsmasq на интерфейсе 10.8.0.1 и откройте udp dport 53 iifname "wg0" в цепочке input.
Что именно управляет параметр AllowedIPs?
Он выполняет две функции. Для исходящего трафика это таблица маршрутизации: трафик, соответствующий AllowedIPs пира, шифруется и отправляется этому пиру. Для входящего трафика это список контроля доступа: расшифрованный пакет, источник которого находится вне AllowedIPs этого пира, отбрасывается. Поэтому на стороне сервера для каждого клиента прописывается /32, в то время как на стороне клиента может быть указан 0.0.0.0/0.
Будет ли WireGuard работать на любом VPS?
На KVM VPS он работает через модуль ядра без дополнительной настройки. В контейнеризации, использующей ядро хоста (например, OpenVZ или LXC), modprobe wireguard завершается с ошибкой Operation not supported, и в качестве резервного варианта используется реализация wireguard-go в userspace. Выполните sudo modprobe wireguard && echo ok перед началом настройки.