Запуск VPN-сервера за провайдерським NAT'ом

Стаття про те, як мені вдалося запустити VPN-сервер за NAT'ом домашнього провайдера (без білої IP-адреси). Відразу обмовлюся: що працездатність даної реалізації безпосередньо залежить від типу NAT використовуваного Вашим провайдером, а також роутером..
Отже, виникла у мене необхідність підключатися зі свого Android-смартфону до домашнього комп'ютера, обидва девайси підключені до Інтернету через провайдерські NAT'и, плюсом комп'ютер підключений через домашній роутер, який також NAT'іл з'єднання.
Класична схема з використанням орендованої VPS/VDS з білою IP-адресою, а також оренда білої IP-адреси у провайдера не розглядалася з кількох причин.
З урахуванням досвіду минулих статей, провівши кілька дослідів зі STUNами та NAT'ами провайдерів. Зважився на невеликий експеримент, виконавши команду на домашньому роутері, що працює на прошивці OpenWRT:

$ stun stun.sipnet.ru

отримав результат:

STUN client version 0.97
Primary: Independent Mapping, Independent Filter, random port, will hairpin
Return value is 0x000002

Дослівний переклад:
Independent Mapping – незалежне відображення
Independent Filter – незалежний фільтр
random port – випадковий порт
will hairpin - буде шпилька
Виконавши аналогічну команду на своєму ПК, отримав:

STUN client version 0.97
Primary: Independent Mapping, Port Dependent Filter, random port, will hairpin
Return value is 0x000006

Port Dependent Filter - порт залежний фільтр
Різниця в результатах виведення команд говорила про те, що домашній роутер вносив «свій внесок» у процес трансляції пакетів з Інтернету, це виявлялося в тому, що при виконанні команди на комп'ютері:

stun stun.sipnet.ru -p 11111 -v

я отримував результат:

...
MappedAddress = XX.1XX.1X4.2XX:4398
...

в цей момент на деякий час відкривалася UDP-сесія, якщо в цей момент надіслати UDP-запит (наприклад: netcat XX.1XX.1X4.2XX 4398 -u), то запит приходив на домашній роутер, що підтвердив запущений на ньому TCPDump, але запит не доходив до комп'ютера - IPtables як NAT-транслятор на роутері дропав його.
Запуск VPN-сервера за провайдерським NAT'ом
Але сам факт проходження UDP запиту через провайдер NAT давав надію на успіх. Так як роутер знаходиться в моїй юрисдикції, вирішив проблему перенаправлення UDP/11111 порту на комп'ютер:

iptables -t nat -A PREROUTING -i eth1 -p udp -d 10.1XX.2XX.XXX --dport 11111 -j DNAT --to-destination 192.168.X.XXX

Тим самим отримав можливість ініціювати UDP-сесію та отримувати запити з Інтернету з будь-якої IP-адреси. У цей момент запустив OpenVPN-server (попередньо конфігурувавши) слухаючи UDP/11111 порт, вказав на смартфоні зовнішню IP-адресу та порт (XX.1XX.1X4.2XX:4398) і успішно підключився зі смартфона до комп'ютера. Але в цій реалізації виникла проблема, потрібно було якось підтримувати UDP-сесію до моменту підключення OpenVPN-клієнта до сервера, варіант із періодичним запуском STUN-клієнта мені не сподобався - не хотілося даремно навантажувати STUN-сервери.
Також звернув увагу на запис «will hairpin - буде шпилька«, даний режим

Hairpinning дозволяє одній машині в локальній мережі за NAT отримати доступ до іншої машини в тій самій мережі на адресу маршрутизатора.

Запуск VPN-сервера за провайдерським NAT'ом
У результаті проблему підтримки UDP-сесії вирішив просто — запустив клієнта на цьому комп'ютері з сервером.
Працювало це так:

  • запускав STUN-клієнта з локальним портом 11111
  • отримував відповідь із зовнішньою IP-адресою та портом XX.1XX.1X4.2XX:4398
  • надсилав дані із зовнішньою IP-адресою та портом на пошту (можливий будь-який інший сервіс), налаштовану на смартфоні
  • запускав OpenVPN-сервер на комп'ютері з прослуховуванням UDP/11111 порту
  • запускав OpenVPN-клієнта на комп'ютері із зазначенням XX.1XX.1X4.2XX:4398 для підключення
  • у будь-який час запускав OpenVPN-клієнта на смартфоні із зазначенням IP-адреси та порту (у моєму випадку IP-адреса не змінювалася) для підключення

Запуск VPN-сервера за провайдерським NAT'ом
Таким чином, я отримав можливість підключатися до свого комп'ютера зі смартфона. Ця реалізація дозволяє підключати будь-якого OpenVPN-клієнта.

Практика

знадобиться:

# apt install openvpn stun-client sendemail

Написавши пару скриптів, пару конфігураційних файлів, згенерувавши необхідні сертифікати (оскільки клієнт на смартфоні працює тільки за сертифікатами) вийшла звичайна реалізація OpenVPN-сервера.

Основний скрипт на комп'ютері

# cat vpn11.sh

