Продолжение статьи о том, как мне удалось организовать прямой VPN-туннель между двумя компьютерами находящимися за NAT'ами провайдеров. В прошлой статье описывался процесс организации соединения с помощью третьей стороны — посредника (арендованный VPS выполняющий роль, что-то типа STUN-сервера и передатчика данных узлов для соединения). В этой статье я расскажу как обошелся без VPS, но посредники остались и ими были STUN-сервер и Яндекс.Диск…

Введение


Прочитав комментарии прошлого поста я понял, что главным недостатком реализации было использование посредника — третьей стороны (VPS) которая указывала текущие параметры узла, куда и как подключаться. Учитывая рекомендации использовать настоящий STUN (коих очень много) для определения текущих параметров подключения. Первым делом я решил посмотреть при помощи TCPDump содержимое пакетов при работе STUN-сервера с клиентами и получил совершенно нечитабельное содержимое. Погуглив протокол наткнулся на статью с описанием протокола. Я понял что самостоятельно реализовать запрос к STUN-серверу я не могу и убрал задумку в «далекий ящик».

Теория


Недавно мне пришлось устанавливать STUN-сервер на Debian из пакета
# apt install stun-server
и в зависимостях я увидел пакет stun-client, но как-то не придал этому значения. Но позже я вспомнил про пакет stun-client и решил разобраться как он работает, погуглив и пояндексив я получил:

# apt install stun-client
# stun stun.ekiga.net -p 21234 -v

В ответ я получил:

STUN client version 0.97
Opened port 21234 with fd 3
Opened port 21235 with fd 4
Encoding stun message:
Encoding ChangeRequest: 0

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 4

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 2

About to send msg of len 28 to 216.93.246.18:3478
Received stun message: 92 bytes
MappedAddress = <Мой IP>:2885
SourceAddress = 216.93.246.18:3478
ChangedAddress = 216.93.246.17:3479
Unknown attribute: 32800
ServerName = Vovida.org 0.98-CPC
Received message of type 257 id=1
Encoding stun message:
Encoding ChangeRequest: 0

About to send msg of len 28 to 216.93.246.17:3478
Encoding stun message:
Encoding ChangeRequest: 4

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 2

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 0

About to send msg of len 28 to <Мой IP>:2885
Received stun message: 28 bytes
ChangeRequest = 0
Received message of type 1 id=11
Encoding stun message:
Encoding ChangeRequest: 0

About to send msg of len 28 to 216.93.246.17:3478
Encoding stun message:
Encoding ChangeRequest: 4

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 2

About to send msg of len 28 to 216.93.246.18:3478
Received stun message: 92 bytes
MappedAddress = <Мой IP>:2885
SourceAddress = 216.93.246.17:3479
ChangedAddress = 216.93.246.18:3478
Unknown attribute: 32800
ServerName = Vovida.org 0.98-CPC
Received message of type 257 id=10
Encoding stun message:
Encoding ChangeRequest: 4

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 2

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 4

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 2

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 4

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 2

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 4

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 2

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 4

About to send msg of len 28 to 216.93.246.18:3478
Encoding stun message:
Encoding ChangeRequest: 2

About to send msg of len 28 to 216.93.246.18:3478
test I = 1
test II = 0
test III = 0
test I(2) = 1
is nat = 1
mapped IP same = 1
hairpin = 1
preserver port = 0
Primary: Independent Mapping, Port Dependent Filter, random port, will hairpin
Return value is 0x000006

Строка со значением
MappedAddress = <Мой IP>:2885

как раз то, что надо! Она отображала текущее состояние для соединения на локальном UDP порту 21234. Но это всего лишь пол дела, встал вопрос как передать эти данные удаленному узлу и организовать VPN-соединение. Использование почтового протокола, а может Telegram?! Вариантов много и решил использовать Яндекс.диск, так как попадалась мне статья о работе Curl через WebDav с Яндекс.диском. Подумав над реализацией я пришел к такой схеме:

  1. Сигнализировать о готовности узлов к установке соединения наличием определенного файла с временной меткой на Яндекс.диске;
  2. Если узлы готовы, то получать текущие параметры от STUN-сервера;
  3. Выгружать текущие параметры на Яндекс.диск;
  4. Проверять наличие и считывать параметры удаленного узла из файла на Яндекс.диске;
  5. Установка соединения с удаленным узлом с помощью OpenVPN.

