EQVPS
לוח בקרההתחלה

איך בודקים פורטים פתוחים ב-VPS: מה מאזין, מה האינטרנט רואה וההפתעה של Docker

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

פורט שנשאר פתוח ונשכח הוא הדרך הקלאסית להיכנס לשרת: מסד נתונים על כתובת ציבורית, לוח דיבאג, קונטיינר שפרסם פורט שאף אחד לא רצה לפרסם. הבדיקה לוקחת דקה כשמכירים שתי פקודות ומלכודת אחת. ביצענו את כל מה שלמטה על שרת Ubuntu 24.04 חדש מהאימג' שלנו וסרקנו אותו ממכונה שנייה, כך שהפלטים הם מה שתראו על השרת שלכם.

1. מה מאזין

הפקודה היא ss. האותיות פירושן TCP, UDP, שקעים מאזינים, מספרי פורטים ושם התהליך:

sudo ss -tulpn

בשרת חדש היא מדפיסה שש שורות:

Netid State  Local Address:Port  Process
udp   UNCONN    127.0.0.54:53    systemd-resolve
udp   UNCONN 127.0.0.53%lo:53    systemd-resolve
tcp   LISTEN    127.0.0.54:53    systemd-resolve
tcp   LISTEN 127.0.0.53%lo:53    systemd-resolve
tcp   LISTEN       0.0.0.0:22    sshd
tcp   LISTEN          [::]:22    sshd

העמודה החשובה היא Local Address. היא אומרת מי יכול להתחבר:

כתובתמי מגיע אליה
127.0.0.1:… או 127.0.0.53רק תוכנות בשרת הזה
0.0.0.0:…כל אחד דרך IPv4, אלא אם חומת האש חוסמת
[::]:…כל אחד דרך IPv6 (ובדרך כלל גם IPv4)
10.0.0.5:… (כתובת מסוימת)כל מי שמגיע לכתובת הזאת

כלומר השרת החדש חושף כלפי חוץ דבר אחד בלבד: SSH בפורט 22, דרך IPv4 ו-IPv6. פותר ה-DNS על 127.0.0.53 פרטי.

כדי להציג רק את מה שהאינטרנט היה יכול לראות, סננו את כתובות ה-loopback:

sudo ss -tlnp | awk 'NR>1 && $4 !~ /^(127\.|\[::1\])/ {print $4, $6}'

הריצו את זה אחרי כל התקנה. התקנו nginx, Docker ואפליקציית Python קטנה, והרשימה גדלה מ-2 שורות ל-10, ביניהן docker-proxy על 0.0.0.0:8081 ושקע של containerd על פורט localhost אקראי.

2. איזה תהליך תופס פורט

כשפורט תפוס ורוצים לדעת מי תופס אותו:

sudo lsof -i :80 -P -n
sudo fuser 80/tcp
sudo ss -tlnp 'sport = :80'

הכי קריא הוא lsof. עבור nginx הוא הראה תהליך ראשי כ-root ו-worker כ-www-data, שניהם על *:80, ל-IPv4 ול-IPv6. fuser מדפיס רק את מזהי התהליכים, שימושי בסקריפטים. אם צריך לשחרר את הפורט, עצרו את השירות במקום להרוג את התהליך, אחרת systemd יפעיל אותו מחדש: sudo systemctl stop nginx.

3. מה האינטרנט רואה

ss מראה את נקודת המבט של השרת. נקודת המבט של האינטרנט יכולה להיות שונה, כי באמצע יש חומת אש או כי שירות קשור ל-127.0.0.1. כדי לראות את התמונה האמיתית, סרקו את השרת ממכונה אחרת, מהמחשב הנייד או משרת שני. הכלי הוא nmap (apt install nmap, או המתקין מ-nmap.org ב-Windows וב-macOS).

nmap your-server-ip
nmap -p- your-server-ip
nmap -p 22,80,443,3306 your-server-ip

