Сайт FSA
18.05.2023

Свой VPN с поддержкой IPv6 на базе Wireguard

Данная заметка была изначально опубликована на Хабре: https://habr.com/ru/articles/752866/

Иногда возникает необходимость получить доступ к сайтам, которые доступны только по IPv6, а ваш провайдер не предоставляет вам такой возможности. Одним из выходов может стать VPN. Однако покупать VPN где-то на стороне при наличии собственного VPS сервера, где интернет работает нормально, просто лишняя трата денег. В таком случае возникает вопрос: а как, собственно, организовать этот самый VPN? В интернете есть множество инструкций, но они обычно не затрагивают тему протокола IPv6 или затрагивают, но там используются спорные решения.

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

Если с IPv4 всё понятно, поскольку большинство руководств сходятся к решению с использованием серой адресации с организацией NAT на сервере. По сути, это стандартная схема для IPv4 адресации. Адресов IPv4 мало и стоят они дорого, поэтому никто, в здравом уме, не будет использовать «белые» адреса непосредственно на устройствах, если, конечно, это не сервер, который доступен в интернете. А вот с IPv6 не всё так однозначно.

Сам по себе протокол IPv6 строится на том, что никому не потребуется использовать NAT. При этом, это не означает, что сам по себе NAT в IPv6 недоступен. NAT для IPv6 доступен (обычно его называют NAT66), но его использование не рекомендуется, а в некоторых случаях это даже вредно. К тому же всегда есть возможность его не использовать, в противовес IPv4.

Выдавать IPv6 адреса хостинг-провайдеры могут по разному:

  1. Выдача IPv6 адресов поштучно. Самый странный вариант. Если у вашего хостинг-провайдера так, то, скорее всего, стоит просто сменить его на другого, даже к контексте использования VPS для обычного сервера, не говоря уже об использовании как VPN. С некоторой долей вероятности тоже можно получить VPN с IPv6, но далее этот вариант просто не рассматривается. После прочтения материала вы сами сможете понять что вам нужно делать, если вы всё-таки решитесь использовать подобного провайдера.
  2. Выдача сети /64. Наиболее часто встречающийся вариант для мелких провайдеров. Иногда есть возможность через поддержку получить сеть /63 или, даже, ещё большего размера, что будет рассмотрено в следующем варианте. Наличие только /64 не самый хороший вариант для VPN, но с ним уже можно работать.
  3. Вариант не особо отличается от предыдущего, но вместо сети /64 вам выдают сеть большего размера (до /48), т.е. в вашем распоряжении оказывается адресация некоторого количества сетей /64. Например при /48 - 65536 штук.
  4. Маршрутизация определённого префикса IPv6 на вашу виртуальную машину. Самый редкий вариант среди мелких провайдеров, в публичном доступе такое почти не встречается, но, возможно есть возможность получить префикс через техническую поддержку. Этот вариант подразумевает, что ваш хостинг провайдер прописал маршрут на ваш виртуальный сервер для выделенного вам префикса. Вам может быть выдана сеть от /64 до /48, которые без костылей могут быть использованы вами для организации VPN.

Сложность настройки VPN постепенно уменьшается от 1 до 4 варианта, поэтому начнём с 4 варианта и постепенно поднимемся вверх.

Шаг 1: Включение маршрутизации на сервере VPN

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

Для IPv4 маршрутизация включается с помощью sysctl путём установки

net.ipv4.ip_forward=1

Для IPv6 это делается аналогично:

net.ipv6.conf.all.forwarding = 1

Но есть одно НО! Если ваша машина получает адреса в автоматическом режиме, то после включения маршрутизации она перестанет принимать анонсы маршрутизаторов, а значит, больше не сможет получить адрес в автоматическом режиме. Чтобы это исправить, включите

net.ipv6.conf.ens3.accept_ra = 2

В данном примере ens3 - это сетевой интерфейс нашего VPS, откуда мы хотим принимать анонсы маршрутизатора. На большинстве хостингов назначение IPv6 адресов на VPS производилось путём ручной настройки, а значит и принимать RA не требуется, т.е. данную настройку можно пропустить.

Теперь осталось выделить сеть /64 из доступного вам диапазона, назначить один из адресов на интерфейс Wireguard на VPS, а также выдать каждому клиенту по IPv6 адресу из этого же диапазона. Всё прописывается в конфигурации сервера и клиентов. На этом настройка VPN для 4 варианта будет закончена.

