ipipou: повече от просто некриптиран тунел

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

ipipou: повече от просто некриптиран тунел
Точно така, днес ще кажем същото на бога на криптирането.

Тук ще говорим за некриптиран IPv4 тунел, но не за „топла лампа“, а за модерен „LED“. Тук също мигат необработени сокети и се работи с пакети в потребителското пространство.

Има N протокола за тунелиране за всеки вкус и цвят:

  • стилен, модерен, младежки WireGuard
  • многофункционални, като швейцарски ножове, OpenVPN и SSH
  • стар и не зъл GRE
  • най-простият, бърз, напълно некриптиран IPIP
  • активно се развива ЖЕНЕВ
  • много други.

Но аз съм програмист, така че ще увелича 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", чтобы лишние пакеты не плодить и не портить производительность.

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"

Тунелна конфигурация

ипипоу
сървър
/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, всичко според очакванията)
Конфигуриран с помощта на openvpn-управление

телена охрана
сървър
/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

резултати

Влажен грозен знак
Натоварването на процесора на сървъра не е много показателно, т.к. Там работят много други услуги, понякога те изяждат ресурси:

proto bandwidth[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# 20 Mbps канал с микрокомпьютера (4 core) до VPS (1 core) через Атлантику
# pure
UDP 20.4      99.80 93.34
TCP 19.2      99.67 96.68
ICMP latency min/avg/max/mdev = 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 latency min/avg/max/mdev = 199.562/208.919/220.222/7.905 ms
# openvpn0 (auth only, no encryption)
UDP 19.3      99.89 72.90
TCP 16.1      95.95 88.46
ICMP latency min/avg/max/mdev = 191.631/193.538/198.724/2.520 ms
# openvpn (full encryption, auth, etc)
UDP 19.6      99.75 72.35
TCP 17.0      94.47 87.99
ICMP latency min/avg/max/mdev = 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 latency min/avg/max/mdev = 217.925/223.601/230.696/3.266 ms

## около-1Gbps канал между VPS Европы и США (1 core)
# pure
UDP 729      73.40 39.93
TCP 363      96.95 90.40
ICMP latency min/avg/max/mdev = 106.867/106.994/107.126/0.066 ms
# ipipou
UDP 714      63.10 23.53
TCP 431      95.65 64.56
ICMP latency min/avg/max/mdev = 107.444/107.523/107.648/0.058 ms
# openvpn0 (auth only, no encryption)
UDP 193      17.51  1.62
TCP  12      95.45 92.80
ICMP latency min/avg/max/mdev = 107.191/107.334/107.559/0.116 ms
# wireguard
UDP 629      22.26  2.62
TCP 198      77.40 55.98
ICMP latency min/avg/max/mdev = 107.616/107.788/108.038/0.128 ms

20 Mbps канал

ipipou: повече от просто некриптиран тунел

ipipou: повече от просто некриптиран тунел

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

ipipou: повече от просто некриптиран тунел

ipipou: повече от просто некриптиран тунел

Във всички случаи ipipou е доста близък по производителност до основния канал, което е страхотно!

Некриптираният openvpn тунел се държеше доста странно и в двата случая.

Ако някой ще го тества, ще е интересно да чуя отзиви.

Нека IPv6 и NetPrickle бъдат с нас!

Източник: www.habr.com

Добавяне на нов коментар