#!/bin/bash
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
ABSOLUTE_FILENAME=`readlink -f "$0"`
DIR=`dirname "$ABSOLUTE_FILENAME"`
localport=11111
until [[ $a ]]; do
	address=`stun stun.sipnet.ru -v -p $localport 2>&1 | grep "MappedAddress" | sort | uniq | head -n 1 | sed 's/:/ /g' | awk '{print $3" "$4}'`
        ip=`echo "$address" | awk {'print $1'}`
        port=`echo "$address" | awk {'print $2'}`
	srv="openvpn --config $DIR/server.conf --port $localport --daemon"
	$srv
	echo "$(date) Сервер запущен с внешним адресом $ip:$port"
	$DIR/sendemail.sh "OpenVPN-Server" "$ip:$port"
	sleep 1
	openvpn --config $DIR/client.conf --remote $ip --port $port
	echo "$(date) Cоединение клиента с сервером разорвано"
	for i in `ps xa | grep "$srv" | grep -v grep | awk '{print $1}'`; do
		kill $i && echo "$(date) Завершен процесс сервера $i ($srv)"
		done
	echo "Жду 15 сек"
	sleep 15
	done

Скрипт надсилання даних на пошту:

# cat sendemail.sh 

#!/bin/bash
from="От кого"
pass="Пароль"
to="Кому"
theme="$1"
message="$2"
server="smtp.yandex.ru:587"
sendEmail -o tls=yes -f "$from" -t "$to" -s "$server" -xu "$from" -xp "$pass" -u "$theme" -m "$message"

Конфігураційний файл сервера:

# cat server.conf

proto udp
dev tun
ca      /home/vpn11-srv/ca.crt
cert    /home/vpn11-srv/server.crt
key     /home/vpn11-srv/server.key
dh      /home/vpn11-srv/dh2048.pem
server 10.2.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
tls-server
tls-auth /home/vpn11-srv/ta.key 0
tls-timeout 60
auth    SHA256
cipher  AES-256-CBC
client-to-client
keepalive 10 30
comp-lzo
max-clients 10
user nobody
group nogroup
persist-key
persist-tun
log /var/log/vpn11-server.log
verb 3
mute 20

Конфігураційний файл клієнта:

# cat client.conf

client
dev tun
proto udp
ca      "/home/vpn11-srv/ca.crt"
cert    "/home/vpn11-srv/client1.crt"
key     "/home/vpn11-srv/client1.key"
tls-client
tls-auth "/home/vpn11-srv/ta.key" 1
auth SHA256
cipher AES-256-CBC
auth-nocache
comp-lzo
user nobody
group nogroup
persist-key
persist-tun
log /var/log/vpn11-clent.log
verb 3
mute 20
ping 10
ping-exit 30

Генерація сертифікатів проводилася по цієї статті.
Запуск скрипту:

# ./vpn11.sh

Попередньо зробивши його виконуваним

# chmod +x vpn11.sh

На боці смартфона

Встановивши програму OpenVPN для Android, скопіювавши конфігураційний файл, сертифікати та налаштувавши його, вийшло так:
На смартфоні перевіряю поштуЗапуск VPN-сервера за провайдерським NAT'ом
Правлю номер порту в налаштуванняхЗапуск VPN-сервера за провайдерським NAT'ом
Запускаю клієнта та підключаюсьЗапуск VPN-сервера за провайдерським NAT'ом

У процесі написання статті я переніс конфігурацію з комп'ютера на Raspberry Pi 3 і спробував запустити всю цю справу на модемі LTE, але не вийшло! Результат команди

# stun stun.ekiga.net -p 11111

STUN client version 0.97
Primary: Independent Mapping, Port Dependent Filter, random port, will hairpin
Return value is 0x000006

значення Port Dependent Filter не дозволило запуститися системі.
Але домашній провайдер без проблем дав запустити систему на Raspberry Pi 3.
У зв'язці з веб-камерою, сVLC для
створення RTSP-потоку з веб-камери

$ cvlc v4l2:///dev/video0:chroma=h264 :input-slave=alsa://hw:1,0 --sout '#transcode{vcodec=x264,venc=x264{preset=ultrafast,profile=baseline,level=31},vb=2048,fps=12,scale=1,acodec=mpga,ab=128,channels=2,samplerate=44100,scodec=none}:rtp{sdp=rtsp://10.2.0.1:8554/}' --no-sout-all --sout-keep

і VLC на смартфоні для перегляду (потік rtsp://10.2.0.1:8554/), вийшла не погана система відеоспостереження на відстані, так само можна підняти Samba, маршрутизувати трафік через VPN, віддалено керувати комп'ютером і багато чого ще.

Висновок

Як показала практика, для оргінізації VPN-сервера можна обійтися і без зовнішньої IP-адреси за яку потрібно платити, так само як і за VPS/VDS, що орендується. Але все залежить від провайдера. Звичайно хотілося отримати більше інформації про різних провайдерів і типи використовуваних NAT'ів, але це тільки початок…
Дякуємо за увагу!

Джерело: habr.com

Додати коментар або відгук