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. resolver 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 فقط PIDها را چاپ می‌کند که در اسکریپت‌ها به کار می‌آید. اگر لازم است پورت را آزاد کنید، به‌جای کشتن PID سرویس را متوقف کنید، وگرنه 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 به nmap گزینهٔ -sU را اضافه کنید و منتظر کندی باشید؛ روی سرور ما سه پورت رایج 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 روی Ubuntu از همان ابتدا روی localhost گوش می‌دهند و راهنماهای ما برای 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 پورت حالا به‌جای 8.9 ثانیه 110 ثانیه طول کشید. زمان بیشتر برای هر پورت برای اسکنر، اطلاعات کمتر دربارهٔ سرور شما. راهنمای 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 زیر ports: بنویسید "127.0.0.1:8081:80". پس از تغییر، 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 قرار دارند. هر پلن لینوکسی، از Nano با $3 به بالا، نسخه‌ای با آدرس IPv4 اختصاصی دارد و آنجا هرچه در ss می‌بینید، کل دنیا هم می‌بیند، جز آنچه فایروالتان جلویش را می‌گیرد.

پرسش‌های متداول

روی یک VPS تازه کدام پورت‌ها باز است؟

روی ایمیج Ubuntu 24.04 ما فقط یکی: SSH روی پورت 22. جز آن فقط resolver محلی 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 فوروارد‌شدهٔ شما جواب می‌دهد؛ بقیه عمداً در دسترس نیستند.

نظرات

هنوز نظری نیست. اولین نفر باشید.

یک نظر بگذارید

نظرات پیش از نمایش بررسی می‌شوند.