Сайт FSA
10.04.2022

Шпаргалка по Wireguard

Возникла необходимость предоставить доступ из интернета устройству за NAT. Самый простой способ - заказать у провайдера выделенный IP адрес. Но это не всегда возможно. Самый простой способ - арендовать дешёвый виртуальный сервер и настроить с ним соединение через VPN. Это позволит направлять запросы из интернета на необходимый узел внутри вашей локальной сети. При этом можно предоставить статический IP адрес даже для ноутбука, который будет доступен извне по одному IP адресу, как бы он не подключился к интернету. Об этом и пойдёт речь в этой заметке.

Что такое VPN

Пишу эти строки в мире, где VPN тождественно способу обхода блокировок в интернете. Но что из себя представляет VPN реально?

Всё очень просто. VPN - это просто виртуальный кабель между двумя машинами. Сервер - это то место, куда вы можете включить свой виртуальный кабель, клиент - это вы с кабелем. При этом, в отличие от реального мира, сервер и клиент могут быть расположены на разных концах света. Всё, что вам нужно, чтобы клиент имел возможность соединиться с сервером.

Почему VPN выступает в качестве средства обхода блокировок? Всё просто. Поскольку вы смогли пробросить виртуальный кабель до удалённой сети, вы можете пользоваться всеми её возможностями, в том числе и доступом в интернет через шлюз этой сети.

Почему Wireguard?

Этот вопрос бы у вас не возник, если вы пробовали настроить, например, OpenVPN. Вереница параметров конфигурации клиента и сервера, создание ключей и прочее. Настройки Wireguard намного проще. Он использует современную криптографию и имеет высокую производительность.

Если вас интересует OpenVPN, то можете ознакомиться с заметкой про OpenVPN. В современных версиях, начиная с OpenVPN 2.6 появился вариант упрощенной настройки, который там и рассматривается.

Установка необходимых компонентов

Для дальнейшей настройки необходим пакет wireguard-tools. Он доступен в репозиториях Ubuntu 20.04 LTS и Fedora 35 под этим именем. В других дистрибутивах названием может отличаться. Пакет включает в себя команды wg и wg-quick, которые нам понадобятся.

Создание ключей

Поскольку мы создаём туннель между двумя точками, то они должны предварительно обменяться своими ключами. Для безопасности используется асимметричное шифрование, т.е. каждая сторона имеет свои закрытый и открытый ключ. Открытый ключ можно свободно передавать через незащищённые сети. С помощью него можно зашифровать данные, которые сможет расшифровать только владелец закрытого ключа, поэтому, в целях безопасности, закрытый ключ должен хранится в секрете.

Сформировать необходимую пару ключей, согласно документации, можно с помощью команды

wg genkey | tee private.key | wg pubkey > public.key

Операцию необходимо выполнить на сервере и на клиенте. Технически, сформировать пары ключей можно на любой машине, но не забывайте, что в целях безопасности закрытый ключ должен быть доступен только владельцу.

wg genkey формирует закрытый ключ, команда tee private.key сохраняет этот ключ в файл private.key и передаёт по цепочке следующей команде - wg pubkey, которая генерирует на базе закрытого ключа открытый ключ. Далее этот открытый ключ сохраняется в файле public.key. После генерации ключей лучше скрыть private.key от чужих глаз установив разрешения 600 на файл

chmod 600 private.key

Каждый из ключей закодирован с помощью base64 кодировки, которая представляет из себя строку из нескольких десятков символов латиницы и цифр. Например:

tDxPhycF7mRLga9e5U/YQgE1FZpZjdKcNkwxIfZikXI=

Настройка сервера и клиента

Wireguard умеет работать в разных режимах: точка-точка, звезда и mesh-сеть. Однако, поскольку в данной заметке рассматривается вариант использования Wireguard в схеме «звезда», т.е. имеется некий центральный сервер и его клиенты, то далее я буду использовать термины «сервер» и «клиент». Сервером является узел, который ожидает подключения других узлов, при этом сам никуда не подключается. Клиент подключается к серверу, но сам не ожидает подключений от других узлов с Wireguard.

Wireguard может быть настроен как вручную с использованием команды wg, так и с помощью утилиты wg-quick и написания файлов конфигурации для неё, которые будут содержать все необходимые настройки. В данной заметке рассматривается второй способ с использованием wg-quick. Кроме этого, в современных дистрибутивах wq-quick может быть легко запущена автоматически с нужной конфигурацией с помощью systemd или скриптов для системы инициализации вашего дистрибутива.