Шаг 2: Обеспечиваем маршрутизацию пакетов для сервера без выделенного префикса

Теперь перейдём к варианту, когда нам выделено, в лучшем случае, от /63 до /48, в худшем /64. Сначала рассмотрим лучший вариант.

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

В IPv6 есть функция обнаружения соседей. Это означает, что если хост хочет с кем-то связаться, то в первую очередь отправляет запрос на поиск соседа с заданным адресам. Если будет получен ответ, то хост сможет направить данные напрямую, минуя маршрутизатор. Если хост назначения недоступен, то узел воспользуется таблицей маршрутизации и отправит пакет на соответствующий маршрутизатор. Для IPv4 эта ситуация немного необычна. А в IPv6 пакеты между разными диапазонами адресов могут ходить напрямую между хостами, а также, существуют ситуации, когда даже хосты в одной сети будут общаться через маршрутизатор.

Поскольку мы назначили для своего сервера сеть /64, то при попытке маршрутизатора провайдера найти соседа с адресом, который не попадает в данную подсеть, наш хост будет эти запросы игнорировать. Чтобы этого не происходило, можно воспользоваться специальной службой NDP proxy. Этот служба может пересылать запросы на поиск соседей и возвращать ответы и отвечать на указанные запросы положительным ответом. К сожалению, Wireguard работает на более высоком сетевом уровне, поэтому единственным вариантом для нас является просто выдача положительных ответов при запросе адресов, которые используются в VPN.

Одним из вариантов решения задачи - это установка пакета ndppd. Пакет точно доступен в стандартных поставках Ubuntu и Fedora в официальных репозиториях. Установите его с помощью вашего пакетного менеджера. Конфигурация ndppd достаточно простая. Файл конфигурации сервиса расположен по адресу /etc/ndppd.conf. Содержимое этого файла должно быть примерно таким:

proxy ens3 {
    rule 2001:0db8:827:fcde:cafb:073d:a65e:25b0 {
        static
    }
}

Данная конфигурация означает, что если приходит запрос на интерфейс ens3 с запросом соседа по адресу 2001:0db8:827:fcde:cafb:073d:a65e:25b0, то необходимо ответить, что это наш адрес. Записи rule можно повторить несколько раз и, даже, указать в каждой записи не отдельный адрес, а целый диапазон. Однако сам ndppd не приветствует широкую маску сети. Если клиентов VPN немного, лучше для каждого из них создать свою запись.

Шаг 2.1: Что делать, если у нас только сеть /64

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

Поскольку мы используем VPS, то вряд ли будем использовать механизм SLAAC, которы работает только с сетями /64. К тому же, в большинстве случаев, адресация для VPS прописывается вручную, просто потому, что у сервера должен быть строго определённый адрес. Исходя из этого можно разделить сеть /64 на сети меньшего размера. Как делить? Если вы не планируете запускать на сервере другие сервисы VPN, то самым оптимальным вариантом являются сети /65, т.е. сеть /64 мы делим пополам. В этом случае для клиентов VPN нам доступны почти все биты из диапазона /64, кроме старшего. При таком разделении наш адрес будет выглядеть как обычный адрес, который, например, мог бы быть выделен через SLAAC.

Если кроме Wireguard вы хотите, например, также запустить на вашем сервере ещё и OpenVPN, то вам может понадобиться ещё одна сеть для этого сервиса. В таком случае вы можете выбрать сеть /66. Конечно, в этом случае вам нужно учитывать уже не один старший бит, а два. Можно и дальше сужать сети, но при этом адреса ваших клиентов будут уже не так похожи на адреса, которые обычно выделяются при использовании механизма SLAAC. Вариант отличные от /65 вы можете организовать самостоятельно.

Чтобы ваш сервер обеспечил маршрутизацию пакетов, которые необходимо будет отправить клиенту VPN, необходимо урезать сеть в настройках сетевого интерфейса. Вместо /64 указываем выбранную маску, например, /65. После этого настройка вашего VPN сервера не отличается от настроек, которые мы делали на шаге 2. Конечно, маску /65 нужно указать и в настройках Wireguard на вашем сервере.