הצורה הראשונה בודקת את 1,000 הפורטים הנפוצים, השנייה את כל 65,535, השלישית רשימה. בשרת החדש שלנו, לפני כל חומת אש:

Not shown: 999 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
Nmap done: 1 IP address (1 host up) scanned in 6.83 seconds

הסריקה המלאה של 65,535 פורטים לקחה 8.9 שניות ומצאה את אותו פורט יחיד. פורטים סגורים עונים מיד ב-reset, ולכן סריקתם מהירה.

שלושה מצבים חשובים:

  • open: תוכנה קיבלה את החיבור. זה משטח התקיפה שלכם.
  • closed: השרת ענה שאין שם שום דבר שמאזין. בטוח, אבל מאשר שהשרת קיים ומה הוא לא מריץ.
  • filtered: אין תשובה. חומת אש זרקה את החבילה. הסורק לא יכול לדעת אם יש שירות מאחוריה.

לפורט בודד בלי להתקין כלום, nc -zv כתובת-השרת 443 מחזיר succeeded או refused, ובפורט מסונן נתקע עד תום הזמן. עבור UDP הוסיפו -sU ל-nmap והתכוננו להמתנה; בשרת שלנו שלושת פורטי ה-UDP הנפוצים, 53, 123 ו-161, היו closed.

הערה לחבילות NAT: מהאינטרנט מגיע רק פורט ה-SSH שהעברנו אליכם. סריקה של הכתובת המשותפת מראה את קצה הרשת שלנו, לא את השרת שלכם, ושאר הפורטים לא נגישים, לא משנה מה מאזין עליהם. בחבילה עם כתובת IPv4 משלה, כל פורט פתוח בניהולכם.

4. סגרו את מה שלא צריך

שלוש דרכים, מהטובה ביותר לרחבה ביותר.

קשרו ל-localhost. אם רק תוכנות בשרת משתמשות בשירות, תנו לו להאזין על 127.0.0.1. הפעלנו אפליקציית ווב קטנה עם --bind 127.0.0.1 8080: ss הראה אותה על 127.0.0.1:8080, ו-nmap מבחוץ דיווח 8080/tcp closed. למסדי נתונים זו ברירת המחדל הנכונה: Redis, MySQL ו-PostgreSQL מאזינים על localhost כבר מההתקנה ב-Ubuntu, והמדריכים שלנו ל-Redis ול-MySQL משאירים את זה כך.

עצרו את השירות. sudo systemctl disable --now שם מסיר את המאזין לתמיד. בדקו עם ss שהוא נעלם.

הפעילו את חומת האש. אשרו קודם SSH, אחר כך את מה שאתם מגישים, ואז הפעילו:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw enable
sudo ufw status verbose

הסריקה מבחוץ השתנתה לגמרי:

Not shown: 997 filtered tcp ports (no-response)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8081/tcp open  blackice-icecap
Nmap done: 1 IP address (1 host up) scanned in 10.18 seconds

כל פורט שלא אושר עבר מ-closed ל-filtered, והסריקה המלאה של 65,535 פורטים לקחה עכשיו 110 שניות במקום 8.9. יותר זמן לכל פורט עבור הסורק, פחות מידע על השרת שלכם. מדריך ה-UFW שלנו עוסק בהגבלת קצב, באישור כתובת IP אחת וב-IPv6.

אבל הסתכלו על השורה השלישית.

5. ההפתעה של Docker

פורט 8081 נשאר פתוח כשחומת האש פעילה ובלי כלל שמתיר אותו. הפורט שייך לקונטיינר שהפעלנו עם docker run -p 8081:80 nginx:alpine. Docker מוסיף כללים משלו לחומת האש של הליבה, והם נבדקים לפני הכללים של ufw. הכלל שהוא כתב בשרת שלנו:

-A DOCKER ! -i docker0 -p tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80

