SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-28

Как настроить WireGuard VPN на своем VPS

Пошаговое руководство по установке WireGuard на Linux VPS. Разберем генерацию ключей, настройку wg0.conf, IP forwarding, NAT и типичные ошибки при проверке рукопожатия.

Что вы создаете

WireGuard VPN на собственном сервере — это около сорока строк конфигурации: одна пара ключей, один файл интерфейса, одна настройка sysctl, одно правило NAT и одно правило межсетевого экрана. Установка тривиальна, поэтому большая часть этого руководства посвящена возможным сбоям, правам доступа к ключам, AllowedIPs, пересылке пакетов и DNS.

WireGuard — это туннель третьего уровня (Layer 3), работающий на уровне ядра. Он включен в основную ветку Linux начиная с версии 5.6, поэтому Ubuntu 24.04 и Debian 13 поставляются с ним без необходимости в сторонних модулях. Здесь нет согласования шифров, центра сертификации или этапа ввода имени пользователя и пароля: пир определяется открытым ключом и списком IP-адресов, которые этот ключ может использовать. Пакет, не прошедший проверку MAC, отбрасывается без ответа, поэтому порт не реагирует на сканирование. Обратная сторона: сервера аутентификации не существует, поэтому для отзыва доступа необходимо удалить пир на самом сервере.

Сначала проверьте виртуализацию

Для работы WireGuard требуется ядро, в которое можно загрузить модуль; на KVM VPS это работает сразу. При использовании контейнерной виртуализации, разделяющей ядро хоста (OpenVZ, LXC), первая команда завершится ошибкой RTNETLINK answers: Operation not supported, и в качестве альтернативы придется использовать реализацию 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/32

chmod 600 его; предупреждение при запуске о том, что файл доступен всем пользователям, означает, что вы пропустили этот шаг. Address — это адрес сервера внутри туннеля, включающий маску всей подсети VPN. Выберите диапазон, который не встретится вам в реальных сетях; 192.168.1.0/24 конфликтует с половиной домашних маршрутизаторов, за которыми находятся ваши клиенты, из-за чего туннель будет молча проигрывать локальному маршруту.

AllowedIPs пира на стороне сервера — это /32, единственный адрес в туннеле, принадлежащий данному клиенту. Если назначить двум пирам одинаковый AllowedIPs, трафик будет переходить к тому, кто был настроен последним, а первый перестанет получать данные без вывода каких-либо ошибок. Оставьте 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 требуется исходящий интерфейс — сетевая карта, через которую осуществляется выход в интернет, а не 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-сессию: policy drop в сочетании с опечаткой в правиле SSH заблокирует вам доступ к собственному серверу. Обратите внимание на то, что цепочка forward не разрешает, от wg0 до wg0. Узлы получают доступ в интернет, но не друг к другу; добавьте iifname "wg0" oifname "wg0" accept для VPN типа peer-to-peer. Эта же цепочка определяет, к чему узел может обращаться на самом сервере, что важно, если машина одновременно служит удаленной средой разработки с запущенным 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 show

wg-quick создает интерфейс, добавляет адреса и устанавливает маршруты, полученные из AllowedIPs. enable --now — это важная часть: настройки, выполненные вручную через wg-quick up wg0, сбрасываются после перезагрузки, а обновления ядра требуют перезагрузки системы. Юнит, который не запустился автоматически после перезагрузки, остается неактивным, пока кто-либо не попытается установить соединение. Поэтому использование drop-in файла OnFailure= в wg-quick@wg0 с настройкой уведомлений на ваш собственный ntfy сервер — самый простой способ узнать о сбое через телефон, не дожидаясь жалоб от пользователей, потерявших доступ.

Клиентская конфигурация и настройка, в которой все ошибаются

