Точно така, днес ще кажем същото на бога на криптирането.
Тук ще говорим за некриптиран IPv4 тунел, но не за „топла лампа“, а за модерен „LED“. Тук също мигат необработени сокети и се работи с пакети в потребителското пространство.
Има N протокола за тунелиране за всеки вкус и цвят:
Но аз съм програмист, така че ще увелича N само с малка част и ще оставя разработването на реални протоколи на разработчиците на Комерсант.
В едно неродено проектТова, което правя сега, е да достигна до хостове зад NAT отвън. Използвайки протоколи с криптография за възрастни за това, не можех да се отърся от усещането, че е като да стреляте врабчета от оръдие. защото тунелът се използва в по-голямата си част само за пробиване на дупки в NAT-e, вътрешният трафик обикновено също е криптиран, но те все още се давят в HTTPS.
Докато проучвах различни протоколи за тунелиране, вниманието на моя вътрешен перфекционист беше насочено към IPIP отново и отново поради минималните му разходи. Но има един и половина значителни недостатъци за моите задачи:
изисква публични IP адреси от двете страни,
и няма удостоверяване за вас.
Следователно перфекционистът беше върнат обратно в тъмния ъгъл на черепа или където и да седи там.
И тогава един ден, докато четях статии за естествено поддържани тунели в Linux попаднах на FOU (Foo-over-UDP), т.е. каквото и да е, обвито в UDP. Засега се поддържат само IPIP и GUE (Generic UDP Encapsulation).
„Ето го сребърният куршум! Един обикновен IPIP е достатъчен за мен. - Мислех.
Всъщност куршумът се оказа не напълно сребърен. Капсулирането в UDP решава първия проблем - можете да се свържете с клиенти зад NAT отвън, като използвате предварително установена връзка, но тук половината от следващия недостатък на IPIP цъфти в нова светлина - всеки от частна мрежа може да се скрие зад видимата публичен IP и клиентски порт (в чист IPIP този проблем не съществува).
За да разреши този един и половина проблем, се роди помощната програма ипипоу. Той прилага домашно направен механизъм за удостоверяване на отдалечен хост, без да нарушава работата на FOU на ядрото, което бързо и ефективно ще обработва пакети в пространството на ядрото.
Нямаме нужда от вашия сценарий!
Добре, ако знаете публичния порт и IP на клиента (например, всички зад него не отиват никъде, NAT се опитва да картографира портове 1-в-1), можете да създадете 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 адрес, присвоен на интерфейс eth0
10001 — локален клиентски порт за FOU
20001 — публичен клиентски порт за FOU
10000 — публичен сървърен порт за FOU
encap-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 и [почти] всички останали отиват направо към слушателя FOU.
За тези, които не знаят, nfqueue (или NetfilterQueue) е специално нещо за аматьори, които не знаят как да разработват модули на ядрото, което с помощта на netfilter (nftables/iptables) ви позволява да пренасочвате мрежови пакети към потребителското пространство и да ги обработвате там с помощта на примитивни средства под ръка: модифицирайте (по избор) и го върнете обратно на ядрото или го изхвърлете.
За някои езици за програмиране има обвързвания за работа с nfqueue, за bash нямаше (хей, не е изненадващо), трябваше да използвам python: ipipou използва NetfilterQueue.
Ако производителността не е критична, с помощта на това нещо можете сравнително бързо и лесно да измислите своя собствена логика за работа с пакети на сравнително ниско ниво, например да създадете експериментални протоколи за пренос на данни или да тролирате локални и отдалечени услуги с нестандартно поведение.
Необработените сокети работят ръка за ръка с nfqueue, например, когато тунелът вече е конфигуриран и 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. По отношение на степента на близост до реалността, това е приблизително същото като емулирането на реалния свят в Minecraft, но засега ще свърши работа.
Участници в състезанието:
референтен основен канал
героят на тази статия е ipipou
OpenVPN с удостоверяване, но без криптиране
OpenVPN в режим all inclusive
WireGuard без PresharedKey, с MTU=1440 (само от IPv4)
Технически данни за маниаци Метриките се вземат със следните команди:
на клиента:
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"
# Где "-b 12M" это пропускная способность основного канала, делённая на число потоков "-P", чтобы лишние пакеты не плодить и не портить производительность.