Настройки wg-quick в Ubuntu и Fedora находятся в папке /etc/wireguard. В других дистрибутивах путь может отличаться. В этой папке необходимо создать файл с расширением conf и именем сетевого интерфейса для соединения. Пример файла wg0.conf для сервера:

[Interface]
Address = 192.168.0.1/24
PrivateKey = SERVER_PRIVATE_KEY
ListenPort = 51820

[Peer]
PublicKey = CLIENT_PUBLIC_KEY
AllowedIPs = 192.168.0.2

В данной конфигурации создаётся виртуальная сеть с адресацией 192.168.0.0/24, адрес сервера в этой сети 192.168.0.1. Параметр ListenPort указывает на то, что это сервер и он будет прослушивать порт 51820 на сетевых интерфейсах для входящих подключений. Сервер будет использовать закрытый ключ SERVER_PRIVATE_KEY, который в реальном файле конфигурации необходимо заменить на соответствующую строку с закрытым ключом сервера.

Секция [Peer] описывает клиента и может быть использована несколько раз для разных клиентов. В секции указан публичных ключ клиента (CLIENT_PUBLIC_KEY) и адреса узлов, которые могут присылать пакеты через соединение (AllowedIPs), в данном случае, это адрес удалённой машины. Вместо CLIENT_PUBLIC_KEY укажите строку с публичным ключом клиента.

Пример файла конфигурации wg0-client.conf для клиента:

[Interface]
Address = 192.168.0.2
PrivateKey = CLIENT_PRIVATE_KEY

