Вдохновившись аргументами из статьи «IPv6 — прекрасный мир, стоящий скорого перехода на него», мне стало катастрофически не хватать IPv6. Конечная цель: обеспечить каждое из своих устройств уникальным публичным псевдостатическим IPv6.
Для дома решение довольно простое: при наличии статического IPv4, можно получить подсеть IPv6 от туннельного брокера и на этом вопрос можно считать закрытым.
А вот с мобильными устройствами так не получится: услуга статического IPv4 тут скорее редкость; да и при наличии бесплатного WiFi я выберу подключение к нему, а не к мобильной сети.
Остаётся единственный вариант — использовать VPN. Существующие на GitHub решения типа “wireguard-install” или “openvpn-install” имеют фатальный недостаток: они отдают клиенту единственный IPv6/128 адрес, а чтобы у клиентов появилась возможность автоконфигурации, нужна целая /64 подсеть.
Предлагается решение на основе WireGuard.
Вам понадобятся
Особый виртуальный сервер. Для реализации задуманного, нужно купить VDS, которому хостер согласится отдать хотя бы /60 подсеть. Как оказалось, найти такое — достаточно сложная задача: в худшем случае, хостеры почему-то продают IPv6 поштучно; в лучшем — только /64 подсети. В результате долгих поисков и благоприятного стечения обстоятельств, был найдена компания Melbicom, которая способна выделить /56 подсеть на VDS.
Linux + wireguard + udppd + nftables. На моём сервере установлен ArchLinux, но описанное решение может быть воспроизведено и на других дистрибутивах.
Условные обозначения
ens3
– имя сетевого интерфейса.
Далее в тексте используются следующие «IP» адреса:
2a06:X:X:a::2/128
и213.x.x.2/24
– IPv6 и IPv4 адреса VDS. Назначаются хостером автоматически и бесплатно к каждому приобретаемому VDS.2a06:X:X:100::/56
– Маршрутизируемая до VDS подсеть /56 IPv6. Заказывается отдельно.2a06:X:X:111::/64
– подсеть для клиента (мобильного устройства), которая выделяется из заказанной выше /56 подсети.2a06:X:X:111:a:b:c:d/64
– пример IPv6 клиента (мобильного устройства), гдеa:b:c:d
(interface id) выбирается клиентом самостоятельно.
Где заглавная буква X
означают одну группу шестнадцатеричных цифр IPv6 адреса, а строчная x
- одну группу десятичных цифр IPv4 адреса.
Тестирование наличия выделенной /56 подсети
Изначально, на сервере, сеть не должна быть настроена, но должна маршрутизироваться до него. Содержимое /etc/systemd/network/ens3.network выглядит примерно так:
[Match]
Name = ens3
[Network]
Address = 213.x.x.2/24
Gateway = 213.x.x.1
Address = 2a06:X:X:a::2/128
Gateway = 2a06:X:X:a::1
Для проверки, выполним команду: tcpdump ip6 -nq -i ens3
Далее, пингуем любой IPv6 из выделенной подсети, например 2a06:X:X:111:a:b:c:d
. Если всё хорошо, то в дампе появятся строки типа:
19:14:39.689842 IP6 fe80::XX > ff02::XX ICMP6, neighbor solicitation, who has 2a06:X:X:111:a:b:c:d, length 32
Установка ndppd
Ndppd — это демон, задача которого слушать интерфейс: ожидать «neighbor solicitation» и отвечать «router advertisement» Таким образом, вышестоящий роутер узнает, куда адресовать трафик выделенной подсети. Далее этот трафик через wireguard будет передан клиенту.
К сожалению, в ArchLinux ndppd пока доступен только из AUR. Если в вашем дистрибутиве он доступен из репозиториев, переходите к следующем разделу.
Для начала ставим все необходимые компоненты: pacman -Suy git base-devel
Далее создаём пользователя, под которым будем все это собирать:useradd -m router
Переходим в пользователя:su router
cd /home/router
Теперь из-под пользователя выполняем команды:git clone https://aur.archlinux.org/ndppd.git
cd ndppd
makepkg
Возвращаемся в root:exit
Переходим в каталог ndppd:cd /home/router/ndppd
Находим в каталоге ndppd-*.zst файл и устанавливаем его:pacman -U ndppd-0.2.5-1-x86_64.pkg.tar.zst
Настройка ndppd
Создаём файл /etc/ndppd.conf с содержимым:
proxy ens3 {
rule 2a06:X:X:100::/56 {
static
}
}
Где:
ens3
– имя интерфейсаstatic
- опция, которая сообщает ndppd, что он должен немедленно отвечать на Neighbor Solicitation, без дополнительных проверок.
Подробное описание параметров изложено в документации.
Далее активируем сервис:systemctl enable ndppd
и запускам:systemctl start ndppd
Установка системных переменных
Чтобы заработала пересылка IPv6 пакетов между интерфейсами, необходимо в каталоге /etc/sysctl.d/ создать файл wireguard.conf с содержимым:
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
Первая строка включает пересылку IPv4, вторая — IPv6. Подробное описание каждого из параметров можно узнать из документации.
Применяем настройки:sysctl --system
Настройка WireGuard на сервере
Создаём публичный и приватный ключи сервера:wg genkey | tee server-private.key | wg pubkey > server-public.key
Создаём публичный и приватный ключи клиента:wg genkey | tee client1-private.key | wg pubkey > client1-public.key
Создаём pre-shared key:wg genpsk > pre-shared.key
Создаём файл /etc/wireguard/wg0.conf:
[Interface]
SaveConfig = false
ListenPort = 52525 #Может быть любым
PrivateKey = #содержимое server-private.key#
Address = 10.20.30.1/24, 2a06:X:X:100::/56
[Peer]
PublicKey = #содержимое client1-public.key#
PresharedKey = #содержимое pre-shared.key#
AllowedIPs = 10.20.30.17/32, 2a06:X:X:111::/64
Активируем и запускаем демона wireguard:systemctl enable wg-quick@wg0 && systemctl start wg-quick@wg0
В случае изменения конфигурации, перезапуск удобно выполнять командой:wg-quick down wg0 && wg-quick up wg0
Настройка nftables
Поскольку iptables постепенно выходит из употребления, я предпочитаю nftables.
Установка командой:pacman -S nftables
Для конфигурации нужно создать или отредактировать файл /etc/nftables.conf :
#!/usr/sbin/nft -f
# Start by flushing all the rules.
flush ruleset
# Документация https://man.archlinux.org/man/nft.8
# Определение переменных: интерфейсы и виртуальная локальная IPv4 подсеть
define wan = ens3
define vpn = wg0
define vpn_net4 = 10.20.30.0/24
# Общие настройки для IPv6 и IPv4
table inet wg-common {
# Все пересылаемые пакеты по умолчанию дропаем, за исключением тех, которые идут между vpn и wan
chain forward {
type filter hook forward priority 0; policy drop;
iifname $wan oifname $vpn accept;
oifname $wan iifname $vpn accept;
}
chain input {
type filter hook input priority 0;
# allow established/related connections
ct state {established, related} accept
# early drop of invalid connections
ct state invalid drop
# allow from loopback
iifname lo accept
# allow icmp
ip protocol icmp accept
meta l4proto ipv6-icmp accept
# allow ssh
tcp dport ssh accept
# Разрешаем вкходящие соединения с WireGuard
udp dport 52525 accept;
# everything else
reject with icmpx type port-unreachable
}
}
# Настройка NAT для IPv4
table ip wg-nat {
# Все исходяющие пакеты IPv4 c подсети vpn на интерфейс wan подвергаются маскараду
chain pre {
type nat hook postrouting priority 0;
oifname $wan ip saddr $vpn_net4 masquerade persistent;
}
}
Настройка WireGuard на клиенте
Конфигурация клиента выглядит так:
[Interface]
PrivateKey = #содержимое client1-private.key#
Address = 10.20.30.17/32, 2a06:X:X:111:a:b:c:d/64
DNS = 9.9.9.9, 149.112.112.112, 2620:fe::fe, 2620:fe::9
[Peer]
PublicKey = #содержимое server-public.key#
PresharedKey = #содержимое pre-shared.key#
AllowedIPs = 2000::/3
Endpoint = #Публичный IP сервера, или домен#:52525
PersistentKeepalive = 25
На этом конфигурация завершена. Всё, что осталось это установить на телефон WireGuard, импортировать полученный конфиг, включить и пользоваться IPv6. Важной особенностью тут является то, что в IPv6 адресе клиента вместо a:b:c:d
можно указать абсолютно любой interface id. Такой себе ручной SLAAC. А если хочется пустить через wireguard ещё и весь IPv4 трафик, в поле AllowedIPs
добавьте подсеть 0.0.0.0/0
.
Бонус: обход блокировок
Если из-за политики государства, часть Интернета заблокирована, при помощи клиента WireGuard на роутере с OpenWRT можно обойти блокировку.
Для этого переносим конфиг клиента на роутер с OpenWRT, указав в параметре AllowedIPs
заблокированные подсети. Например, чтобы разблокировать Яндекс из Украины, нужно указать:
AllowedIPs=5.45.192.0/18,5.45.196.0/24,5.45.202.0/24,5.45.205.0/24,5.45.213.0/24,5.45.229.0/24,5.45.240.0/24,5.255.192.0/18,5.255.195.0/24,5.255.196.0/24,5.255.197.0/24,5.255.255.0/24,37.9.64.0/18,37.9.64.0/24,37.9.73.0/24,37.9.112.0/24,37.140.128.0/18,77.88.0.0/18,77.88.8.0/24,77.88.35.0/24,77.88.44.0/24,77.88.54.0/24,77.88.55.0/24,87.250.224.0/19,87.250.247.0/24,93.158.128.0/18,95.108.128.0/17,100.43.64.0/19,141.8.128.0/18,141.8.174.0/24,178.154.128.0/19,178.154.131.0/24,178.154.160.0/19,185.32.187.0/24,199.21.96.0/22,199.36.240.0/22,213.180.192.0/19,213.180.199.0/24,2a02:6b8::/29,2620:10f:d000::/44
Список подсетей Яндекса взят отсюда https://ipinfo.io/AS13238
IPv4 заработает сразу. А чтобы заработал IPv6, нужно при помощи nat6 подменить префикс клиента. В разделе Firewall :: Custom Rules допишем следующие пользовательские правила:
ip6tables -t nat -A POSTROUTING -d 2a02:6b8::/29 -j NETMAP --to 2a06:X:X:111::/64
ip6tables -t nat -A POSTROUTING -d 2620:10f:d000::/44 -j NETMAP --to 2a06:X:X:111::/64
После этого можно перейти на https://yandex.ru/internet/ и убедиться, что всё работает.
Evengard
Извините, но зачем подсеть /56? Вполне можно и /64 выдаваемую серверу распределить. В чём принципиальная проблема?
Плюс на самом деле SLAAC не гарантирует "статичности" IPv6, по той простой причине что EUI-64 может (и очень часто устройствами таки это делается, причём это где-то даже в спеке прописано) быть рандомизирован.
Как вариант (который я к слову практиковал) можно вполне пробросить те же IPv6 туннельного брокера на свой VPS (tunnelbroker так то вообще позволяет /40 получить).
Ещё надо быть очень аккуратным с
net.ipv6.conf.all.forwarding=1
- эта штука на самом деле отключает приём Router Advertisement-ов на уровне ядра на всех интерфейсах, так что если у вас перенаправляющий роутер должен конфигурироваться тоже на основе SLAAC/DHCPv6 - то для интерфейса который так конфигурируется - его надо обязательно отключать (например какnet.ipv6.conf.ens5.forwarding=0
), а то IP вы не получите.Balling
>быть рандомизирован
Нет, не должен быть рандомизирован. Можно выбрать любой адрес, какой вам нравится из /64.
Это называется ip token. https://serverfault.com/questions/968641/configure-ipv6-address-on-interface-with-static-iid
Evengard
Я скорее имел ввиду https://datatracker.ietf.org/doc/html/rfc4941. Большинство устройств реализуют что-то подобное. Да, при большом желании можно зафиксировать EUI, но по умолчанию большинство устройств его таки рандомизируют.
Sunvas Автор
Предлагаете нарезать её на более мелкие подсети?
Только если очень не хочется менять хостера. Ещё один туннель неминуемо увеличит пинг.
Не в моём случае. Отключать не обязательно, можно воспользоваться опцией
accept_ra=2
.Evengard
A зачем нарезать на более мелкие подсети-то? В чём проблема прямо её и распределять?
Sunvas Автор
Уточните, о какой именно /64 подсети идёт речь?
Вы предлагаете одну подсеть давать разным устройствам?
Evengard
У вас VPS исключительно для IPv6 VPN, я не вижу проблем выдавать конечным устройствам конфигурироваться на основе /64 подсети выдаваемой хостером. На самом деле даже если не исключительно для VPN, всё равно, вероятность что настроенный на самом VPS X:X:X:X::1 сконфликтует со сгенеренным X:X:X:X::/64 по SLAAC-у настолько мала, что практически отсутствует (если только вы там статические адреса не настроили кому-то. Но даже если настроили, то опять таки вероятность конфликта SLAAC-а с каким-нибудь статическим X:X:X:X::1:Y чуть выше, но всё равно исчезающе мала).
Sunvas Автор
А кто будет заниматься маршрутизацией на уровне VDS? Если на случайный interface id придёт трафик, кому он достанется?
Evengard
В смысле? Объедините все wg* интерфейсы в bridge, вы итак уже systemd-networkd используете, там это делается тривиально. Или вы про что?
Sunvas Автор
У меня только 1 wg0 интерфейс.
Evengard
Тогда я не понял вашего последнего вопроса.
Sunvas Автор
Под
interface id
я понимал правую часть IPv6 адреса.В случае, если одна /64 IPv6 подсеть будет роздана двум устройствам, которые (например) выберут себе IP ::2 и ::3, откуда VDS узнает кому какой трафик слать? Теоретически, в этом случае, пиры смогут общаться между собой, чего мне хотелось бы избежать.
Evengard
Эм, вы итак предоставляете глобальные IPv6 адреса своим пирам, они в любом случае смогут общаться между собой.
А так эта проблема с роутингом решается бриджем на OpenWRT стороне, сбриджить вайргвардовский интерфейс в основной (можно прописать ebtables правила чтобы IPv4 не просачивался куда не надо), и настроить на сервере router advertisement в сторону вайргварда (radvd или dnsmasq в помощь).
romancelover
Посмотрите, как работает раздача мобильного интернета с IPv6, когда оператор выделяет только одну /64 подсеть. Раздающее устройство берёт себе один адрес из подсети на внешнем интерфейсе, и запускает radvd с этой /64 на внутреннем.
И не очень понятно, зачем на wireguard нужен slaac, когда можно адрес в конфиге статически прописывать из подсети меньшего размера. Хотя он может понадобиться тогда, если вы хотите раздать по Wi-Fi с роутера IPv6, полученный по VPN с вашего VPS (андроид телефоны по wifi иначе получать IPv6 не могут). Тогда придётся выделять из /56 уже две подсети: одну на линк сервер-роутер, другую для раздачи, и прописать маршруты.
Интересно, с одной /64 будет работать в таком случае? наверное, да, если на линке сервер-роутер прописать статически маленькую подсеть вроде /126. Это не рекомендуется, но сойдёт, если хостер жадный на адреса или неохота переплачивать за отдельную подсеть. А вот если роутеров с раздачей за сервером будет несколько, одна /64 уже никак не подойдёт.
Sunvas Автор
Задача стояла в том, чтобы через wireguard раздать целиком /64 подсеть, а не 1 статический адрес.
romancelover
Но механизм SLAAC реально не используется при этом, вы в конфиге адрес из /64 указываете. Заголовок статьи неправильный.
Попробуйте сделать, как я написал, подключить по VPN к серверу роутер и с него уже раздать IPv6 по Wi-Fi. Вот там уже понадобится SLAAC.
Sunvas Автор
Исключительно конфиге клиента, заметим, а не сервера. На сервере только префикс указывается.
В данном случае под SLAAC понимается, что клиент получает от сервера только префикс, а далее сам выбирает себе interface id и, таким образом, создаёт себе IPv6 адрес. Тут нет Router Advertisement, поскольку его заменяет сам WireGuard предоставляя префикс и его длину клиенту.
На OpenWRT это возможно и это работает.
romancelover
«В данном случае под SLAAC понимается, что клиент получает от сервера только префикс, а далее сам выбирает себе interface id и, таким образом, создаёт себе IPv6 адрес. „
Он не сам выбирает, а берёт из конфига (a:b:c:d в примере), а это уже не то.