Практическая часть

Дабы не вдаваться в подробности, будем считать, что вы ознакомились с моей «Шпаргалкой по Wireguard» или любой другой инструкции, которых очень много в интернете, и вам не надо объяснять как генерировать ключи.

Включаем маршрутизацию на VPS путём установки параметров, например, путём добавления файла /etc/sysctl.d/20-vpn.conf и загрузки их через sysctl -p или путём перзагрузки VPS:

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding = 1

Сгенерируйте пару ключей для своего VPN сервера, например так

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

Для дополнительной безопасности для каждого клиента может быть сгенерирован ключ PSK, но это не обязательно:

wg genpsk > psk.key

Аналогичным образом сгенерируйте ключи для всех ваших клиентов. После этого можно сформировать серверную конфигурацию для Wireguard.

[Interface]
Address = 192.168.0.1/24, 2001:0db8:827:fcde::1/64
PrivateKey = <SERVER_PRIVATE_KEY>
ListenPort = <SERVER_PORT>

[Peer]
PublicKey = <CLIENT_PUBLIC_KEY>
AllowedIPs = 192.168.0.2, 2001:0db8:827:fcde::1ce:1ce:babe
PresharedKey = <PRESHARED_KEY>

Не сложно догадаться, что <SERVER_PRIVATE_KEY> - это закрытый ключ сервера, а <CLIENT_PUBLIC_KEY> - это публичный ключ клиента. 192.168.0.0/24 - это сеть IPv4 для нашего подключения, а адрес 192.168.0.1 будет присвоен интерфейсу Wireguard. По аналогии, 2001:0db8:827:fcde::/64, соответственно сеть IPv6, а адрес 2001:0db8:827:fcde::1 - адрес интерфейса Wireguard. Опция PresharedKey не обязательная, но если она указана на сервере, то она также должна быть указана в конфигурации клиента, значение ключей на сервере и клиенте должно совпадать.

Секция [Peer] должна быть создана для каждого клиента. В AllowedIPs должны быть указаны IP адреса и сети, которые мы ожидаем от клиента.

Конфигурации клиентов должны иметь вид

[Interface]
Address = 192.168.0.2, 2001:0db8:827:fcde:beef:1ce:1ce:babe
PrivateKey = <CLIENT_PRIVATE_KEY>

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
AllowedIPs = 0.0.0.0/0, 2000::/3
Endpoint = <SERVER_NAME>:<SERVER_PORT>
PresharedKey = <PRESHARED_KEY>
PersistentKeepalive = 20

Аналогично, на клиенте: <CLIENT_PRIVATE_KEY> - закрытый ключ клиента, <SERVER_PUBLIC_KEY> - публичный ключ сервера. В Endpoint необходимо указать адрес <SERVER_NAME> и порт <SERVER_PORT> сервера, которые соответствуют указанным параметрам в настройках сервера. Параметр PresharedKey должен присутствовать, если он есть в секции [Peer] для этого клиента, содержимое ключа должно совпадать.

PersistentKeepalive может также отсутствовать. Но если указано, то клиент каждое указанное количество секунд будет отправлять пакет данных. Это полезно в тех случаях, когда клиент находится за NAT или Firewall, которые при отсутствии активности могут запретить удалённому хосту присылать вам ответы.

Чтобы обеспечить доступ клиентов в интернет по протоколу IPv4, необходимо включить NAT, например, с помощью скриптов, которые необходимо добавить в секцию [Interface] на сервере, и отключить их, если клиенты отключаются.

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

Автоматизация

Конечно, делать все необходимые настройки руками, особенно если новый клиент вашего VPN появляется не часто, это просто сущее наказание. Поэтому я задумался об автоматизации. В качестве базы был найден скрипт nebulakl/wireguard-config-generator. Он не совсем подходил под мои нужды, поэтому я его переделал и выложил по в репозитории fsa/wireguard-config-generator.

Скрипт позволяет генерировать конфигурацию для сервера VPN на базе Wireguard. Далее можно генерировать необходимое количество конфигураций клиентов. Скрипт сохранил возможность использования NAT66, как было в оригинальном скрипте, но включение NAT66 производится явно через редактирование конфигурации сервера. По умолчанию скрипт использует «серую» адресацию, как в оригинальном скрипте.


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


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