Практика


Немного подумав, с учетом опыта прошлой статьи, написал скрипт на скорую руку.
Собственно сам скрипт:
Первоначальный вариант
# cat vpn8.sh

#!/bin/bash
######################## Задаем цветной текст ###
WARN='\033[37;1;41m'				#
END='\033[0m'					#
RED='\033[0;31m'         #  ${RED}		#
GREEN='\033[0;32m'      #  ${GREEN}		#
#################################################
####################### Проверяем наличие необходымих приложений #########################################################
al="echo readlink dirname grep awk md5sum shuf nc curl sleep openvpn cat stun"
ch=0
for i in $al; do which $i > /dev/null || echo -e "${WARN}Для работы необходим $i ${END}"; which $i > /dev/null || ch=1; done
if (( $ch > 0 )); then echo -e "${WARN}Ой, отсутствуют необходимые для корректной работы приложения${END}"; exit; fi
#######################################################################################################################

if [[ $1 == '' ]]; then echo -e "${WARN}Введите идентификатор соединения (любое уникальное слово, должно быть одинаковое с двух сторон!) ${END} \t
${GREEN}Для запуска в автоматическом режиме при включении компьютера можно прописать в /etc/rc.local строку nohup /<путь к файлу>/vpn8.sh  > /var/log/vpn8.log 2>/dev/hull & ${END}"; exit; fi
ABSOLUTE_FILENAME=`readlink -f "$0"`                                                    # полный путь до скрипта
DIR=`dirname "$ABSOLUTE_FILENAME"`                                                      # каталог в котором лежит скрипт
############################### Проверка наличия секретного ключа ##################################
key="$DIR/secret.key"
if [ ! -f "$key" ]; then
				echo -e "${WARN}Секретный ключ VPN-соединения не найден, для генерации ключа выполните: openvpn --genkey --secret secret.key Внимание: ключ используется для авторизации и должен быть одинаковым с двух сторон!!!${END}
 # ls -l secret.key
 -rw------- 1 root root 637 ноя 27 11:12 secret.key
 # chmod 600 secret.key";
				exit;
				fi
########################################################################################################################

ABSOLUTE_FILENAME=`readlink -f "$0"`                                                    # полный путь до скрипта
DIR=`dirname "$ABSOLUTE_FILENAME"`                                                      # каталог в котором лежит скрипт
name=$(uname -n | md5sum | awk '{print $1}')
vpn=$(echo $1 | md5sum | awk '{print $1}')
stun="stun.ekiga.net" 	# STUN сервер
username="Yandex"	# Логин от Яндекс.диска	
password="Password"	# Пароль от Яндекс.диска
localport=`shuf -i 20000-65000 -n 1`	# генерация локального порта

echo "$(date) Создаю папку на Яндекс.диске"
curl -X MKCOL --user "${username}:${password}" https://webdav.yandex.ru/vpn-$vpn
echo "$(date) Очищаю папку от всякого мусора"
for i in `curl --silent --user "$username:$password" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/vpn-$vpn/ | sed 's/></\n/g' | grep "d:displayname" | sed 's/d:displayname//g' | sed 's/>//g' | sed 's/<//' | sed 's/\///g' | grep -v $(date +%Y-%m-%d-%H-%M)`; do
	echo "$(date) Delete: $i"
	curl -X DELETE --user "${username}:${password}" https://webdav.yandex.ru/vpn-$vpn/$i
	done

