Terowongan VPN langsung antara komputer melalui penyedia NAT (tanpa VPS, menggunakan server STUN dan Yandex.Disk)

Kelanjutan artikel tentang bagaimana saya berhasil mengatur terowongan VPN langsung antara dua komputer yang terletak di belakang penyedia NAT. Artikel terakhir menjelaskan proses mengatur koneksi menggunakan pihak ketiga - perantara (VPS sewaan bertindak sebagai sesuatu seperti server STUN dan pemancar data node untuk koneksi). Pada artikel ini saya akan memberitahu Anda bagaimana melakukannya tanpa VPS, tetapi perantara tetap dan mereka adalah server STUN dan Yandex. Disk ...


Pendahuluan


Setelah membaca komentar dari posting terakhir, saya menyadari bahwa kelemahan utama dari implementasi adalah penggunaan perantara - pihak ketiga (VPS) yang menunjukkan parameter node saat ini, di mana dan bagaimana menghubungkan. Diberikan rekomendasi untuk menggunakan STUN ini (ada banyak di antaranya ) untuk menentukan parameter koneksi saat ini. Pertama-tama, saya memutuskan untuk melihat isi paket menggunakan TCPDump ketika server STUN bekerja dengan klien dan menerima konten yang sama sekali tidak dapat dibaca. Googling protokol menemukan artikel yang menggambarkan protokol . Saya menyadari bahwa saya tidak dapat mengimplementasikan permintaan ke server STUN sendiri dan memasukkan gagasan itu ke "kotak jauh".

Teori


Baru-baru ini saya harus menginstal server STUN pada Debian dari sebuah paket
# apt install stun-server 
dan dalam dependensi saya melihat paket setrum-klien, tetapi entah bagaimana tidak mementingkan ini. Tetapi kemudian, saya ingat tentang paket setrum-klien dan memutuskan untuk mencari tahu cara kerjanya, dengan googling dan Poindeksiv yang saya terima:

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

Sebagai tanggapan, saya menerima:

STUN klien versi 0.97
Membuka port 21234 dengan fd 3
Membuka port 21235 dengan fd 4
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 0

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 4

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 2

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Menerima pesan setrum: 92 byte
MappedAddress = <My IP>: 2885
SourceAddress = 216.93.246.18//478
ChangedAddress = 216.93.246.17lla479
Atribut tidak dikenal: 32800
ServerName = Vovida.org 0,98-CPC
Menerima pesan tipe 257 id = 1
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 0

Akan mengirim pesan dari len 28 ke 216.93.246.17lla478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 4

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 2

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 0

Akan mengirim pesan dari len 28 ke <My IP>: 2885
Menerima pesan setrum: 28 byte
ChangeRequest = 0
Menerima pesan tipe 1 id = 11
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 0

