Direct VPN tunnel between computers through NATs of providers (without VPS, using STUN server and Yandex.disk)

Extension Articles about how I managed to organize a direct VPN tunnel between two computers located behind ISP NATs. The last article described the process of organizing a connection with the help of a third party - an intermediary (a rented VPS acting as something like a STUN server and a node data transmitter for connection). In this article I will tell you how I managed without VPS, but the intermediaries remained and they were the STUN server and Yandex.Disk ...
Direct VPN tunnel between computers through NATs of providers (without VPS, using STUN server and Yandex.disk)

Introduction

After reading the comments of the last post, I realized that the main drawback of the implementation was the use of an intermediary - a third party (VPS) that indicated the current parameters of the node, where and how to connect. Given the recommendations to use a real STUN (of which there are a lot) to determine the current connection settings. First of all, I decided to use TCPDump to look at the contents of the packets when the STUN server was working with clients and received completely unreadable contents. Googling the protocol came across article describing the protocol. I realized that I can’t implement a request to the STUN server on my own and put the idea away.

Theory

I recently had to install a STUN server on Debian from the package

# apt install stun-server

and in the dependencies I saw the stun-client package, but somehow did not attach any importance to this. But later I remembered the stun-client package and decided to figure out how it works, by googling and using Yandex I got:

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

In response I received:

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 = <My 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 <My 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 = <My 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
hair pin = 1
preserver port = 0
Primary: Independent Mapping, Port Dependent Filter, random port, will hairpin
return value is 0x000006

String with value

MappedAddress = <My IP>:2885

just what you need! It displayed the current state for the connection on the local UDP port 21234. But this is only half the battle, the question arose of how to transfer this data to a remote host and establish a VPN connection. Using the mail protocol, or maybe Telegram?! There are many options and I decided to use Yandex.disk, as I came across an article about working Curl via WebDav with Yandex.disk. After thinking about the implementation, I came up with this scheme:

  1. Signal that nodes are ready to establish a connection by the presence of a specific file with a timestamp on Yandex.disk;
  2. If the nodes are ready, then get the current parameters from the STUN server;
  3. Upload current settings to Yandex.disk;
  4. Check for the presence and read the parameters of a remote host from a file on Yandex.disk;
  5. Establishing a connection to a remote host using OpenVPN.

Practice

After a little thought, taking into account the experience of the last article, I wrote a script in haste. We will need:

# apt install openvpn stun-client curl 

Actually the script itself:

original version

# cat vpn8.sh

#!/bin/bash
######################## Π—Π°Π΄Π°Π΅ΠΌ Ρ†Π²Π΅Ρ‚Π½ΠΎΠΉ тСкст ###
WARN='33[37;1;41m'				#
END='33[0m'					#
RED='33[0;31m'         #  ${RED}		#
GREEN='33[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

For the script to work you need:

  1. Copy to clipboard and paste into editor, for example:
    # nano vpn8.sh 
  2. specify the login and password from Yandex.disk.
  3. in the field "β€”ifconfig 10.45.54.(1 or 2) 255.255.255.252" specify the internal IP address of the interface
  4. create secret key command:
    # openvpn --genkey --secret secret.key 
  5. make the script executable:
    # chmod +x vpn8.sh
  6. run script:
    # ./vpn8.sh nZbVGBuX5dtturD

    where nZbVGBuX5dtturD is the connection-id generated here

On the remote host, do the same except generate the secret.key and connection ID, they must be identical.

Updated version (for correct work, the time must be synchronized):

cat vpn10.sh

#!/bin/bash
stuns="stun.sipnet.ru stun.ekiga.net"   		# Бписок STUN сСрвСров Ρ‡Π΅Ρ€Π΅Π· ΠΏΡ€ΠΎΠ±Π΅Π»
username=" Login "		# Π›ΠΎΠ³ΠΈΠ½ ΠΎΡ‚ ЯндСкс.диска
password=" Password "   	# ΠŸΠ°Ρ€ΠΎΠ»ΡŒ ΠΎΡ‚ ЯндСкс.диска
intip="10.23.22.1"		# IP-адрСс Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½Π΅Π³ΠΎ интСрфСйса
WARN='33[37;1;41m'
END='33[0m'
RED='33[0;31m'
GREEN='33[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=''
fi
else
if (( $ex >= 5 ))
then
echo "$(date) Бброс"
a=1
b=''
fi
(( ex++ ))
sleep 5
fi
done
done

For the script to work you need:

  1. Copy to clipboard and paste into editor, for example:
    # nano vpn10.sh 
  2. specify the login (2nd line) and password from Yandex.disk (3rd line).
  3. specify the internal IP address of the tunnel (4th line).
  4. make the script executable:
    # chmod +x vpn10.sh
  5. run script:
    # ./vpn10.sh nZbVGBuX5dtturD

    where nZbVGBuX5dtturD is the connection-id generated here

On the remote host, do the same, specify the appropriate internal tunnel IP address and connection ID.

To autorun the script on startup, I use the command "nohup /<path to the script>/vpn10.sh nZbVGBuX5dtturD > /var/log/vpn10.log 2>/dev/null &" contained in the file /etc/rc.local

Conclusion

The script works, tested on Ubuntu (18.04, 19.10, 20.04) and Debian 9. You can use any other service as a transmitter, but for the experience I used Yandex.disk.
During the experiments, it was found that some types of NAT providers do not allow you to establish a connection. Mostly from mobile operators, where torrents are blocked.

I plan to improve in terms of:

  • Automatic generation of secret.key every time you start, encrypting and copying to Yandex.disk for transmission to a remote host (Considered in the updated version)
  • Automatic assignment of interface IP addresses
  • Data encryption before uploading to Yandex.disk
  • Code optimization

Let there be IPv6 in every home!

Updated! Latest files and DEB package here β€” yandex.disk

Source: habr.com

Add a comment