until [ $c ];do

	until [[ $b ]]; do
		echo "$(date) Проверяю папку"
		date=`date +%Y-%m-%d-%H-%M`
		mydata=`curl --silent --user "${username}:${password}" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/vpn-$vpn/ | sed 's/></>\n</g' | grep $name | grep $date | grep "d:displayname"`
		if [[ -z $mydata ]]; 	then
						echo "$(date) Файл готовности создан"
					        echo "$date" > "/tmp/$date-$name-ready.txt"
					        curl -T "/tmp/$date-$name-ready.txt" --user "$username:$password" https://webdav.yandex.ru/vpn-$vpn/$date-$name-ready.txt
					else
						echo "$(date) Файл готовности уже существует - $date"
					fi
		remote=`curl --silent --user "${username}:${password}" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/vpn-$vpn/ | sed 's/></>\n</g' | grep -v $name | grep $date | grep "d:displayname"`
		if [[ -z $remote ]];	then
						echo -e "$(date) ${RED} Удаленный узел не готов ${END}"
						echo "$(date) Жду"
						sleep 20
					else
						echo -e "$(date) ${GREEN} Удаленный узел готов ${END}"
						b=1
						a=''
					fi
	done

	until [ $a ]; do
		echo "$(date) Подключение и получение данных от STUN сервера: $stun"
                mydata=`stun $stun -p $localport -v 2>&1 | grep MappedAddress | sort | uniq`
                echo -e "$(date) ${GREEN}Мои данные соединения: $mydata${END}"
                echo "$mydata" > "$DIR/mydata"
                echo "$(date) Загрузка данных на Яндекс.диск"
                curl -T "$DIR/mydata" --user "$username:$password" https://webdav.yandex.ru/vpn-$vpn/$name.txt
		echo "$(date) Получение файла данных удаленного узла"
		filename=$(curl --silent --user "${username}:${password}" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/vpn-$vpn/ | sed 's/></\n/g' | grep "d:displayname>" | grep "txt" | grep -v "$name" | grep -v "ready" | sed 's|.*d:displayname>||' | sed 's/</ /g' | awk '{print $1}')
		echo "$(date) Чтение файла данных удаленного узла: $filename"
		address=$(curl --silent --user "$username:$password" https://webdav.yandex.ru/vpn-$vpn/$filename | sort | uniq | head -n1 | sed 's/:/ /g')
		echo "$(date) Определение IP-адреса и порта"
		ip=$(echo "$address" | awk '{print $3}')
		port=$(echo "$address" | awk '{print $4}')
		if [[ -n "$ip" && -n "$port" ]]; then
			echo -e "$(date) ${GREEN} Соединение $ip $port ${END}"
       		 	openvpn --remote $ip --rport $port --lport $localport 	       	 	    --proto udp --dev tap --float --auth-nocache --verb 3 --mute 20 	       	 	    --ifconfig 10.45.54.2 255.255.255.252 	       		    --secret "$DIR/secret.key" 	       		    --auth SHA256 --cipher AES-256-CBC 	        	    --ncp-disable --ping 10  --ping-exit 30 	        	    --comp-lzo yes
			echo -e "$(date) ${WARN} Соединение разорвано${END}"
			a=1
			b=''
			else
			a=1
			b=''
			fi
	done
done

Для работы скрипта нужно:
  1. Скопировать в буфер и вставить в редактор, например:
    # nano vpn8.sh 
  2. указать логин и пароль от Яндекс.диска.
  3. в поле "--ifconfig 10.45.54.(1 или 2) 255.255.255.252" указать внутренний IP-адрес интерфейса
  4. cоздать secret.key командой:
    # openvpn --genkey --secret secret.key 
  5. сделать скрипт исполняемым:
    # chmod +x vpn8.sh
  6. запустить скрипт:
    # ./vpn8.sh nZbVGBuX5dtturD

    где nZbVGBuX5dtturD — ID-соединения сгенерированный тут

На удаленном узле произвести всё тоже самое за исключением генерации secret.key и ID-соединения, они должны быть идентичными.

Обновленный вариант (для корректной работы время должно быть сихронизировано):

cat vpn10.sh

#!/bin/bash
stuns="stun.sipnet.ru stun.ekiga.net"   		# Список STUN серверов через пробел
username=" Login "		# Логин от Яндекс.диска
password=" Password "   	# Пароль от Яндекс.диска
intip="10.23.22.1"		# IP-адрес внутреннего интерфейса

WARN='\033[37;1;41m'
END='\033[0m'
RED='\033[0;31m'
GREEN='\033[0;32m'
al="ip echo readlink dirname grep awk md5sum openssl sha256sum shuf curl sleep openvpn cat stun"
ch=0
for i in $al; do which $i > /dev/null || echo -e "${WARN}Для работы необходим $i ${END}"; which $i > /dev/null || ch=1; done
if (( $ch > 0 )); then echo -e "${WARN}Ой, отсутствуют необходимые для корректной работы приложения${END}"; exit; fi
if [[ $1 == '' ]];
	then
		echo -e "${WARN}Введите идентификатор соединения (любое уникальное слово, должно быть одинаковое с двух сторон!) ${END} \t
		${GREEN}Для запуска в автоматическом режиме при включении компьютера можно прописать в /etc/rc.local строку nohup /<путь к файлу>/vpn10.sh  > /var/log/vpn10.log 2>/dev/hull & ${END}"
		exit
fi
ABSOLUTE_FILENAME=`readlink -f "$0"`                                                    # полный путь до скрипта
DIR=`dirname "$ABSOLUTE_FILENAME"`                                                      # каталог в котором лежит скрипт
key="$DIR/secret.key"
until [[ -n "$iftosrv" ]]
	do
		echo "$(date) Определяю сетевой интерфейс"; iftosrv=`ip route get 8.8.8.8 | head -n 1 | sed 's|.*dev ||' | awk '{print $1}'`
		sleep 5
done
timedatectl
name=$(uname -n | md5sum | awk '{print $1}')
vpn=$(echo $1 | md5sum | awk '{print $1}')
echo "$(date) Создаю папку на Яндекс.диске"
curl -X MKCOL --user "${username}:${password}" https://webdav.yandex.ru/vpn-$vpn
echo "$(date) ID на диске: $vpn"
until [ $c ];do
	echo "$(date) Очищаю папку от всякого мусора"
	for i in `curl --silent --user "$username:$password" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/vpn-$vpn/ | sed 's/></\n/g' | grep "d:displayname" | sed 's/d:displayname//g' | sed 's/>//g' | sed 's/<//' | sed 's/\///g' | grep -v $(date +%Y-%m-%d-%H-%M)`
               do
               echo -e "$(date)${RED} Удаляю старый файл: $i${END}"
               curl -X DELETE --user "${username}:${password}" https://webdav.yandex.ru/vpn-$vpn/$i
               done
	echo "$(date) ID на диске: $vpn"
        openvpn --genkey --secret "$key"
        passwd=`echo "$vpn-tt" | sha256sum | awk '{print $1}'`
        openssl AES-256-CBC -e -in "$key" -out "$DIR/file.enc" -k "$passwd" -base64
        curl -T "$DIR/file.enc" --user "$username:$password" https://webdav.yandex.ru/vpn-$vpn/key.enc
        rm "$DIR"/file.enc
	echo -e "$(date) ${GREEN}Фаза 1 - Получение готовности удаленного узла${END}"
	go=3
	localport=`shuf -i 20000-65000 -n 1`    # генерация локального порта
	start=''
	remote=''
	timeout1=''
	nextcheck=''
	timestart=''
	until [[ $b ]]
		do
		echo "$(date) Проверяю папку"
		date=`date +%s`
		timeout1=60
		echo "$(date) Создание файла готовности $date"
		echo "$date" > "/tmp/ready-$date-$name.txt"
		curl -T "/tmp/ready-$date-$name.txt" --user "$username:$password" https://webdav.yandex.ru/vpn-$vpn/ready-$name.txt
		readyfile=`curl --silent --user "${username}:${password}" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/vpn-$vpn/ | sed 's/></>\n</g' | grep -v $name | grep "ready" | grep "d:displayname" | sed 's/<d:displayname>//g' | sed 's/<\/d:displayname>//g'`
		if [[ -z $readyfile ]]
			then
				echo -e "$(date) ${RED} Удаленный узел не готов ${END}"
				echo "$(date) Жду 60 секунд"
				sleep $timeout1
			else
				remote=$(curl --silent --user "$username:$password" https://webdav.yandex.ru/vpn-$vpn/$readyfile)
				echo -e "$(date) ${GREEN} Удаленный узел готов ${END}"
				start=`curl --silent --user "${username}:${password}" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/vpn-$vpn/ | sed 's/></>\n</g' | grep "start" | grep "d:displayname" | sed 's/-/ /g' | awk '{print $2}'`
				if [[ -z $start ]]
					then
                                                let nextcheck=$timeout1-$date+$remote
						let timestart=$date+$timeout1-$nextcheck
						go=$nextcheck
						echo "$timestart" > "/tmp/start-$date-$name.txt"
						curl -T "/tmp/start-$date-$name.txt" --user "$username:$password" https://webdav.yandex.ru/vpn-$vpn/start-$date-$name.txt
					else
						echo "$(date) жду $go секунд"
						sleep $go
						b=1
						a=''

					fi

			fi
	done
echo -e "$(date) ${GREEN}Фаза 2 - Обмен данными и установка соединения${END}"
mydata=''
filename=''
address=''
myip=''
ip=''
port=''
ex=0
	until [ $a ]; do
                until [[ -n "$mydata" ]]; do
				k=`echo "$stuns" | wc -w`
				x=1
				z=`shuf -i 1-$k -n 1`
				for st in $stuns; do
					if [[ $x == $z ]]; then
						stun=$st;
					fi;
					(( x++ ));
				done
                                echo "$(date) Подключение и получение данных от STUN сервера: $stun"
				sleep 5 && for pid in $(ps xa | grep "stun "$stun" 1 -p "$localport" -v" | grep -v grep | awk '{print $1}'); do kill $pid; done &
                                mydata=`stun "$stun" 1 -p "$localport" -v 2>&1 | grep "MappedAddress" | sort | uniq`
                done
			echo -e "$(date) ${GREEN}Мои данные соединения: $mydata${END}"
               	 	echo "$(date) Загрузка данных на Яндекс.диск"
			echo "$mydata" > "$DIR/mydata"
			echo "IntIP $intip" >> "$DIR/mydata"
			curl -T "$DIR/mydata" --user "$username:$password" https://webdav.yandex.ru/vpn-$vpn/$name-ipport.txt
			rm "$DIR/mydata"
			sleep 5
			echo "$(date) Получение файла данных удаленного узла"
			filename=$(curl --silent --user "${username}:${password}" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/vpn-$vpn/ | sed 's/></\n/g' | grep "d:displayname>" | grep "ipport" | grep -v "$name" |  sed 's|.*d:displayname>||' | sed 's/</ /g' | awk '{print $1}')
			if [[ -n "$filename" ]]
				then
					echo "$(date) Чтение файла данных удаленного узла: $filename"
					address=$(curl --silent --user "$username:$password" https://webdav.yandex.ru/vpn-$vpn/$filename | grep "MappedAddress" | head -n1 | sed 's/:/ /g')
					intip2=$(curl --silent --user "$username:$password" https://webdav.yandex.ru/vpn-$vpn/$filename | grep "IntIP" | head -n1 | awk '{print $2}')
					echo "$(date) Определение IP-адреса и порта: $address $sesid2 $tunid2"
					ip=$(echo "$address" | awk '{print $3}')
					port=$(echo "$address" | awk '{print $4}')
					myip=`ip route get "$ip" | head -n 1 | sed 's|.*src ||' | awk '{print $1}'`
					if [[ -n "$ip" && -n "$port" && -n "$myip" && -n "$localport" ]];
						then
							echo -e "$(date) ${GREEN} Соединение $ip $port ${END}"
							echo -e  "`date` ${GREEN} $myip:$localport -> $ip:$port ${END}"
	                	        		curl --silent --user "$username:$password" https://webdav.yandex.ru/vpn-$vpn/key.enc > "$DIR/secret.enc"
	                        			openssl AES-256-CBC -d -in "$DIR/secret.enc" -out "$key" -k "$passwd" -base64
	                        			chmod 600 "$key"
	                        			rm "$DIR/secret.enc"
	                        			openvpn --remote $ip --rport $port --lport $localport 	                        			--proto udp --dev tun --float --auth-nocache --verb 3 --mute 20 	                        			--ifconfig "$intip" "$intip2" 	                        			--secret "$key" 	                        			--auth SHA256 --cipher AES-256-CBC 	                        			--ncp-disable --ping 10 --ping-exit 20 	                        			--comp-lzo yes
							a=1
							b=''
						else
							if (( $ex >= 5 ))
								then
									echo "$(date) Сброс"
									a=1
									b=''
							fi
							(( ex++ ))
					sleep 5
					fi
			fi
		done
done

Для работы скрипта нужно:
  1. Скопировать в буфер и вставить в редактор, например:
    # nano vpn10.sh 
  2. указать логин (2я строка) и пароль от Яндекс.диска (3я строка).
  3. указать внутренний IP-адрес туннеля (4я строка).
  4. сделать скрипт исполняемым:
    # chmod +x vpn10.sh
  5. запустить скрипт:
    # ./vpn10.sh nZbVGBuX5dtturD

    где nZbVGBuX5dtturD — ID-соединения сгенерированный тут

На удаленном узле произвести всё тоже самое, указать соответствующий внутренний IP-адрес туннеля и ID-соединения.

Для автозапуска скрипта при включении я использую команду «nohup /<путь до скрипта>/vpn10.sh nZbVGBuX5dtturD > /var/log/vpn10.log 2>/dev/null &» содержащиюся в файле /etc/rc.local

Заключение


Скрипт работает, проверен на Ubuntu 18.04 и Debian 9. В качестве передатчика можно использовать любой другой сервис, но для опыта я использовал Яндекс.диск.
В процессе экспериментов было обнаружено, что некоторые типы NAT провайдеров не позволяют организовать соединение. В основном у сотовых операторов, где заблокированы торренты.

Планирую доработать в плане:
  • Автоматической генерации secret.key каждый раз при старте, шифровании и копирования на Яндекс.диск для передачи на удаленный узел (Учтено в обновленном варианте)
  • Автоматического назначения IP-адресов интерфейсов
  • Шифрования данных перед выгрузкой на Яндекс.диск
  • Оптимизация кода

Да будет IPv6 в каждом доме!

Комментарии (23)


  1. Griboks
    19.12.2019 10:13
    +1

    Отличная работа. Наконец стали появляться адекватные статьи по работе через STUN. Осталось только написать третью часть, где вы организуете VPN без посредника и STUN сервера.


    1. Winseven Автор
      19.12.2019 10:29

      VPN без посредника и STUN сервера.
      это будет VPN over IPv6)


      1. Griboks
        19.12.2019 11:13

        1. Устанавливаем сервер.
        2. Сервер выполняет трассировку и получает белый ip/сеть за NAT. Если что, то существуют всемирные базы провайдеров.
        3. Сервер начинает udp-прослушку.
        4. Устанавливаем клиент.
        5. Вбиваем в него полученный ip/сеть.
        6. Запускается сканер портов (ищем udp-прослушку).Однако, сканирование достаточно выполнить один раз в большинстве случаев, если провайдер не использует балансировщик и прочие подобные вещи.
        7. Мы узнали ip: порт сервера. Теперь можно использовать обычный VPN.

        Естественно, этот способ имеет ещё меньшую область применения чем stun, который в свою очередь уступает ретрансляции. Однако, даже 1% от 10 000 000 устройств — неплохой результат.


        1. edo1h
          19.12.2019 14:39

          5. Вбиваем в него полученный ip/сеть.


          так весь смысл статьи в отказе от ручной работы


          1. Griboks
            19.12.2019 16:03

            Это как звонок по телефону. Вы один раз записали номер, а потом можете на него звонить.
            Тем более, вы забываете, что кто-то ещё должен установить openvpn + остальное ПО + сконфигурировать это всё. На фоне таких манипуляций вписывание 4 числе не кажется уж такой большой прибавкой к ручной работе.
            Кроме того, весь смысл в организации vpn без посредников. Если я захочу избавиться от ручной работы, я найму сисадмина или хотя бы куплю сервер.


            1. edo1h
              19.12.2019 18:59

              На фоне таких манипуляций вписывание 4 числе

              а кто сказал, что маппинг будет постоянным во времени? вспоминаем легендарное "ни единого разрыва" — рестарт pppoe-сессии почти наверняка даст смену внешнего IP и/или порта.


              Кроме того, весь смысл в организации vpn без посредников

              совсем без посредников, очевидно, не получится — нам нужны провайдеры последней мили, магистральные провайдеры, все эти BGP-сессии и т.п.


              Если я захочу избавиться от ручной работы, я найму сисадмина или хотя бы куплю сервер.

              вот тут альтернатива и предлагается — использовать уже имеющуюся инфрастурктуру (stun-серверы, яндекс-диск и т.п.), а не создавать новую.


              честно скажу, читая прошлую статью, я не проникся. а потом подумал: ведь на самом деле мысли дельные. решил "надо будет на эту тему ещё подумать".


              а тут автор вторую статью написал с развитием идеи, и развитие, считаю, в правильном направлении.


  1. edo1h
    19.12.2019 14:37
    +1

    Но это всего лишь пол дела, встал вопрос как передать эти данные удаленному узлу и организовать VPN-соединение. Использование почтового протокола, а может Telegram?!

    мне кажется, тут dyndns бы подошёл


    1. Revertis
      19.12.2019 15:00

      Для передачи номера порта?


      1. edo1h
        19.12.2019 15:15
        +1

        а почему нет? в TXT записи, например, можно хранить ip:port


        1. Winseven Автор
          19.12.2019 15:22

          А тут есть над чем подумать, спасибо!


  1. DmitriyPanteleev
    19.12.2019 15:38

    IMHO: Заголовок «немножко» не корректный. Посредник есть! Вы параметры определяете через посредника, какой-то stun-сервер. Плюс Ваш метод не работает через «продвинутый», читай нормальный провайдерский CG-NAT, мобильных операторов или простой офисный прозрачный проксик.


    1. Winseven Автор
      19.12.2019 16:34

      Вы читали прошлую статью? Там посредником был арендованный VPS, за которого нужно платить — это самый большой минус, а данном случае VPS нет — платить не надо. STUN-сервер, в данном случае идеальный и бесплатный способ определения текущих параметров соединения.

      CG-NAT, мобильных операторов или простой офисный прозрачный проксик
      — это самое интересное, OpenVPN можно научить работать через прокси — идея для другой реализации. Я надеюсь, что статья не последняя, при моральной поддержке, есть куда развиваться… Спасибо за комментарии, я из них черпаю вектор развития)))


  1. e_v_genius
    19.12.2019 16:34
    +1

    Отличная статья! Спасибо.


  1. SEVENID
    20.12.2019 00:21

    Но это всего лишь пол дела, встал вопрос как передать эти данные удаленному узлу и организовать VPN-соединение. Использование почтового протокола, а может Telegram?!

    Можно развить эту мысль, зачем тогда openvpn?


  1. mishutka_ua
    20.12.2019 01:54

    Какая потеря скорости, в процентах, при использовании VPN-сервера на VPS?


  1. eri
    21.12.2019 02:32

    Почему openvpn когда l2tpv3 поднять проще? А шифрование можно применить внутри трубы политикой ipsec


    1. Winseven Автор
      21.12.2019 10:43

      Так исторически сложилось, использовался OpneVPN, так как изначально вдохновение дала статья об OpenVPN в режиме peer-to-peer. Спасибо Вам за идею l2tpv3, я обязательно учту в следующей реализации.


      1. eri
        21.12.2019 11:03

        У руки чешутся сделать впн с инициализацией через торент и dht. Магнет ссылку кидаем и коннект через весь мир совсем без серверов.


        1. ton1
          21.12.2019 13:43

          Сколько там время анонса в dht сети? Имхо будет быстрее заюзать пачку открытых трекеров.


          1. eri
            21.12.2019 13:47

            в магнет ссылке можно указать трекер


    1. extraterrestrial
      22.12.2019 20:10
      +1

      Решил попробовать l2tpv3, работает.
      Какое-то оборудование во время простоя быстро закрывало порт, и туннель переставал работать. Быстро растолкать придумал только ping`ом. OpenWRT (19.07-rc2), роутер сказал что не знает про l2tpv3 :-)
      Прикинув что есть UDP, простое, быстрое, шифрование и с Keepalive — WireGuard. Работает.


      1. Winseven Автор
        22.12.2019 20:16

        во время простоя быстро закрывало порт
        Потому что роутер «на лету» транслирует (подменяет) обратный IP-адрес пакета на свой внешний (видимый из интернета) IP-адрес и меняет номер порта (чтобы различать ответные пакеты, адресованные разным локальным компьютерам). Комбинацию, нужную для обратной подстановки, роутер сохраняет у себя во временной таблице. Если какое-то время (около минуты, но не факт) не было трафика, то запись во временной таблице удаляется и соединение пропадает. Спасибо за WireGuard…


      1. eri
        22.12.2019 21:47
        +1

        в опнврт ставь kmod-l2tp и kmod-l2tp-ip (или kmod-l2tp-eth для л2 туннелей)
        пакет ip-full если соединение создаешь на баше.

        Для поддержания соединения у меня там тусит bird, проверяет доступность пира и маршрутами обменивается.

        UDP очень быстро забывается роутерами 10 секунд одиночный пакет, 3 минуты если поток только в одну сторону.