Akan mengirim pesan dari len 28 ke 216.93.246.17lla478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 4

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 2

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Menerima pesan setrum: 92 byte
MappedAddress = <My IP>: 2885
SourceAddress = 216.93.246.17lla479
ChangedAddress = 216.93.246.18 {478
Atribut tidak dikenal: 32800
ServerName = Vovida.org 0,98-CPC
Menerima pesan tipe 257 id = 10
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 4

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 2

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 4

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 2

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 4

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 2

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 4

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 2

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 4

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
Pengkodean pesan setrum:
Pengkodean ChangeRequest: 2

Akan mengirim pesan dari len 28 ke 216.93.246.18 opin478
tes I = 1
tes II = 0
Tes III = 0
tes I (2) = 1
adalah nat = 1
IP yang dipetakan sama = 1
jepit rambut = 1
port pemelihara = 0
Primer: Pemetaan Independen, Port Dependent Filter, port acak, akan menyematkan rambut
Nilai kembali adalah 0x000006

String dengan nilai
MappedAddress = <My IP>: 2885

apa yang Anda butuhkan! Ini menampilkan status saat ini untuk koneksi pada port UDP lokal 21234. Tetapi ini hanya setengah dari pertempuran, timbul pertanyaan tentang bagaimana mentransfer data ini ke host jarak jauh dan membuat koneksi VPN. Menggunakan protokol surat, mungkin Telegram ?! Ada banyak pilihan dan saya memutuskan untuk menggunakan Yandex.Disk, karena saya menemukan sebuah artikel tentang bagaimana Curl bekerja melalui WebDav dengan Yandex.Disk . Setelah memikirkan implementasinya, saya sampai pada skema ini:

  1. Untuk memberi tanda bahwa node siap untuk membuat koneksi dengan kehadiran file tertentu dengan cap waktu pada Yandex.disk;
  2. Jika node sudah siap, dapatkan parameter saat ini dari server STUN;
  3. Unggah parameter saat ini ke Yandex.Disk;
  4. Periksa ketersediaan dan baca parameter situs jauh dari file di Yandex.Disk;
  5. Buat koneksi ke host jarak jauh menggunakan OpenVPN.

Berlatih


Setelah sedikit berpikir, dengan mempertimbangkan pengalaman artikel sebelumnya, saya menulis skrip cepat. Kami akan membutuhkan:
 # apt install openvpn stun-client curl 

Sebenarnya skrip itu sendiri:
Opsi awal
 # 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 

Untuk menjalankan skrip yang Anda butuhkan:
  1. Salin ke clipboard dan rekatkan ke editor, misalnya:
     # nano vpn8.sh 
  2. tentukan nama pengguna dan kata sandi dari Yandex.Disk.
  3. di bidang "--ifconfig 10.45.54. (1 atau 2) 255.255.255.252" tentukan alamat IP internal antarmuka
  4. buat secret.key dengan perintah:
     # openvpn --genkey --secret secret.key 
  5. membuat skrip dapat dieksekusi:
     # chmod +x vpn8.sh 
  6. jalankan skrip:
     # ./vpn8.sh nZbVGBuX5dtturD 

    di mana nZbVGBuX5dtturD adalah ID koneksi yang dihasilkan di sini

Pada node jarak jauh, lakukan hal yang sama kecuali untuk pembuatan secret.key dan koneksi ID, mereka harus identik.

Versi terbaru (untuk operasi yang benar, waktu harus disinkronkan):

 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='' fi else if (( $ex >= 5 )) then echo "$(date) " a=1 b='' fi (( ex++ )) sleep 5 fi done done 

Untuk menjalankan skrip yang Anda butuhkan:
  1. Salin ke clipboard dan rekatkan ke editor, misalnya:
     # nano vpn10.sh 
  2. tentukan login (baris kedua) dan kata sandi dari Yandex.Disk (baris ketiga).
  3. tentukan alamat IP internal terowongan (baris 4).
  4. membuat skrip dapat dieksekusi:
     # chmod +x vpn10.sh 
  5. jalankan skrip:
     # ./vpn10.sh nZbVGBuX5dtturD 

    di mana nZbVGBuX5dtturD adalah ID koneksi yang dihasilkan di sini

Pada node jarak jauh, lakukan hal yang sama, tentukan alamat IP internal terowongan dan koneksi ID yang sesuai.

Untuk memulai skrip saat startup, saya menggunakan perintah "nohup / <path to script> /vpn10.sh nZbVGBuX5dtturD> /var/log/vpn10.log 2> / dev / null &" yang terdapat dalam file /etc/rc.local

Kesimpulan


Script berfungsi, diuji pada Ubuntu 18.04 dan Debian 9. Anda dapat menggunakan layanan lain sebagai pemancar, tetapi untuk pengalaman saya menggunakan Yandex.Disk.
Dalam perjalanan percobaan, ditemukan bahwa beberapa jenis penyedia NAT tidak memungkinkan untuk koneksi. Sebagian besar dengan operator seluler di mana torrent diblokir.

Saya berencana untuk menyelesaikan dalam hal:
  • Pembuatan otomatis secret.key setiap kali Anda memulai, mengenkripsi dan menyalin ke Yandex.Disk untuk mentransfer ke host jarak jauh (Dipertimbangkan dalam versi yang diperbarui)
  • Secara otomatis Menetapkan Alamat IP Antarmuka
  • Enkripsi data sebelum mengunggah ke Yandex.Disk
  • Optimasi kode

Semoga ada IPv6 di setiap rumah!

Source: https://habr.com/ru/post/id481034/


All Articles