Заметка о том как создать mesh-сеть Yggdrasil over TLS и убрать публичный пир за Nginx. Кратко, что такое Yggdrasil? Это протокол для создания зашифрованной overlay IPv6 mesh сети поверх локальных и публичных сетей. Нам не надо знать как маршрутизируется трафик, через что подключены узлы сети (wi-fi, ethernet, bluetooth), достаточно чтобы пиры как-то могли подключиться друг к другу.
Зачем использовать TLS и 443 порт? Это будет примитивная маскировка под обычный TLS трафик с указанием в SNI-заголовке левого домена. Этого должно хватить для обхода простых блокировок.
В моём случае я не хочу, чтобы пиры были участниками глобальной Yggdrasil-сети, а образовали мою закрытую mesh-сеть. Один узел будет доступен через интернет и иметь белый IP, остальные будут подключаться как придётся. В идеале, если внешних пиров несколько, их можно выключать и перезагружать не нарушая доступность/связности mesh-сети.
Если хотите стать участником общей сети yggdrasil, со своими сервисами и сайтами (такой интернет в интернете) - укажите хотя бы один публичный пир в настройках ваших клиентов.
Установка
Сначала установим необходимое ПО, для Ubuntu:
sudo apt install yggdrasil
sudo systemctl enable yggdrasil.service
для Arch:
sudo pacman -S yggdrasil
sudo systemctl enable yggdrasil.service
Настройка внешнего пира
Создаем конфиг:
yggdrasil -genconf | sudo tee /etc/yggdrasil/yggdrasil.conf
Меняем Listen в /etc/yggdrasil/yggdrasil.conf на что-то подобное (порт любой):
Listen: ["tls://127.0.0.1:19657"]
Запускаем сервис:
sudo systemctl start yggdrasil.service
Настройка Nginx
На моем сервере помимо yggdrasil работает vless и несколько сайтов, поэтому будем использовать nginx модуль stream и SNI для маршрутизации трафика между сервисами.
В /etc/nginx/nginx.conf добавляем
stream {
include /etc/nginx/stream-enabled/*;
}
В /etc/nginx/stream-enabled/proxy
map $ssl_preread_server_name $sni_name {
hostnames;
www.twitch.tv yggdrasil;
# my local services
example.ru www;
*.example.ru www;
# xray
default xray;
}
upstream www {
server 127.0.0.1:7443;
}
upstream xray {
server 127.0.0.1:8443;
}
upstream yggdrasil {
server 127.0.0.1:19657;
}
server {
listen 443;
proxy_pass $sni_name;
ssl_preread on;
}
Порт 19657 в примере можно заменить на любой другой, но он должен совпадать в Nginx и конфиге Yggdrasil.
Домен www.twitch.tv тоже любой, далее будет использоваться другими пирами в SNI-заголовках для подключению к этому пиру.
Остальные пиры
Также устанавливаем Yggdrasil и создаем конфиг:
sudo apt install yggdrasil
sudo systemctl enable yggdrasil.service
yggdrasil -genconf | sudo tee /etc/yggdrasil/yggdrasil.conf
В конфиг добавляем внешние пиры, у меня он один, у вас может быть сколько угодно:
Peers: ["tls://123.4.5.6:443?sni=www.twitch.tv"]
Указываем IP по которому доступен пир. Значение sni должно совпадать с доменом, указанным в map-блоке Nginx на внешнем сервере.
Запускаем сервис:
sudo systemctl start yggdrasil.service
Безопасность
Чтобы у нас была своя огороженная mesh-сеть и другие узлы не могли подключиться к ней, необходимо ограничить какие публичные ключи принимают пиры.
Чтобы получить публичный ключ выполняем на каждом узле:
sudo yggdrasilctl getSelf
Build name: yggdrasil
Build version: 0.5.12
IPv6 address: 202:xxxx:yyyy:zzz:becc:f050:3ac4:c32
IPv6 subnet: 302:xxxx:yyyy:zzz::/64
Routing table size: 2
Public key: 2a28e5ad7e304asdasdsdf74rguydrfga8adc97c8ffc9ed8e0ffsdfsdf8934rfsduyjh
Берем Public key's и добавляем к конфиг каждого пира в массив AllowedPublicKeys, например:
AllowedPublicKeys: [ "key1", "key2", "key3" ]
соответственно подключиться к пиру смогут только пиры с указанными публичными ключами и никто другой.
После настройки и запуска, проверяем работу:
$ sudo yggdrasilctl getPeers
URI State Dir IP Address Uptime RTT RX TX Down Up Pr
Cost Last Error
tls://123.4.5.6:443 Up Out 202:xxxx:yyyy:zzz:becc:f050:3ac4:2370 15m28s 43.27ms 351.6KB 326.9KB - - 0
83 -
Статус пиров должен быть Up.
И пингуем узлы сети по IPv6 адресам:
$ ping 202:xxxx:yyyy:zzz:becc:f050:3ac4:2370
PING 202:xxxx:yyyy:zzz:becc:f050:3ac4:2370 (202:xxxx:yyyy:zzz:becc:f050:3ac4:2370) 56 data bytes
64 bytes from 202:xxxx:yyyy:zzz:becc:f050:3ac4:2370: icmp_seq=1 ttl=64 time=89.5 ms
64 bytes from 202:xxxx:yyyy:zzz:becc:f050:3ac4:2370: icmp_seq=2 ttl=64 time=69.8 ms
64 bytes from 202:xxxx:yyyy:zzz:becc:f050:3ac4:2370: icmp_seq=3 ttl=64 time=103 ms
64 bytes from 202:xxxx:yyyy:zzz:becc:f050:3ac4:2370: icmp_seq=4 ttl=64 time=79.7 ms
^C
--- 202:xxxx:yyyy:zzz:becc:f050:3ac4:2370 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3001ms
rtt min/avg/max/mdev = 69.802/85.529/103.108/12.306 ms
В итоге у вас должна получиться overlay сеть, где все пиры имеют IPv6 адреса и видят друг друга, где бы физически они не находились. Теперь, находясь с ноутбуком где-нибудь в кафе с публичным wi-fi, вы сможете прозрачно иметь доступ к любому узлу вашей mesh-сети как если бы находились с ними в одной локалке.