[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 = 25

AllowedIPs выполняет две задачи одновременно, и их смешение — основной источник путаницы при работе с WireGuard.

Для исходящего трафика это таблица маршрутизации. Пакет, адрес назначения которого соответствует AllowedIPs пира, шифруется и отправляется этому пиру. 0.0.0.0/0, ::/0 направляет всё в туннель, создавая полный туннель, где сервер становится маршрутом по умолчанию. Раздельный туннель (split tunnel) — это более узкий список: AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 передает VPN-трафик и трафик одной частной сети за сервером, а всё остальное продолжает идти через локальный маршрут. Именно этот узкий список позволяет полностью скрыть сервисы от публичного интернета, например, приватный экземпляр Nextcloud на VPS, привязанный к адресу туннеля, или лабораторные виртуальные машины, работающие на том же узле, остаются доступными для пиров и невидимыми для всех остальных.

Для входящего трафика это список контроля доступа. Расшифрованный пакет от пира, чей адрес источника отсутствует в 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 = клиент продолжает использовать резолвер, полученный из локальной сети, например, роутер кафе по адресу 192.168.1.1. Этот маршрут является более специфичным, чем маршрут по умолчанию, поэтому DNS-запросы уходят через локальный канал в открытом виде, в то время как весь остальной трафик передается через туннель. Сам трафик остается приватным, но список запрашиваемых имен — нет.

Есть два честных варианта. Укажите DNS на публичный резолвер (DNS = 9.9.9.9), тогда запросы пройдут через туннель и выйдут с вашего сервера, хотя резолвер все равно будет их видеть. Либо запустите unbound или dnsmasq, привязанные к 10.8.0.1, установите DNS = 10.8.0.1 и добавьте udp dport 53 iifname "wg0" accept в цепочку input; после настройки этой строки и удаления резолвера разрешение имен прекратится вовсе.

На клиентах 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 выводит конфигурацию без ключей, специфичных для Address (DNS, PostUp), а syncconf применяет изменения, сохраняя активные сессии. Обновляются только пиры: при изменении Address по-прежнему требуется полная перезагрузка интерфейса (down/up). Для отзыва доступа используйте sudo wg set wg0 peer <public key> remove, а затем удалите соответствующий блок из файла, иначе он вернется при следующей перезагрузке.

Типовые сбои и сообщения об ошибках

Рукопожатие не завершается. wg show показывает пир без latest handshake, а в логах клиента отображается:

Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)

Данные не поступают или не принимаются. Проверьте по порядку: открыт ли UDP 51820 на брандмауэре VPS и на сетевом экране провайдера (отдельная настройка в большинстве панелей управления); верны ли адрес и порт в Endpoint; не перепутаны ли ключи. В блоке [Peer] на стороне клиента должен быть указан публичный ключ сервера, и наоборот. Вставка приватного ключа или собственного публичного ключа клиента приводит именно к такому симптому. sudo tcpdump -ni any udp port 51820 на сервере показывает, доходят ли пакеты вообще. По умолчанию модуль ядра ничего не логирует; сообщения WireGuard появляются в dmesg только после включения динамической отладки (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control). При включенной отладке несовпадение ключей будет отображаться как отброшенный пакет с неверным MAC.

Рукопожатие проходит, интернета нет. ping 10.8.0.1 выполняется успешно, но ping 1.1.1.1 выдает таймаут: отсутствует пересылка (forwarding) или NAT. Убедитесь, что sysctl net.ipv4.ip_forward содержит 1, затем наблюдайте за счетчиками во время пинга с клиента с помощью sudo nft list ruleset или sudo iptables -t nat -L POSTROUTING -n -v. Нулевые значения на правиле маскарадинга означают, что неверно указано имя исходящего интерфейса; растущий счетчик при отсутствии ответов указывает на политику цепочки forward.

Интернет работает, имена не разрешаются. ping 1.1.1.1 выполняется успешно, а curl https://example.com возвращает Could not resolve host. Отсутствует строка DNS или в ней указан резолвер, недоступный изнутри туннеля.

