Какво казваме на бога IPv6?

Верно, и на бога на шифроването днес ще му кажем същото.
Тук ще стане дума за нешифрован IPv4 тунел, но не за "топъл лампов", а за модерен "LED". И тук ще се появят сурови сокети, и ще се работи с пакети в потребителското пространство.
Има N протоколи за тунелиране за всеки вкус и цвят:
- стилен, модерен, младежки
- мултифункционални, като швейцарски ножове, OpenVPN и SSH
- стар и добродушен GRE
- максимално прост, бърз, съвсем нешифрован IPIP
- активно развиващ се
- много други.
Но аз съм програмист, затова ще увелича N само с малко, а разработката на истински протоколи я оставям на Ъ-разработчиците.
В един още не родил се , с който в момента се занимавам, трябва да достигна до хостовете зад NAT отвън. Използвайки за целта протоколи с добра криптография, ме напускаше усещането, че това е като да стреляш по врабчета с пушката. Тъй като тунелът основно се използва за пробиване на дупка в NAT, вътрешният трафик обикновено също е шифрован, все пак отбелязват за HTTPS.
Изследвайки различни протоколи за тунелиране, вниманието на вътрешния ми перфекционист многократно привличаше IPIP заради минималните разходи. Но за моите нужди той има един и половина съществени недостатъка:
- изисква публични IP адреси от двете страни,
- и никаква аутентификация.
Затова перфекционистът се завръщаше обратно в тъмен ъгъл на черепната кутия, или там, където се крие.
И ето как веднъж, четейки статии за в Linux, се натъкнах на FOU (Foo-over-UDP), т.е. нещо, завито в UDP. В момента от нещата, поддържани, само IPIP и GUE (Generic UDP Encapsulation) са факт.
"Ето я сребърната куршум! И простият IPIP ми стига" — мислех аз.
На практика куршумът се оказа не напълно сребърен. Инкапсулацията в UDP решава първия проблем — клиентите зад NAT могат да се свържат отвън, използвайки предварително установена връзка, но тук половината от следващия недостатък на IPIP цъфти в нова светлина — зад видимите публични IP и порта на клиента може да се крие кого ли не от частната мрежа (в чистият IPIP този проблем не съществува).
За решаването на този полуабсолютен проблем и се роди утилитата В него е реализиран самоделен механизъм за удостоверяване на отдалечен хост, без да се нарушава работата на ядрения FOU, който бързо и ефективно ще обработва пакетите в ядрото.
Не ти трябва сценарият!
Добре, ако знаеш публичния порт и IP адреса на клиента (например, след него не ходят произволно, NAT се опитва да картира портовете един към един), можеш да създадеш IPIP-over-FOU тунел с следните команди, без никакви скриптове.
на сървъра:
# Подгрузить модуль ядра FOU
modprobe fou
# Создать IPIP туннель с инкапсуляцией в FOU.
# Модуль ipip подгрузится автоматически.
ip link add name ipipou0 type ipip
remote 198.51.100.2 local 203.0.113.1
encap fou encap-sport 10000 encap-dport 20001
mode ipip dev eth0
# Добавить порт на котором будет слушать FOU для этого туннеля
ip fou add port 10000 ipproto 4 local 203.0.113.1 dev eth0
# Назначить IP адрес туннелю
ip address add 172.28.0.0 peer 172.28.0.1 dev ipipou0
# Поднять туннель
ip link set ipipou0 up
на клиента:
modprobe fou
ip link add name ipipou1 type ipip
remote 203.0.113.1 local 192.168.0.2
encap fou encap-sport 10001 encap-dport 10000 encap-csum
mode ipip dev eth0
# Опциите local, peer, peer_port и dev може да не се поддържат от стари ядра, може да бъдат пропуснати.
# peer и peer_port се използват за създаване на връзка веднага при създаването на FOU-listener.
ip fou add port 10001 ipproto 4 local 192.168.0.2 peer 203.0.113.1 peer_port 10000 dev eth0
ip address add 172.28.0.1 peer 172.28.0.0 dev ipipou1
ip link set ipipou1 up
където
ipipou*— име на локалния тунелен мрежов интерфейс203.0.113.1— публичен IP адрес на сървъра198.51.100.2— публичен IP адрес на клиента192.168.0.2— IP адрес на клиента, назначен на интерфейса eth010001— локален порт на клиента за FOU20001— публичен порт на клиента за FOU10000— публичен порт на сървъра за FOUencap-csum— опцията за добавяне на контролна сума UDP в инкапсулираните UDP пакети; може да бъде заменена сnoencap-csum, за да не се брои, цялостността се контролира от външния слой на инкапсулацията (докато пакетът е вътре в тунела)eth0— локалният интерфейс, с който ще бъде свързан IPIP тунелът172.28.0.1— IP адрес на тунелния интерфейс на клиента (приватен)172.28.0.0— IP адрес на тунелния интерфейс на сървъра (приватен)
Докато UDP-съвместимостта е активна, тунелът ще работи, а когато прекъсне, каквото стане — ако IP: портът на клиента останат същите — ще преживее, ако се променят — ще се счупи.
Най-лесно е да се върне всичко обратно, като се изтеглят модулите на ядрото: modprobe -r fou ipip
Дори ако удостоверяването не е необходимо, публичните IP и порт на клиента не винаги са известни и често са непредсказуеми или променливи (в зависимост от типа NAT). Ако пропуснеш encap-dport отстрани на сървъра, тунелът няма да заработи, не е толкова умен, за да взима отдалечения порт на връзката. В този случай ipipou също може да помогне, или WireGuard и подобните му.
Как работи това?
Клиентът (който обикновено е зад NAT) инициира тунел (как в примера по-горе) и изпраща пакет с автентикация към сървъра, за да настрои тунела от своя страна. В зависимост от конфигурацията, това може да бъде празен пакет (просто за да види сървърът публичния IP: порт на връзката), или с данни, с които сървърът може да идентифицира клиента. Данните могат да бъдат обикновена парола на открит текст (върху която може да се направи аналогия с HTTP Basic Auth) или подписани с частен ключ специално оформени данни (по аналогия с HTTP Digest Auth, но по-съществени). client_auth в кода).
На сървъра (страна с публичен IP) при стартиране ipipou създава обработчик на опашката nfqueue и настройва netfilter, така че нужните пакети да бъдат насочени там, където трябва: пакетите, иницииращи връзката в опашка nfqueue, а [почти] всички останали директно към listener FOU.
Който не е наясно, nfqueue (или NetfilterQueue) е специална функция за дилетанти, които не знаят как да разработват модули на ядрото, която със средствата на netfilter (nftables/iptables) позволява пренасочване на мрежови пакети в пространството на потребителя и обработването им с примитивни средства: модифициране (по желание) и връщане обратно на ядрото, или отхвърляне.
За някои програмни езици съществуват биндинги за работа с nfqueue, за bash не намерих (хех, не е изненадващо), затова трябваше да използвам python: ipipou използва .
Ако производителността не е критична, с помощта на тази функция може да се създаде сравнително бързо и лесно собствена логика за работа с пакети на доста ниско ниво, например да се конструират експериментални протоколи за предаване на данни или да се тролират локални и отдалечени услуги с нестандартно поведение.
Ръка за ръка с nfqueue работят сурови сокети (raw sockets), например когато тунелът вече е настроен и FOU слуша на желания порт, не може да се изпрати пакет по обикновен начин от този същия порт — той е зает, но може да се вземе и да се изпрати произвольно генериран пакет директно в мрежовия интерфейс, използвайки суров сокет, дори и за генерирането на подобен пакет да се наложи да се поработи малко повече. Тук се създават в ipipou пакети с автентикация.
Тъй като ipipou обработва само първите пакети от връзката (и тези, които са успели да преминат в опашката преди установяване на връзка), производителността почти не страда.
След като ipipou-сървърът получи пакет, който е преминал аутентификацията, тунелът се създава и всички последващи пакети в връзката вече се обработват от ядрото, заобикаляйки nfqueue. Ако връзката е изтекла, първият пакет на следващата връзка ще бъде насочен в опашката nfqueue, в зависимост от настройките, ако това не е пакет с аутентификация, но с последния запомнен IP и порт на клиента, той може да бъде или пропуснат нататък, или отхвърлен. Ако аутентифицираният пакет идва от нови IP и порт, тунелът се пренастройва за тяхното използване.
Обикновеният IPIP-over-FOU има още един проблем при работа с NAT — не може да се създадат два IPIP тунела, инкапсулирани в UDP, с еднакви IP, тъй като модулите FOU и IPIP са достатъчно изолирани един от друг. Тоест, двойка клиенти зад един публичен IP не могат едновременно да се свържат към един и същ сървър по този начин. В бъдеще, , ще бъде решен на ниво ядрото, но това не е сигурно. А засега проблемите с NAT могат да се решат с NAT — ако се случи така, че двойка IP адреси вече е заета от друг тунел, ipipou ще направи NAT от публичния на алтернативен частен IP, вуаля! — могат да се създават тунели, докато не свършат портовете.
Понеже не всички пакети в връзката са подписани, такава простичка защита е уязвима на MITM, така че ако по пътя между клиента и сървъра се крие злодей, който може да слуша трафика и да го контролира, той може да пренасочва пакети с аутентификация през друг адрес и да създаде тунел от недоверен хост.
Ако някой има идеи как да се реши това, оставяйки основната част от трафика в ядрото, не се колебайте — изразете се.
Между другото, инкапсулацията в UDP се е доказала много добре. В сравнение с инкапсулацията върху IP, тя е много по-стабилна и често по-бърза, въпреки допълнителните разходи за UDP заглавието. Това се дължи на факта, че в интернет по-голямата част от хостовете работят задоволително само с трите най-популярни протокола: TCP, UDP, ICMP. Осезаема част може изобщо да отхвърля всичко останало или да обработва по-бавно, тъй като е оптимизирана само за тези три.
Например, именно по этой причине QUICK, на базе которого построен HTTP/3, был создан поверх UDP, а не IP.
Хватит слов, давайте посмотрим, как это работает в «реальном мире».
Батл
Для имитации реального мира используется iperf3. По степени приближённости к реальности это примерно как эмуляция реального мира в Майнкрафт, хотя пока и сойдёт.
В соревновании участвуют:
- эталонный основной канал
- герой этой статьи ipipou
- OpenVPN с аутентификацией, но без шифрования
- OpenVPN в режиме «всё включено»
- WireGuard без PresharedKey, с MTU=1440 (так как IPv4-only)
Технические данные для гиков
Метрики снимаются такими командами
на клиента:
UDP
CPULOG=NAME.udp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -c SERVER_IP -4 -t 60 -f m -i 10 -B LOCAL_IP -P 2 -u -b 12M; tail -1 "$CPULOG"
TCP
CPULOG=NAME.tcp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -c SERVER_IP -4 -t 60 -f m -i 10 -B LOCAL_IP -P 2; tail -1 "$CPULOG"
ICMP задержка
ping -c 10 SERVER_IP | tail -1
на сервере (запускается одновременно с клиентом):
UDP
CPULOG=NAME.udp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -s -i 10 -f m -1; tail -1 "$CPULOG"
TCP
CPULOG=NAME.tcp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -s -i 10 -f m -1; tail -1 "$CPULOG"
Конфигурация туннелей
ipipou
сървър
/etc/ipipou/server.conf:
server
number 0
fou-dev eth0
fou-local-port 10000
tunl-ip 172.28.0.0
auth-remote-pubkey-b64 eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI=
auth-secret topsecret
auth-lifetime 3600
reply-on-auth-ok
verb 3
systemctl start ipipou@server
клиент
/etc/ipipou/client.conf:
client
number 0
fou-local @eth0
fou-remote SERVER_IP:10000
tunl-ip 172.28.0.1
# pubkey of auth-key-b64: eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI=
auth-key-b64 RuBZkT23na2Q4QH1xfmZCfRgSgPt5s362UPAFbecTso=
auth-secret topsecret
keepalive 27
verb 3
systemctl start ipipou@client
openvpn (без шифрования, с аутентификацией)
сървър
openvpn --genkey --secret ovpn.key # Затем нужно передать ovpn.key клиенту
openvpn --dev tun1 --local SERVER_IP --port 2000 --ifconfig 172.16.17.1 172.16.17.2 --cipher none --auth SHA1 --ncp-disable --secret ovpn.key
клиент
openvpn --dev tun1 --local LOCAL_IP --remote SERVER_IP --port 2000 --ifconfig 172.16.17.2 172.16.17.1 --cipher none --auth SHA1 --ncp-disable --secret ovpn.key
openvpn (с шифрованием, аутентификацией, через UDP, как положено)
Настроено с использованием
wireguard
сървър
/etc/wireguard/server.conf:
[Interface]
Address=172.31.192.1/18
ListenPort=51820
PrivateKey=aMAG31yjt85zsVC5hn5jMskuFdF8C/LFSRYnhRGSKUQ=
MTU=1440
[Peer]
PublicKey=LyhhEIjVQPVmr/sJNdSRqTjxibsfDZ15sDuhvAQ3hVM=
AllowedIPs=172.31.192.2/32
systemctl start wg-quick@server
клиент
/etc/wireguard/client.conf:
[Interface]
Address=172.31.192.2/18
PrivateKey=uCluH7q2Hip5lLRSsVHc38nGKUGpZIUwGO/7k+6Ye3I=
MTU=1440
[Peer]
PublicKey=DjJRmGvhl6DWuSf1fldxNRBvqa701c0Sc7OpRr4gPXk=
AllowedIPs=172.31.192.1/32
Endpoint=SERVER_IP:51820
systemctl start wg-quick@client
Резултати
Сырая ужасная таблица
Загрузка CPU сервера не является самым точным показателем, так как там работают много других сервисов и иногда они используют ресурсы:
прото пропускна способност[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# 20 Mbps канал с микрокомпютър (4 ядра) до VPS (1 ядро) през Атлантика
# чист
UDP 20.4 99.80 93.34
TCP 19.2 99.67 96.68
ICMP латентност мин/ср/макс/мdev = 198.838/198.997/199.360/0.372 ms
# ipipou
UDP 19.8 98.45 99.47
TCP 18.8 99.56 96.75
ICMP латентност мин/ср/макс/мdev = 199.562/208.919/220.222/7.905 ms
# openvpn0 (само автентикация, без криптиране)
UDP 19.3 99.89 72.90
TCP 16.1 95.95 88.46
ICMP латентност мин/ср/макс/мdev = 191.631/193.538/198.724/2.520 ms
# openvpn (пълно криптиране, автентикация и т.н.)
UDP 19.6 99.75 72.35
TCP 17.0 94.47 87.99
ICMP латентност мин/ср/макс/мdev = 202.168/202.377/202.900/0.451 ms
# wireguard
UDP 19.3 91.60 94.78
TCP 17.2 96.76 92.87
ICMP латентност мин/ср/макс/мdev = 217.925/223.601/230.696/3.266 ms
## около-1Gbps канал между VPS Европа и САЩ (1 ядро)
# чист
UDP 729 73.40 39.93
TCP 363 96.95 90.40
ICMP латентност мин/ср/макс/мdev = 106.867/106.994/107.126/0.066 ms
# ipipou
UDP 714 63.10 23.53
TCP 431 95.65 64.56
ICMP латентност мин/ср/макс/мdev = 107.444/107.523/107.648/0.058 ms
# openvpn0 (само автентикация, без криптиране)
UDP 193 17.51 1.62
TCP 12 95.45 92.80
ICMP латентност мин/ср/макс/мdev = 107.191/107.334/107.559/0.116 ms
# wireguard
UDP 629 22.26 2.62
TCP 198 77.40 55.98
ICMP латентност мин/ср/макс/мdev = 107.616/107.788/108.038/0.128 ms
канал от 20 Mbps


канал на 1 оптимистичен Gbps


Във всички случаи ipipou е доста близък по показатели до основния канал и това е прекрасно!
Нешифрованият тунел openvpn се държеше доста странно и в двата случая.
Ако някой реши да тества, ще бъде интересно да чуя отзивите.
Да пребъде с нас IPv6 и NetPrickle!
Източник: habr.com