כל חבילה לפורט 8081 מועברת לקונטיינר לפני ש-ufw מקבל זכות דיבור. כך עושה כל קונטיינר עם -p או רשומת ports:, ו-ss מציג את זה ביושר כ-docker-proxy על 0.0.0.0:8081.

התיקון הוא לפרסם רק על localhost:

docker rm -f web
docker run -d --name web -p 127.0.0.1:8081:80 nginx:alpine

ב-Compose כתבו "127.0.0.1:8081:80" תחת ports:. אחרי השינוי ss הראה 127.0.0.1:8081, כלל ה-Docker קיבל -d 127.0.0.1/32, והסריקה מבחוץ דיווחה 8081/tcp filtered. שימו nginx כ-reverse proxy מלפנים עבור הצד הציבורי, עם HTTPS, כך שהפורטים הפתוחים היחידים בשרת יהיו 22, 80 ו-443.

רשימת בדיקה אחרי כל שינוי

  1. sudo ss -tulpn: כל שורה עם 0.0.0.0 או [::] היא משהו שרציתם לחשוף?
  2. nmap כתובת-השרת מהמחשב הנייד: הפורטים הפתוחים תואמים?
  3. מסד נתונים, מטמון או לוח ניהול על כתובת ציבורית: העבירו היום ל-127.0.0.1 או מאחורי חומת האש.
  4. כל רשומת ports: ב-Docker: מתחילה ב-127.0.0.1: אם זה לא פורט הווב הציבורי?
  5. SSH פתוח לכולם: הוסיפו fail2ban או כניסה עם מפתח בלבד.

ב-Windows Server אותה בדיקה היא Get-NetTCPConnection -State Listen, והכללים נמצאים בחומת האש של Windows. לכל חבילת Linux, החל מ-Nano ב-$3, יש גרסה עם כתובת IPv4 משלה, ושם מה שאתם רואים ב-ss רואה כל העולם, חוץ ממה שחומת האש שלכם עוצרת.

שאלות נפוצות

אילו פורטים פתוחים ב-VPS חדש?

באימג' Ubuntu 24.04 שלנו, אחד: SSH בפורט 22. מלבדו מאזין רק פותר ה-DNS המקומי על 127.0.0.53 ו-127.0.0.54, שהאינטרנט לא מגיע אליו. סריקה של כל 65,535 פורטי ה-TCP ממכונה אחרת מצאה בדיוק פורט פתוח אחד.

מה ההבדל בין פורט closed לפורט filtered?

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

למה ss מראה פורט ש-nmap אומר שהוא closed?

השירות מאזין רק על 127.0.0.1, כתובת ה-loopback. תוכנות על השרת עצמו יכולות להתחבר, מבחוץ אף אחד. ב-ss הסתכלו על העמודה Local Address: 127.0.0.1:8080 פרטי, ו-0.0.0.0:8080 או [::]:8080 נגישים מהאינטרנט אם חומת האש מתירה.

הפעלתי ufw, למה הפורט של Docker עדיין פתוח?

Docker כותב כללי חומת אש משלו, והם נבדקים לפני הכללים של ufw. קונטיינר שהופעל עם -p 8081:80 היה נגיש מבחוץ בשרת שלנו כש-ufw פעיל ובלי כלל עבור 8081. פרסמו במקום זאת על localhost, -p 127.0.0.1:8081:80, ושימו nginx או Caddy לפניו עבור הצד הציבורי.

איך בודקים פורט מהמחשב שלי?

הריצו nc -zv כתובת-השרת 443 לפורט אחד, או התקינו nmap והריצו nmap כתובת-השרת לאלף הפורטים הנפוצים. בחבילת NAT רק פורט ה-SSH שהועבר אליכם עונה מהאינטרנט; כל השאר לא נגיש בכוונה.

תגובות

אין עדיין תגובות. היו הראשונים.

השאירו תגובה

התגובות עוברות מודרציה לפני שהן מופיעות.