Boa tarde!
Neste artigo, quero contar como implementei () script em Bash para conectar dois computadores que estão atrás de NAT, utilizando a tecnologia UDP hole punching com o sistema operacional Ubuntu/Debian.
A organização da conexão consiste em várias etapas:
- Iniciar o nó e esperar o nó remoto estar pronto;
- Determinar o endereço IP externo e a porta UDP;
- Enviar o endereço IP externo e a porta UDP para o nó remoto;
- Receber o endereço IP externo e a porta UDP do nó remoto;
- Organizar o túnel IPIP;
- Monitorar a conexão;
- Ao se desconectar, deletar o túnel IPIP.
Pensei por muito tempo e ainda estou pensando no que pode ser utilizado para troca de dados entre os nós, o mais simples e rápido para mim no momento é trabalhar através do Yandex.Disk.
- Em primeiro lugar, é simples de usar — são necessárias 3 ações: criar, ler, deletar. Com curl isso é:
Criar:curl -s -X MKCOL --user "$usename:$password" https://webdav.yandex.ru/$folderLer:
curl -s --user "$usename:$password" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/$folderDeletar:
curl -s -X DELETE --user "$usename:$password" https://webdav.yandex.ru/$folder - Em segundo lugar, é simples de instalar:
apt install curl
Para determinar o endereço IP externo e a porta UDP, usa-se o stun-client com o comando:
stun stun.sipnet.ru -v -p $1 2>&1 | grep "MappedAddress"Instalar com o comando:
apt install stun-clientPara organizar o túnel, são usados os recursos padrão do sistema operacional do pacote iproute2. Existem que podem ser configurados com ferramentas padrão (L2TPv3, GRE, etc.), mas eu escolhi IPIP porque ele cria uma carga adicional mínima para o sistema. Eu tentei L2TPv3 sobre UDP e me decepcionei, a velocidade caiu 10 vezes, mas isso pode ser limitações diversas relacionadas aos provedores ou outras questões. Como o túnel IPIP opera no nível IP, usa-se o túnel FOU para operar no nível das portas UDP. Para organizar o túnel IPIP, é necessário:
— carregar o módulo FOU:
modprobe fou— escutar a porta local:
ip fou add port $localport ipproto 4— criar o túnel:
ip link add name fou$name type ipip remote $remoteip local $localip encap fou encap-sport $localport encap-dport $remoteport— ativar a interface do túnel:
ip link set up dev fou$name— atribuir o endereço IP interno local e o IP interno remoto do túnel:
ip addr add $intIP peer $peerip dev fou$nameDeletar o túnel:
ip link del dev fou$nameip fou del port $localportO monitoramento do estado do túnel é realizado através de um ping periódico ao IP interno do túnel do nó remoto, com o comando:
ping -c 1 $peerip -s 0O ping periódico é necessário principalmente para manter o canal. Caso contrário, se o túnel ficar ocioso, as tabelas NAT nos roteadores podem ser limpas e a conexão será interrompida.
Se o ping falhar, o túnel IPIP é removido e aguarda a disponibilidade do nó remoto.
O próprio script:
#!/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 0Variáveis nome de usuário, senha e folder devem ser iguais em ambos os lados, enquanto intip pode ser diferente, por exemplo: 10.0.0.1 e 10.0.0.2. O tempo nos nós deve estar sincronizado. O script pode ser executado assim:
nohup script.sh &Ressalto que o túnel IPIP não é seguro em termos de que o tráfego não é criptografado, mas isso pode ser facilmente resolvido com IPsec, , que eu considerava simples e compreensível.
Tenho usado este script para me conectar ao meu PC de trabalho há várias semanas e não notei problemas. É conveniente no sentido de que configurei e esqueci.
Talvez você tenha comentários e sugestões, ficarei feliz em ouvi-los.
Obrigado pela atenção!
Fonte: habr.com
