Тут будзе аб нешыфраваным IPv4 тунэлі, але не аб "цёплым лямпавым", а аб мадэрновым "святлодыёдным". А яшчэ тут мільгаюць волкія сокеты, і ідзе праца з пакетамі ў прасторы карыстача.
Ёсць 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 гэтай праблемы няма).
Для вырашэння гэтай паўтарачнай праблемы і нарадзілася ўтыліта ipipou. У ёй рэалізаваны самапальны механізм аўтэнтыфікацыі выдаленага хаста, пры гэтым не парушаючы працы ядранага 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, а [амаль] усе астатнія наўпрост у listener FOU.
Хто не ў тэме, nfqueue (або NetfilterQueue) - гэта такая спецыяльная штука для дылетантаў, якія не ўмеюць распрацоўваць модулі ядра, якая сродкамі netfilter (nftables/iptables) дазваляе перанакіроўваць сеткавыя пакеты ў прастору карыстальніка і апрацоўваць іх там прымітыўнымі падручнымі сродкамі: ) і аддаваць назад ядру, ці адкідаць.
Для некаторых моў праграмавання ёсць біндынгі для працы з nfqueue, для bash не знайшлося (хех, не дзіўна), прыйшлося выкарыстоўваць python: ipipou выкарыстоўвае NetfilterQueue.
Калі прадукцыйнасць не крытычная, з дапамогай гэтай штукі можна адносна хутка і проста кухарыць уласную логіку працы з пакетамі на досыць нізкім узроўні, напрыклад ляпіць эксперыментальныя пратаколы перадачы дадзеных, ці троліць лакальныя і выдаленыя сэрвісы нестандартнымі паводзінамі.
Рука аб руку з 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"
# Где "-b 12M" это пропускная способность основного канала, делённая на число потоков "-P", чтобы лишние пакеты не плодить и не портить производительность.