[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 192.168.0.0/24
Endpoint = SERVER_NAME:51820
PersistentKeepalive = 20

Клиентская конфигурация незначительно отличается от серверной. В секции [Interface] указан IP адрес или несколько адресов клиента, в том числе IPv6, в виртуальной сети и закрытый ключ клиента (CLIENT_PRIVATE_KEY). Секция [Peer] содержит публичный ключ сервера (SERVER_PUBLIC_KEY). В AllowedIPs указываются диапазоны сетей, которые будут маршрутизироваться через данное соединение. В данном случае указана сеть, которая формируется сервером Wireguard - 192.168.0.0/24. Endpoint указывает на адрес (SERVER_NAME) и порт сервера. Параметр PersistentKeepalive необходим для стабильного соединения, если вы находитесь за брандмауэром или NAT. Клиент будет периодически отправлять пакет на сервер, что обеспечит обновление данных об активных соединениях. Вместо значений CLIENT_PRIVATE_KEY, SERVER_PUBLIC_KEY и SERVER_NAME необходимо указать соответствующие строковые значения.

Дополнительное шифрование рукопожатия

Есть возможность сгенерировать ещё один ключ, который будет использоваться при рукопожатии сервера и клиента:

wg genpsk > psk.key

Сгенерированный ключ необходимо добавить в конфигурацию в раздел [Peer], параметр PresharedKey на стороне сервера и стороне клиента.

Проверяем подключение

Протестировать соединение можно с помощью утилиты wg-quick, например:

# wg-quick up wg0-client
[#] ip link add wg0-client type wireguard
[#] wg setconf wg0-client /dev/fd/63
[#] ip -4 address add 192.168.0.2 dev wg0-client
[#] ip link set mtu 1420 up dev wg0-client
[#] ip -4 route add 192.168.0.0/24 dev wg0-client

Из текста сообщений видно, что адрес из значения Address был присвоен интерфейсу туннеля, а диапазон AllowedIPs был использован для создания нового маршрута.

Обеспечить автоматический запуск сервера и/или клиента можно с помощью команды:

systemctl enable --now wg-quick@wg0

При этом wg0 - это имя соединения: wg0 - сервер, wg0-client - клиент. Ключ --now позволит сразу запустит службу после её активации.

При тестировании Wireguard через соединение IPv6 столкнулся с проблемой, что соединение устанавливается, адреса становятся доступными, однако обмена информацией нет. Вся проблема оказалась в блокировке входящих пакетов UDP со стороны провайдера. Если у вас что-то не работает, убедитесь, что UDP пакеты успешно доставляются в обе стороны.

Превращаем сервер в шлюз

Для превращения вашего сервера в шлюз необходимо включить NAT. Это можно сделать разными способами, в том числе с использованием iptables или Firewalld.

Использование iptables

При использовании iptables необходимо в секцию [Interface] сервера добавить следующие параметры:

PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE

где ens3 - это ваш сетевой интерфейс, который имеет белый IP адрес. Вместо режима MASQUERADE, конечно, можно применить другие методы NAT.

Настройка Firewalld

В отличии от iptables, Firewalld может быть не установлен в вашей операционной системе. Fedora использует Firewalld. В Ubuntu 20.04 LTS и 22.04 LTS может быть установлен из репозитория universe с помощью команды

sudo apt install firewalld

Как показала практика, настройки по умолчанию не блокируют порт 22, поэтому даже если Firewalld устанавливается на удалённую машину, доступ к ней вы не потеряете. Кроме этого, можно не активировать сервис firewalld, а только запустить его

sudo systemctl start firewalld

В случае проблем вы можете просто перезагрузить ваш сервер и он запустится без firewalld. Когда вы будете уверены, что всё работает как надо, можете включить сервис:

sudo systemctl enable firewalld

После этого, при следующей перезагрузке, он будет запущен автоматически.

Чтобы включить NAT, необходимо на сетевом интерфейсе, который смотрит в интернет, включить режим masquerade. Это можно сделать вручную выполнив команду

firewall-cmd --permanent --zone=ZONE --add-masquerade

где ZONE - это имя зоны, в которой находится внешний интерфейс. Однако делать этого не обязательно, потому что firewalld имеет типовые настройки для часто встречающихся вариантов. Например, зона external соответствует нашим требованиям. Просто необходимо добавить наш сетевой интерфейс в эту зону и разрешить из неё доступ к Wireguard

firewall-cmd --permanent --zone=external --add-interface ens3
firewall-cmd --permanent --zone=external --add-service wireguard

Если вы используете Firewalld до версии 1.0 или нестандартный номер порта, например, 35053, то вместо использования имени службы необходимо указать номер порта:

firewall-cmd --permanent --zone=external --add-port 35053/udp

Для VPN сети можно выбрать зону internal

firewall-cmd --permanent --zone=internal --add-interface wg0

Чтобы изменения вступили в силу, выполняем

firewall-cmd --reload

Начиная с Firewalld 1.0 по умолчанию блокируется транзитный трафик между зонами. Необходимо разрешить его с помощью policy object, например, создав правило client-to-inet:

firewall-cmd --permanent --new-policy client-to-inet
firewall-cmd --permanent --policy client-to-inet --set-target ACCEPT
firewall-cmd --permanent --policy client-to-inet --add-ingress-zone internal
firewall-cmd --permanent --policy client-to-inet --add-egress-zone external
firewall-cmd --reload

Теперь все пакеты, которые будут проходить из зоны internal в external будут подвергнуты NAT. При необходимости, на Wireguard клиенте разрешить получение любых пакетов через соединение с помощью параметра AllowedIPs

AllowedIPs = 0.0.0.0/0

и вы получите возможность перенаправить весь трафик вашего устройства через ваш сервер, т.е.получить именно тот самый VPN для доступа в интернет, который сейчас так часто рекламируют.

Ещё немного о Firewalld

Наиболее часто используемую зону можно использовать в качестве зоны по умолчанию, тогда её можно не указывать в командах

firewall-cmd --set-default-zone=external

После запуска Firewalld могут возникнуть проблемы с доступом к другим службам на сервере. Для них необходимо открыть доступ. Наиболее оптимальный путь - разрешить необходимые вам службы. Список служб можно получить с помощью команды

firewall-cmd --get-services

Детальная информация об этих службах содержится в XML файлах в папке /usr/lib/firewalld/services. Разрешите доступ, например, к вашему веб-серверу по HTTP и HTTPS протоколам

firewall-cmd --permanent --zone=external --add-service=http
firewall-cmd --permanent --zone=external --add-service=https

Если подходящих вариантов не нашлось, то можно открыть порт непосредственно по номеру, например, 1080 по протоколу TCP

firewall-cmd --permanent --zone=external --add-port 1080/tcp

Использование адресации IPv6

В целом Wireguard полноценно поддерживает протокол IPv6. Вы можете использовать адреса в формате IPv6 и всё будет прекрасно работать. Однако если вы хотите получить публичный адрес IPv6 через Wireguard то можете столкнуться с небольшими проблемами.

Большинство используемых мною сейчас или ранее хостинг провайдеров не делегируют префикс на виртуальные машины. По сути виртуальная машина является обычным узлом в сети провайдера. На ней можно включить режим маршрутизатора с помощью sysctl. Возможно также понадобится активировать получение анонсов от маршрутизатора, которые перестают приниматься после включения маршрутизации для IPv6

net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.ens3.accept_ra = 2

Однако в большинстве моих случаев хостинг провайдер не направляет трафик с адресами моей сети непосредственно на виртуальную машину. Чтобы трафик в мою сторону был направлен, мне необходимо отвечать на запросы поиска соседей (NDP) от маршрутизатора. В этом случае, если маршрутизатор получит ответ от моей виртуальной машины, что адресом владеет она, трафик будет отправлен на неё.

Самым простым вариантом будет использование пакета ndppd. Не смотря на то, что этот пакет умеет проксировать запросы NDP, нам это не понадобится. Чтобы трафик IPv6 с глобальными адресами спокойно ходил из интернета к клиенту VPN необходимо сообщить маршрутизатору провайдера, что адрес находится у нас.

proxy ens3 {
    rule 2a12:5940:123:feee::abcd:ef01/128 {
        static
    }
}

В данном случае 2a12:5940:123:feee::/64 может быть сетью, которую вам выделил провайдер хостинга, так и частью большего выделенного вам хостинг провайдером диапазона 2a12:5940:123::/48. Например, провайдер Aeza даёт сеть /48 на виртуальную машину. Поскольку префикс нам не делегирован, особой разницы между сетями /64 и /48 нет. Разве что для удобства мы можем выделить для VPN свою отдельную сеть /64.

При такой настройке ваш хостинг-провайдер при получении трафика для данного адреса сделает запрос NDP и получит ответ, что данный адрес расположен на вашем хосте. Пакет успешно будет передан на нашу виртуальную машину, после чего она передаст этот пакет клиенту через VPN соединение.

Конечно, можно указать маску больше 128, но маска больше 120 вызывает предупреждение о том, что не рекомендуется использовать такие большие диапазоны. При этом всё равно ndppd будет успешно работать, так что если вы будете так делать, то делайте это на свой страх и риск.

Аналогично подобные правила (rule) необходимо добавить для каждого адреса, который будет использован VPN клиентами.

Передача конфигурации VPN на смартфон

Клиент Wireguard для смартфона умеет считывать qr-код с конфигурацией. Чтобы сгенерировать код не нужен даже графический режим. Просто создайте файл конфигурации для вашего клиента и отобразите его в виде QR-кода в консоли с помощью команды qrencode, которая в Ubuntu и Fedora устанавливается с помощью пакетов с именем qrencode.

qrencode -t ansiutf8 < wg-client.conf

wg-client.conf - это имя файла содержимым конфигурации, которую необходимо передать на другое устройство.

Управление подключениями через NetworkManager

Возможно в вашей операционной системе уже установлен NetworkManager. Он может без проблем импортировать конфигурацию Wireguard и управлять этим подключением. Всё, что нужно сделать, импортировать конфигурацию для wg-quick:

nmcli connection import type wireguard file wg0.conf

Новое соединение получит наименование такое же, как было у файла конфигурации — wg0.

После того, как соединение импортировано им можно управлять из графического интерфейса или командной строки:

nmcli connection up wg0

чтобы включить или

nmcli connection down wg0

чтобы выключить.

При использовании NetworkManager необходимости запускать сервисы wg-quick нет.

Заключение

В результате настройки клиента и сервера мы получили виртуальную сеть между двумя узлами. С помощью этого соединения можно получить доступ к ресурсам, которые ранее были недоступны. При этом при обмене данными между клиентом и сервером через VPN можно использовать даже нешифрованные соединения, поскольку весь трафик, который доступен по пути следования пакетов VPN, скрыт от любопытных глаз шифрованием.


Обратите внимание, что заметки могут обновляться со временем. Это может быть как исправление найденных ошибок, так и доработка содержания с целью более полного раскрытия темы. Информация об изменениях доступна в репозитории на github. Там же вы можете оставить в Issue ваши замечания по данной заметке.


Если данная заметка оказалась вам полезной, можете поблагодарить автора финансово.