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 טהור בעיה זו לא קיימת).

כדי לפתור בעיה וחצי זו, נולד כלי השירות ipipou. הוא מיישם מנגנון תוצרת בית לאימות מארח מרוחק, מבלי לשבש את פעולת ה-FOU של הליבה, שיעבד במהירות וביעילות מנות בחלל הליבה.

אנחנו לא צריכים את התסריט שלך!

אוקיי, אם אתה יודע את היציאה הציבורית וה-IP של הלקוח (לדוגמה, כולם מאחורי זה לא הולכים לשום מקום, NAT מנסה למפות יציאות 1-in-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: port של הלקוח יישאר זהה - היא תפעל, אם ישתנו - היא תישבר.

הדרך הקלה ביותר להחזיר הכל לאחור היא לפרוק את מודולי הליבה: 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) מאפשרים להפנות מנות רשת למרחב המשתמש ולעבד אותן שם באמצעות אמצעי פרימיטיבי בהישג יד: לשנות (אופציונלי) ולהחזיר אותו לגרעין, או להשליך אותו.

עבור כמה שפות תכנות יש bindings לעבודה עם nfqueue, עבור bash לא היה כזה (הה, לא מפתיע), הייתי צריך להשתמש ב-python: ipipou משתמש Netfilter Queue.

אם הביצועים אינם קריטיים, באמצעות הדבר הזה אתה יכול לרקוח במהירות ובקלות יחסית את ההיגיון שלך לעבודה עם מנות ברמה נמוכה למדי, למשל, ליצור פרוטוקולים ניסיוניים להעברת נתונים, או לנטרל שירותים מקומיים ומרוחקים עם התנהגות לא סטנדרטית.

שקעי Raw עובדים יד ביד עם 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 הוכיחה את עצמה היטב. בהשוואה ל-encapsulation over IP, הוא הרבה יותר יציב ולעיתים מהיר יותר למרות התקורה הנוספת של כותרת UDP. זאת בשל העובדה שרוב המארחים באינטרנט עובדים היטב רק עם שלושת הפרוטוקולים הפופולריים ביותר: TCP, UDP, ICMP. החלק המוחשי יכול להשליך לחלוטין את כל השאר, או לעבד אותו לאט יותר, מכיוון שהוא מותאם רק לשלושה אלה.

לדוגמה, זו הסיבה ש-QUICK, שעליו מבוסס HTTP/3, נוצר על גבי UDP, ולא על גבי IP.

ובכן, מספיק מילים, הגיע הזמן לראות איך זה עובד ב"עולם האמיתי".

קרב

משמש כדי לחקות את העולם האמיתי iperf3. מבחינת מידת הקרבה למציאות, זה בערך כמו חיקוי העולם האמיתי במיינקראפט, אבל בינתיים זה יצליח.

המשתתפים בתחרות:

  • התייחס לערוץ הראשי
  • הגיבור של המאמר הזה הוא ipipou
  • OpenVPN עם אימות אך ללא הצפנה
  • OpenVPN הכל כלול
  • WireGuard ללא מפתח משותף מראש, עם 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"

תצורת המנהרה

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, הכל כצפוי)
מוגדר באמצעות openvpn-manage

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

ממצאים

שלט מכוער לח
עומס מעבד שרת אינו מעיד במיוחד, כי... ישנם שירותים רבים אחרים שפועלים שם, לפעמים הם אוכלים משאבים:

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

קנה אירוח אמין לאתרים עם הגנת DDoS, שרתי VPS VDS 🔥 קנה אחסון אתרים אמין עם הגנת DDoS, שרתי VPS VDS | ProHoster