ΠŸΡ€ΠΎΡΡ‚ΠΎΠΉ UDP hole punching Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ IPIP-туннСля

Π”ΠΎΠ±Ρ€ΠΎΠ΅ врСмя суток!

Π’ этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ Ρ…ΠΎΡ‡Ρƒ Ρ€Π°ΡΡΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΊΠ°ΠΊ я Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π» (Π΅Ρ‰Π΅ ΠΎΠ΄ΠΈΠ½) скрипт Π½Π° Bash для соСдинСния Π΄Π²ΡƒΡ… ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€ΠΎΠ², находящимися Π·Π° NAT, с использованиСм Ρ‚Π΅Ρ…Π½ΠΎΠ»ΠΎΠ³ΠΈΠΈ UDP hole punching Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ ОБ Ubuntu/Debian.

ΠžΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΡ соСдинСния состоит ΠΈΠ· Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… шагов:

  1. Запуск ΡƒΠ·Π»Π° ΠΈ ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ готовности ΡƒΠ΄Π°Π»Π΅Π½Π½ΠΎΠ³ΠΎ ΡƒΠ·Π»Π°;
  2. ΠžΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ внСшнСго IP-адрСса ΠΈ UDP-ΠΏΠΎΡ€Ρ‚Π°;
  3. ΠŸΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° внСшнСго IP-адрСса ΠΈ UDP-ΠΏΠΎΡ€Ρ‚Π° ΡƒΠ΄Π°Π»Π΅Π½Π½ΠΎΠΌΡƒ ΡƒΠ·Π»Ρƒ;
  4. ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ внСшнСго IP-адрСса ΠΈ UDP-ΠΏΠΎΡ€Ρ‚Π° ΠΎΡ‚ ΡƒΠ΄Π°Π»Π΅Π½Π½ΠΎΠ³ΠΎ ΡƒΠ·Π»Π°;
  5. ΠžΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΡ IPIP-туннСля;
  6. ΠœΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ соСдинСния;
  7. ΠŸΡ€ΠΈ ΠΎΠ±Ρ€Ρ‹Π²Π΅ соСдинСния ΡƒΠ΄Π°Π»ΡΡ‚ΡŒ 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="[email protected]"
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/\ | 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/\
}
function yadelete {
        curl -s -X DELETE --user "$1:$2" https://webdav.yandex.ru/\
}
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 ΠΏΠΎ этой ΡΡ‚Π°Ρ‚ΡŒΠ΅, ΠΎΠ½Π° ΠΌΠ½Π΅ показалось простой ΠΈ понятной.

Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽ Π΄Π°Π½Π½Ρ‹ΠΉ скрипт для ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΊ Ρ€Π°Π±ΠΎΡ‡Π΅ΠΌΡƒ ПК ΡƒΠΆΠ΅ нСсколько нСдСль ΠΈ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ Π½Π΅ Π·Π°ΠΌΠ΅Ρ‡Π°Π». Π£Π΄ΠΎΠ±Π½ΠΎ Π² ΠΏΠ»Π°Π½Π΅ Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ настроил ΠΈ Π·Π°Π±Ρ‹Π».

Π’ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ Ρƒ Вас Π±ΡƒΠ΄ΡƒΡ‚ замСчания ΠΈ прСдлоТСния, Π±ΡƒΠ΄Ρƒ Ρ€Π°Π΄ Π²Ρ‹ΡΠ»ΡƒΡˆΠ°Ρ‚ΡŒ.

Бпасибо за вниманиС!

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com