Доброе время суток!
В этой статье хочу рассказать как я реализовал (еще один) скрипт на Bash для соединения двух компьютеров, находящимися за NAT, с использованием технологии UDP hole punching на примере ОС Ubuntu/Debian.
Организация соединения состоит из нескольких шагов:
Я долго думал и еще думаю, что можно использовать для обмена данными между узлами, самое простое и быстрое для меня на данный момент, это работа через Яндекс.диск.
Для определения внешнего IP-адреса и UDP-порта используется stun-client командой:
Установка командой:
Для организации туннеля используются штатные средства ОС из пакета iproute2. Существует множество туннелей которые можно поднять штатными средствами (L2TPv3, GRE и т.п.), но я выбрал IPIP потому что он создаёт минимальную дополнительную нагрузку на систему. Я пробовал L2TPv3 поверх UDP и разочаровался, скорость упала в 10 раз, но это могут быть различные ограничения связанные с провайдерами или еще чего-то. Так как IPIP-туннель работает на уровне IP, то используется FOU-туннель для того, что бы работать на уровне UDP-портов. Для организации IPIP-туннеля нужно:
— загрузить модуль FOU:
— слушать локальный порт:
— создать туннель:
— поднять интерфейс туннеля:
— назначить внутренний локальный и внутренний удаленный IP-адрес тунеля:
Удалить туннель:
Мониторинг состояния туннеля происходит с помощью периодического пинга внутреннего IP-адреса туннеля удаленного узла, командой:
Периодический пинг нужен в первую очередь для поддержания канала, иначе при простое туннеля на роутерах могут очиститься таблицы NAT и тогда соединение разорвется.
Если пинг пропадает, то IPIP-туннель удаляется и ждет готовности от удаленного узла.
Собственно сам скрипт:
Переменные username, password и folder должны быть одинаковые на обоих сторонах, а вот intip — разные, например: 10.0.0.1 и 10.0.0.2. Время на узлах должно быть синхронизированно. Запускать скрипт можно так:
Обращаю внимание на то, что IPIP-теннель небезопасен с точки зрения того, что трафик не шифруется, но это легко решается при помощи IPsec по этой статье, она мне показалось простой и понятной.
Использую данный скрипт для подключения к рабочему ПК уже несколько недель и проблем не замечал. Удобно в плане того, что настроил и забыл.
Возможно у Вас будут замечания и предложения, буду рад выслушать.
Спасибо за внимание!
В этой статье хочу рассказать как я реализовал (еще один) скрипт на Bash для соединения двух компьютеров, находящимися за NAT, с использованием технологии UDP hole punching на примере ОС Ubuntu/Debian.
Организация соединения состоит из нескольких шагов:
- Запуск узла и ожидание готовности удаленного узла;
- Определение внешнего IP-адреса и UDP-порта;
- Передача внешнего IP-адреса и UDP-порта удаленному узлу;
- Получение внешнего IP-адреса и UDP-порта от удаленного узла;
- Организация IPIP-туннеля;
- Мониторинг соединения;
- При обрыве соединения удалять IPIP туннель.
Я долго думал и еще думаю, что можно использовать для обмена данными между узлами, самое простое и быстрое для меня на данный момент, это работа через Яндекс.диск.
- Во первых, это простота в использовании — нужно 3 действия: создать, прочитать, удалить. С помощью curl это:
Создать:
curl -s -X MKCOL --user "$usename:$password" https://webdav.yandex.ru/$folder
Прочитать:
curl -s --user "$usename:$password" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/$folder
Удалить:
curl -s -X DELETE --user "$usename:$password" https://webdav.yandex.ru/$folder
- Во вторых, это простота в установке:
apt install curl
Для определения внешнего IP-адреса и UDP-порта используется stun-client командой:
stun stun.sipnet.ru -v -p $1 2>&1 | grep "MappedAddress"
Установка командой:
apt install stun-client
Для организации туннеля используются штатные средства ОС из пакета iproute2. Существует множество туннелей которые можно поднять штатными средствами (L2TPv3, GRE и т.п.), но я выбрал IPIP потому что он создаёт минимальную дополнительную нагрузку на систему. Я пробовал L2TPv3 поверх UDP и разочаровался, скорость упала в 10 раз, но это могут быть различные ограничения связанные с провайдерами или еще чего-то. Так как IPIP-туннель работает на уровне IP, то используется FOU-туннель для того, что бы работать на уровне UDP-портов. Для организации IPIP-туннеля нужно:
— загрузить модуль FOU:
modprobe fou
— слушать локальный порт:
ip fou add port $localport ipproto 4
— создать туннель:
ip link add name fou$name type ipip remote $remoteip local $localip encap fou encap-sport $localport encap-dport $remoteport
— поднять интерфейс туннеля:
ip link set up dev fou$name
— назначить внутренний локальный и внутренний удаленный IP-адрес тунеля:
ip addr add $intIP peer $peerip dev fou$name
Удалить туннель:
ip link del dev fou$name
ip fou del port $localport
Мониторинг состояния туннеля происходит с помощью периодического пинга внутреннего IP-адреса туннеля удаленного узла, командой:
ping -c 1 $peerip -s 0
Периодический пинг нужен в первую очередь для поддержания канала, иначе при простое туннеля на роутерах могут очиститься таблицы NAT и тогда соединение разорвется.
Если пинг пропадает, то IPIP-туннель удаляется и ждет готовности от удаленного узла.
Собственно сам скрипт:
#!/bin/bash
username="username@yandex.ru"
password="password"
folder="vpnid"
intip="10.0.0.1"
localport=`shuf -i 10000-65000 -n 1`
cid=`shuf -i 10000-99999 -n 1`
tid=`shuf -i 10-99 -n 1`
function yaread {
curl -s --user "$1:$2" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/$3 | sed 's/></>\n</g' | grep "displayname" | sed 's/<d:displayname>//g' | sed 's/<\/d:displayname>//g' | grep -v $3 | grep -v $4 | sort -r
}
function yacreate {
curl -s -X MKCOL --user "$1:$2" https://webdav.yandex.ru/$3
}
function yadelete {
curl -s -X DELETE --user "$1:$2" https://webdav.yandex.ru/$3
}
function myipport {
stun stun.sipnet.ru -v -p $1 2>&1 | grep "MappedAddress" | sort | uniq | awk '{print $3}' | head -n1
}
function tunnel-up {
modprobe fou
ip fou add port $4 ipproto 4
ip link add name fou$7 type ipip remote $1 local $3 encap fou encap-sport $4 encap-dport $2
ip link set up dev fou$7
ip addr add $6 peer $5 dev fou$7
}
function tunnel-check {
sleep 10
pings=0
until [[ $pings == 4 ]]; do
if ping -c 1 $1 -s 0 &>/dev/null;
then echo -n .; n=0
else echo -n !; ((pings++))
fi
sleep 15
done
}
function tunnel-down {
ip link del dev fou$1
ip fou del port $2
}
trap 'echo -e "\nDisconnecting..." && yadelete $username $password $folder; tunnel-down $tunnelid $localport; echo "IPIP tunnel disconnected!"; exit 1' 1 2 3 8 9 14 15
until [[ -n $end ]]; do
yacreate $username $password $folder
until [[ -n $ip ]]; do
mydate=`date +%s`
timeout="60"
list=`yaread $username $password $folder $cid | head -n1`
yacreate $username $password $folder/$mydate:$cid
for l in $list; do
if [ `echo $l | sed 's/:/ /g' | awk {'print $1'}` -ge $(($mydate-65)) ]; then
#echo $list
myipport=`myipport $localport`
yacreate $username $password $folder/$mydate:$cid:$myipport:$intip:$tid
timeout=$(( $timeout + `echo $l | sed 's/:/ /g' | awk {'print $1'}` - $mydate + 3 ))
ip=`echo $l | sed 's/:/ /g' | awk '{print $3}'`
port=`echo $l | sed 's/:/ /g' | awk '{print $4}'`
peerip=`echo $l | sed 's/:/ /g' | awk '{print $5}'`
peerid=`echo $l | sed 's/:/ /g' | awk '{print $6}'`
if [[ -n $peerid ]]; then tunnelid=$(($peerid*$tid)); fi
fi
done
if ( [[ -z "$ip" ]] && [ "$timeout" -gt 0 ] ) ; then
echo -n "!"
sleep $timeout
fi
done
localip=`ip route get $ip | head -n1 | sed 's|.*src ||' | cut -d' ' -f1`
tunnel-up $ip $port $localip $localport $peerip $intip $tunnelid
tunnel-check $peerip
tunnel-down $tunnelid $localport
yadelete $username $password $folder
unset ip port myipport
done
exit 0
Переменные username, password и folder должны быть одинаковые на обоих сторонах, а вот intip — разные, например: 10.0.0.1 и 10.0.0.2. Время на узлах должно быть синхронизированно. Запускать скрипт можно так:
nohup script.sh &
Обращаю внимание на то, что IPIP-теннель небезопасен с точки зрения того, что трафик не шифруется, но это легко решается при помощи IPsec по этой статье, она мне показалось простой и понятной.
Использую данный скрипт для подключения к рабочему ПК уже несколько недель и проблем не замечал. Удобно в плане того, что настроил и забыл.
Возможно у Вас будут замечания и предложения, буду рад выслушать.
Спасибо за внимание!
anonymous
Велосипеды это хорошо и весело, но на самом деле для связи компьютеров за NAT что бы не быть привязанным к единой точки отказа в виде VPN-сервера прекрасно подходят оверлейные сети типа yggdrasil или ZeroTier.
Использую ZeroTier для связи всех моих серверов, серверов клиентов и моих машин, давно забыл о том что какие-то контейнеры за натом или что мои домашние машины за двойным натом(провайдерский, а потом мой). Поднял свой ZeroTier network controller и морду для руления, при появлении новых машин ставлю клиент, добавляю в контроллере и прописываю в свой ДНС, на машинах для резолвинга dnsmasq который знает, что про мою собственную зону
.z.t
запросы надо отправлять вон туда, то есть на один из моих серверов, где собственно ДНС за нее отвечающий.Все прозрачно и удобно, совершенно не думая о натах хожу куда мне нужно, хоть на свои домашние машины, хоть к отцу на ноутбук(если он на что-то жалуется), хоть на сервера или контейнеры. Раньше использовал PeerVPN, он прекрасно умеет во множественные initpeers и проброску трафика через машины, но он заброшен и не работает с OpenSSL 1.1.x, а у меня пожалуй не хватит знаний криптографии пытаться что-то с ним сделать, потому и нашел замену в виде двух указанных выше оверлейных сетей. От yggdrasil отказался потому что у него порой случались приступы безудержного жора памяти, что мне не нравилось, а ZeroTier всем устраивает и все прекрасно работает с ним.
mltk
"что бы не быть привязанным к единой точки отказа в виде VPN-сервера"
А к единой точке отказа в виде вашего собственного ZeroTier Network Controller вы не привязаны?
anonymous
Нет, не привязан, я пробовал гасить свой контроллер, связанность не теряется
Winseven Автор
А если в момент пока погашен контроллер произойдет временный обрыв связи или перезагрузка узла, связанность восстановится?
mltk
И второй вопрос по ZeroTier. Я его пробовал с год назад для связи квартиры (проводной Билайн с L2TP подключением, без внешнего IP. NAT) и дачи (мобильный Билайн в модеме+роутер, без внешнего IP. NAT). Network Controller свой не поднимал — использовал my.zerotier.com
Сеть поднялась и работала. Но раз или два в минуту связность полностью пропадала секунд на пять :) И это при постоянно запущенном пинге между нодами.
anonymous
Ничего такого не наблюдаю, на большинстве точек запущенны заббикс-агенты и мониторинг в том числе наличия пингов до хостов, если бы такое было, то у меня бы мониторинг постоянно орал, а он это делает мне в телеграм, я бы от ора не мог есть и спать
kmeaw
В случае ZeroTier приходится отдавать часть control plane своей сети третьей стороне, что не очень здорово. В решении автора такой проблемы нет (особенно если поменять Я.Диск на DHT, а stun-серверов добавить с десяток). И, кажется, в yggdrasil тоже — там узлы используют криптографию для того, чтобы назначить себе адрес, а для изменения маршрутизации нужно уметь подписывать сообщения своим секретным ключом.
anonymous
Никакой третьей стороны, так как контроль над твоей сетью находится на твоем контролере.
anonymous
Приветствую. Спасибо за интересную наводку. Софтина интереснее описанного в статье процесса, по крайне мере для меня, не в обиду автору. Сам последнее время использую разные вариации ikev2 и softether. На личние веб уравнения у ZeroTier усиливает интерес.
anonymous
Я сам здесь же на Хабре в блоге какого-то из VPS-хостеров вроде и набрел на ZeroTier в свое время, так что это и не мне спасибо, а всему Хабру скорее.