Некоторые HTTPS-сайты зависают. SSH и ping работают нормально, но загрузка тяжелых страниц останавливается. Это проблема MTU пути: туннель добавляет накладные расходы, и какой-то узел на пути следования отбрасывает слишком большие пакеты, не отправляя обратно ICMP-сообщение. Уменьшите MTU в [Interface] на клиенте, попробуйте 1420, затем 1380, затем 1280. Если снижение MTU устранило зависания, но скорость все еще низкая, прекратите подбирать значения наугад и выполните поиск реального MTU пути методом деления пополам и ограничение TCP MSS, что также позволит исключить причины, не связанные с туннелем.

Интерфейс не запускается. 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 на 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 восстанавливает работу автоматически, если вы включили эту опцию.

Состояние каждого узла занимает мало места, а криптография выполняется на уровне ядра, поэтому предел производительности определяется мощностью CPU и пропускной способностью сети вашего VPS, а не параметрами конфигурации. Измеряйте её с помощью iperf3 через туннель, вместо того чтобы доверять заявленным цифрам. При масштабировании основной проблемой становится эксплуатация. Каждому узлу нужен уникальный IP-адрес в туннеле, а ручное редактирование шестидесяти блоков [Peer] неизбежно приводит к появлению дубликатов AllowedIPs: генерируйте конфигурации с помощью скриптов. Один сервер — это одна UDP-конечная точка и одна точка отказа, а в WireGuard нет встроенной кластеризации: резервирование означает наличие второго сервера со своими ключами. Ротация ключей остается ручной процедурой, поэтому ведите учет того, у кого какой ключ, и как отозвать доступ. Когда такой учет перестает помещаться в текстовый файл, стандартным решением становится использование панели управления поверх того же уровня передачи данных ядра. Самостоятельно развернутый сервер NetBird берет на себя распределение адресов, распространение данных об узлах и ключи настройки, которые иначе пришлось бы настраивать вручную. Если запуск такой панели управления для вас — лишняя нагрузка, Tailscale предоставляет её как сервис, и их бесплатный тарифный план включает шесть пользователей с неограниченным количеством устройств, чего достаточно для большинства персональных сетей. После этого лимиты зависят от количества людей, а не машин, поэтому стоимость для семьи или небольшой команды зависит от количества пользователей с доступом, а не от числа узлов, которые вы вручную вписывали бы в wg0.conf. В этом случае учет раздельного туннелирования AllowedIPs сводится к анонсированию ваших частных диапазонов через маршрутизатор подсети, который настраивается один раз на одном VPS и управляется централизованно, вместо того чтобы копировать настройки в каждый файл клиента. Стоит ли идти на такой компромисс, зависит от того, к чему именно имеет доступ панель управления, и она никогда не хранит ключи, шифрующие ваш трафик, хотя и определяет, какие узлы узнают друг о друге.

Для всего этого требуется Linux-сервер под вашим управлением, публичный IP-адрес, ядро, в которое можно загрузить модуль, и полностью подконтрольный вам межсетевой экран.

FAQ

Почему рукопожатие WireGuard никогда не завершается?

wg show при указании пира без latest handshake означает, что пакеты не доходят или не принимаются. Проверьте UDP 51820 на брандмауэре VPS и на внешнем сетевом экране провайдера, подтвердите Endpoint хоста и порта, затем убедитесь, что ключи не перепутаны: блок [Peer] клиента должен содержать публичный ключ сервера. 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 выдает таймаут — это указывает на проблемы с пересылкой (forwarding) или NAT. Убедитесь, что sysctl net.ipv4.ip_forward содержит 1 и что этот параметр задан в /etc/sysctl.d/, а не просто через sysctl -w, который сбрасывается после перезагрузки. Затем проверьте правило маскарадинга для вашего основного исходящего интерфейса в 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. Запустите sudo modprobe wireguard && echo ok перед выполнением любых других действий.