آموزش بررسی باز بودن پورت در لینوکس با ss و nmap
برای بررسی وضعیت پورتها از دستور ss استفاده کنید و سپس با nc یا nmap اتصال را تست کنید. تفاوت پورت مسدود شده با پورت بسته را درک کنید تا عیبیابی شبکه را سریعتر انجام دهید.
بررسی باز بودن پورت در لینوکس: ابتدا پرسش درست را انتخاب کنید
برای بررسی باز بودن یک پورت در لینوکس، ابتدا مشخص کنید که دقیقاً به دنبال چه هستید، زیرا مفهوم «باز بودن» بسته به موقعیت شما متفاوت است. روی خود سرور، باز بودن به این معناست که یک پردازش به آن پورت متصل شده و در حال گوش دادن است. از دید یک ماشین دیگر، باز بودن یعنی یک بسته (packet) به آن پردازش میرسد و پاسخی دریافت میکند. وقتی پاسخی دریافت نمیشود، پرسش اصلی این است که کدام دستگاه بسته را مسدود کرده است. sudo ss -ltnp به پرسش اول پاسخ میدهد. nc -z یا nmap به پرسش دوم پاسخ میدهند. شمارندههای فایروال و tcpdump به پرسش سوم پاسخ میدهند.
اجرای بررسی اشتباه همان چیزی است که باعث اتلاف وقت میشود. آزمایشی که روی خود سرور اجرا میشود، هرگز فایروال شبکه ارائهدهنده شما را درگیر نمیکند، زیرا آن فیلتر خارج از سرور قرار دارد. اگر شماره پورتها هنوز برای شما مبحث جدیدی است، نحوه عملکرد پورتها و سوکتها در لینوکس مدلی را پوشش میدهد که سایر بخشهای این راهنما بر اساس آن فرض شدهاند.
چه چیزی روی این سیستم در حال گوش دادن است؟ خروجی ss را بخوانید
ss به همراه iproute2 عرضه میشود، بنابراین در تمام توزیعهای فعلی موجود است. netstat از بسته net-tools میآید که اوبونتو سالهاست آن را بهصورت پیشفرض نصب نمیکند، بنابراین netstat -tulpn اغلب به netstat: command not found پاسخ میدهد. ss را یاد بگیرید و از ناامیدی دوری کنید.
sudo ss -ltnp-l فقط سوکتهای در حال گوش دادن را نشان میدهد. -t لیست را به TCP محدود میکند. -n بهجای حل کردن نامها، اعداد را چاپ میکند، بنابراین دستور بلافاصله برمیگردد. -p نام پردازش مالک را مشخص میکند و به دسترسی root نیاز دارد: بدون sudo، ستون Process برای هر پردازشی که مالک آن نیستید، خالی میماند. برای مشاهده UDP، -t را با -u جایگزین کنید.
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=612,fd=14))
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=921,fd=3))
LISTEN 0 511 127.0.0.1:8080 0.0.0.0:* users:(("node",pid=1442,fd=19))
LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=921,fd=4))ستون Local Address همه چیز را تعیین میکند و همان ستونی است که افراد بهسادگی از کنار آن میگذرند.
0.0.0.0:22به معنای تمام آدرسهای IPv4 روی دستگاه است، بنابراین اگر فایروال اجازه دهد، از بیرون قابل دسترسی است.[::]:22همین مفهوم را برای IPv6 دارد.127.0.0.1:8080فقط به معنای loopback است. هیچ چیزی خارج از این دستگاه نمیتواند به آن دسترسی داشته باشد.10.20.0.5:5432به معنای آن آدرس رابط خاص و نه هیچ آدرس دیگری است که در تنظیمات شبکه خصوصی رایج است.- خالی بودن ستون Process معمولاً به معنای فقدان
sudoاست، نه فقدان پردازش.
برای پرسوجو درباره یک پورت خاص، بهجای grep کردن کل لیست، داخل ss فیلتر کنید:
sudo ss -ltnp 'sport = :8080'
sudo lsof -nP -iTCP:8080 -sTCP:LISTEN
sudo fuser 8080/tcpخروجی خالی از هر سه دستور به این معناست که هیچ چیزی آن پورت را در اختیار ندارد. سرویس متوقف شده است، در شروع کار شکست خورده است یا در جای دیگری در حال گوش دادن است. پیش از آنکه حتی یک قانون فایروال را تغییر دهید، systemctl status <unit> و journalctl -u <unit> -n 50 را بخوانید.
چرا استفاده از 127.0.0.1 در Local Address باعث اتلاف وقت میشود
سوکتی که به 127.0.0.1 متصل (bind) شده است، از میزبان دیگری قابل دسترسی نیست و هیچ تغییر در قوانین فایروال این موضوع را اصلاح نمیکند. هسته سیستمعامل، ترافیک 127.0.0.0/8 را فقط به رابط loopback هدایت میکند و بستهای که با این آدرس مقصد به کارت شبکه واقعی برسد، به عنوان یک بسته نامعتبر (martian) دور ریخته میشود. بنابراین، فرآیند در حال اجراست، ss نشان میدهد که در حال گوش دادن است، ufw allow 8080 موفقیتآمیز بودن عملیات را گزارش میدهد، اما اتصال از لپتاپ شما همچنان با شکست مواجه میشود. این شکست با یک Connection refused فوری رخ میدهد، زیرا بسته به آدرس عمومی شما میرسد، سوکتی که به آن متصل باشد را پیدا نمیکند و هسته با یک TCP reset پاسخ میدهد.
بسیاری از برنامهها عمداً به loopback متصل میشوند و برای یک دیتابیس یا رابط مدیریتی، این تنظیم پیشفرض درستی است. شما دو انتخاب منطقی دارید. آدرس bind را در فایل پیکربندی خود برنامه تغییر دهید (listen_addresses در postgresql.conf، bind در redis.conf، یا آرگومان host که برنامه شما میپذیرد) و سپس فایروال را باز کنید. یا آن را روی loopback باقی بگذارید و از طریق روش دیگری به آن دسترسی پیدا کنید، مانند یک reverse proxy در nginx، یا یک SSH tunnel از لپتاپ خود:
ssh -L 8080:127.0.0.1:8080 user@203.0.113.10Docker نیز همین تمایز را در فلگ publish خود دارد. -p 8080:8080 به 0.0.0.0 متصل میشود و کانتینر را در معرض اینترنت قرار میدهد. -p 127.0.0.1:8080:8080 به loopback متصل میشود و آن را محلی نگه میدارد.
نحوه بررسی باز بودن یک پورت در لینوکس از یک ماشین دیگر
این تست را از یک شبکه متفاوت انجام دهید. تست از داخل خود سرور فقط ثابت میکند که مسیر loopback کار میکند. حتی اتصال به IP عمومی خودتان از داخل سرور نیز فایروال شبکه ارائهدهنده را دور میزند، زیرا آن فیلتر خارج از VPS اجرا میشود.
nc -zv -w 3 203.0.113.10 443-z متصل شده و بدون ارسال داده، اتصال را میبندد. -w 3 پس از سه ثانیه تلاش را متوقف میکند و این flag اهمیت دارد: بدون timeout، یک بسته (packet) رها شده باعث میشود کلاینت بیش از دو دقیقه برای SYN تلاش کند تا زمانی که kernel متوقف شود. موفقیت به این شکل دیده میشود:
Connection to 203.0.113.10 443 port [tcp/https] succeeded!اگر ابزار مورد نظر موجود نیست (nc: command not found)، netcat-openbsd را روی Debian یا Ubuntu نصب کنید، یا از قابلیت تغییر مسیر شبکه (network redirection) داخلی bash استفاده کنید که به هیچ بستهای نیاز ندارد:
timeout 3 bash -c '</dev/tcp/203.0.113.10/443' && echo open || echo "no answer"این syntax یک قابلیت bash است، بنابراین آن را با bash اجرا کنید. /bin/sh در Debian و Ubuntu همان dash است که /dev/tcp ندارد و گزارش میدهد که مسیر وجود ندارد. برای بازهای از پورتها، یا زمانی که میخواهید وضعیت برای شما نامگذاری شود، از nmap روی میزبانهایی که مسئولیت آنها را دارید استفاده کنید:
sudo nmap -Pn -p 22,80,443 203.0.113.10
sudo nmap -Pn -p 1-1024 203.0.113.10-Pn از کشف میزبان (host discovery) صرفنظر میکند. اکثر میزبانهای VPS بستههای ICMP echo را مسدود میکنند، بنابراین بدون -Pn، ابزار nmap تشخیص میدهد که میزبان خاموش است و چیزی را اسکن نمیکند. nmap زمانی که چیزی پاسخ داده و اتصال را پذیرفته باشد open، زمانی که چیزی با reset پاسخ داده باشد closed، و زمانی که هیچ پاسخی دریافت نشده باشد filtered را چاپ میکند. برای یک سرویس وب، curl -sS -o /dev/null -w '%{http_code}\n' https://example.com یک خطای شبکه را از خطای برنامه جدا میکند، زیرا یک کد وضعیت (status code) ثابت میکند که کل مسیر به درستی کار کرده است.
چرا پورت مسدودشده باعث وقفه میشود و پورت بسته بلافاصله پاسخ رد میدهد
پاسخ رد فوری. بسته به ماشین رسیده و چیزی به آن پاسخ داده است.
nc: connect to 203.0.113.10 port 443 (tcp) failed: Connection refusedدو علت متفاوت دقیقاً همین نتیجه را ایجاد میکنند. یا هیچ پردازشی به آن آدرس و پورت متصل (bind) نیست و در نتیجه هسته سیستمعامل با یک TCP reset پاسخ داده است، یا یک قانون فایروال بسته را با یک reset یا پیام ICMP port unreachable رد کرده است. پاسخ رد، یک پاسخ قطعی است که در یک رفتوبرگشت (round trip) دریافت میشود.
وقفه، سپس اتمام زمان (timeout). چیزی بسته را دور انداخته و هیچ پاسخی نداده است.
nc: connect to 203.0.113.10 port 443 (tcp) timed out: Operation now in progressاین همان کاری است که قانون DROP انجام میدهد و فایروال ارائهدهنده خدمات یا گروه امنیتی ابری (cloud security group) نیز همین رفتار را دارد. سکوت، نشانه یک drop است، زیرا فرستنده نمیتواند تفاوت بین یک drop و یک میزبان ازدسترفته (dead host) را تشخیص دهد.
این نشانه به شما میگوید که در مرحله بعد کجا را بررسی کنید. «ردشده» (Refused) یعنی بستهها بهدرستی در شبکه جابهجا میشوند؛ پس به ss -ltnp برگردید و آدرس bind و شماره پورت را بررسی کنید. «اتمام زمان» (Timed out) یعنی بستهها در حال دور ریخته شدن هستند؛ پس فایروالها را از بیرون به داخل بررسی کنید. تفاوت refused و timed out در SSH همین تفکیک را برای پورت 22 انجام میدهد، جایی که اکثر کاربران با آن مواجه میشوند.
ابزار ufw عمداً هر دو رفتار را ارائه میدهد: ufw deny 8080 بستهها را drop میکند و ufw reject 8080 یک پاسخ رد (rejection) میفرستد. در nftables این دو هدف drop و reject هستند و در iptables این دو -j DROP و -j REJECT نام دارند. سیاستهای پیشفرض تقریباً همیشه drop هستند، به همین دلیل است که یک قانون مفقود باعث ایجاد وقفه میشود و نه یک پیام خطا.
چه چیزی پورت را مسدود کرده است؟ از بیرون به سمت داخل بررسی کنید
sudo ufw status verbose
sudo iptables -L INPUT -n -v --line-numbers
sudo nft list rulesetشمارندههای -v بخش مفید ماجرا هستند. تست nc خود را از بیرون اجرا کنید، دستور iptables را دوباره فراخوانی کنید و به دنبال شمارندهای بگردید که تغییر کرده است: قانونی که تعداد بستههای (packet) آن افزایش مییابد، همان قانونی است که ترافیک شما را مدیریت میکند. این کار حدس و گمان را با شواهد جایگزین میکند.
تست نهایی روی سرور اجرا میشود و در حالی که شما از بیرون متصل میشوید، شبکه را زیر نظر میگیرد:
sudo tcpdump -ni any tcp port 8080رسیدن یک SYN بدون بازگشت SYN-ACK به این معنی است که بسته به VPS شما رسیده اما میزبان آن را دور انداخته است؛ بنابراین فایروال ارائهدهنده مشکلی ندارد و قوانین محلی شما ایراد دارند. اگر هیچ خروجیای مشاهده نمیکنید، یعنی بسته هرگز نرسیده است؛ پس مشکل از فایروال ارائهدهنده، گروه امنیتی (security group) یا اشتباه بودن آدرس IP است. همین یک تفاوت، بخش بزرگی از عیبیابی را حذف میکند.
دو لایه وجود دارند که نتایجی غیرممکن به نظر میرسانند. اول، IPv6: اگر نام دامنه دارای رکورد AAAA باشد، کلاینت شما ممکن است از طریق IPv6 متصل شود در حالی که قانون شما فقط IPv4 را پوشش میدهد؛ بنابراین پیش از اعتماد به هر نتیجه، هر خانواده را با nc -4 و nc -6 تست کنید. قوانین ufw و پورتهای IPv6 روی VPS این عدم تطابق را پوشش میدهد. دوم، Docker: پورت منتشرشدهٔ یک container حتی زمانی که ufw status آن پورت را مسدود نشان میدهد، از اینترنت پاسخ میدهد؛ زیرا این بستهها پیش از آنکه به زنجیره ufw برسند، پردازش میشوند. چرا Docker پورتها را بدون توجه به ufw منتشر میکند مکانیزم و راهحل آن را توضیح میدهد و قوانین ufw برای تنظیم روی یک VPS جدید مجموعه پایه و ضروری برای شروع است.
چرا پاسخهای UDP ذاتاً مبهم هستند
پروتکل UDP فاقد handshake است، بنابراین یک probe هیچ مرحلهای برای موفقیت ندارد. دستور nc -zu 203.0.113.10 53 به محض ارسال بسته، با کد خروج 0 خاتمه مییابد؛ این فقط ثابت میکند که ماشین شما بسته را ارسال کرده است و هیچ اطلاعاتی درباره مقصد ارائه نمیدهد. هنگامی که یک پورت UDP بسته باشد، میزبان معمولاً با یک پیام ICMP port unreachable پاسخ میدهد، اما هسته سیستمعامل این خطا را تنها در write بعدی به یک socket متصل گزارش میکند، بنابراین یک probe تکبسته آن را نادیده میگیرد. فایروالها معمولاً پیامهای ICMP را حذف میکنند که همین سرنخ اندک را نیز از بین میبرد. به همین دلیل است که nmap برای اکثر پورتهای UDP وضعیت open|filtered را چاپ میکند: عدم دریافت پاسخ، دقیقاً همان نتیجهای است که هم یک سرویس بازِ ساکت و هم یک پورت فیلترشده ایجاد میکنند.
برای تست UDP، از طریق پروتکلی که مد نظر دارید با سرویس صحبت کنید. یک سرور DNS به dig +short @203.0.113.10 example.com با یک آدرس یا هیچ پاسخی میدهد. یک peer در WireGuard یک خط latest handshake اخیر را در sudo wg show نشان میدهد. سپس رسیدن بستهها را در سمت سرور اثبات کنید:
sudo tcpdump -ni any udp port 51820ظاهر شدن بستهها همزمان با ارسال توسط کلاینت، به این معنی است که بستهها میرسند و مشکل از سرویس یا زنجیره input است. عدم مشاهده هیچ بستهای به این معنی است که آنها هرگز به مقصد نرسیدهاند.
چکلیستی برای یافتن سریعتر خطا
- روی سرور، دستور
sudo ss -ltnp 'sport = :8080'را اجرا کنید. عدم نمایش خروجی به این معناست که هیچ سرویسی در حال گوش دادن نیست؛ پس ابتدا سرویس را اصلاح کنید. - در صورت وجود خروجی، ستون Local Address را بررسی کنید.
127.0.0.1به این معناست که دسترسی از خارج غیرممکن است، مگر اینکه آن را مجدداً Bind کنید یا یک پروکسی در مقابل آن قرار دهید. - از یک شبکه دیگر، دستور
nc -zv -w 3 <public ip> 8080را اجرا کنید. - خطای Connection refused شما را به مرحله 1 بازمیگرداند. آدرس، پورت یا ماشین مورد نظر، آن چیزی نیست که تصور میکنید.
- خطای Timeout به معنای Drop شدن بسته است. دستور
sudo tcpdump -ni any tcp port 8080را روی سرور اجرا کرده و تست را تکرار کنید. - اگر SYN دریافت شد اما پاسخی ارسال نشد، مشکل از فایروال میزبان است. قانونی که شمارنده آن در
sudo iptables -L INPUT -n -vتغییر میکند را پیدا کنید. - اگر هیچ SYN دریافت نشد، مشکل از فایروال ارائهدهنده خدمات (Provider)، گروه امنیتی (Security Group) یا اشتباه بودن آدرس IP است.
FAQ
چگونه پورتهای باز روی سرور لینوکسی خود را بررسی کنم؟
برای TCP از sudo ss -ltnp و برای UDP از sudo ss -lunp استفاده کنید. هر خط نشاندهنده یک سوکت در حال گوش دادن (listening) است و ستون Local Address مشخص میکند چه کسی میتواند به آن دسترسی داشته باشد: 0.0.0.0 و [::] درخواستها را از هر جایی که فایروال اجازه دهد میپذیرند، در حالی که 127.0.0.1 فقط اتصالات از خودِ ماشین را قبول میکند. ستون Process برای نمایش اطلاعات به دسترسی root نیاز دارد، بنابراین آن را با sudo اجرا کنید، در غیر این صورت خالی میماند. ss بخشی از iproute2 است و همیشه نصب است؛ netstat بخشی از net-tools است و معمولاً نصب نیست.
چرا ss نشان میدهد سرویس من در حال گوش دادن است اما همچنان نمیتوانم متصل شوم؟
دو دلیل رایج برای این موضوع وجود دارد و یک دستور میتواند آنها را از هم تفکیک کند. اگر Local Address برابر با 127.0.0.1 باشد، سرویس به loopback متصل شده و از هیچ میزبان دیگری قابل دسترسی نیست، زیرا هسته سیستمعامل فقط این محدوده را به اینترفیس loopback مسیریابی میکند. اگر آدرس 0.0.0.0 است و اتصالات همچنان ناموفق هستند، sudo tcpdump -ni any tcp port <port> را روی سرور اجرا کنید و از بیرون متصل شوید. اگر یک بسته SYN برسد و پاسخی دریافت نشود، یعنی یک قانون فایروال محلی آن را مسدود میکند. اگر هیچ بستهای نرسد، یعنی بسته قبل از رسیدن به VPS شما متوقف شده است که معمولاً به دلیل فایروال ارائهدهنده یا security group است.
تفاوت بین اتصال رد شده (refused) و اتصال زماندار (timed out) چیست؟
رد شدن یک پاسخ است. بسته به میزبان رسیده و یک TCP reset یا ICMP port unreachable دریافت کرده است؛ این یعنی هیچ چیزی روی آن آدرس و پورت گوش نمیدهد یا یک قانون آن را رد کرده است. زماندار شدن (timeout) به معنای سکوت است: یک قانون بسته را دور انداخته و هیچ پاسخی نفرستاده است، بنابراین کلاینت شما تا زمان تسلیم شدن تلاش میکند. رد شدن شما را به سمت سرویس و آدرس bind آن هدایت میکند. زماندار شدن شما را به سمت فایروال هدایت میکند و فایروالی که به بیرون نزدیکتر است، اولین جایی است که باید بررسی شود.
چگونه بررسی کنم که آیا یک پورت UDP باز است؟
شما نمیتوانید پاسخ قطعی از یک کاوشگر (probe) عمومی دریافت کنید، زیرا UDP فاقد handshake است و یک سرویس ساکت، مشابه یک بسته دور انداخته شده به نظر میرسد. nc -zu به محض ارسال بسته، موفقیت را گزارش میدهد و nmap نیز به همین دلیل open|filtered را گزارش میکند. برای تست، از پروتکل مربوطه استفاده کنید: dig +short @<host> example.com برای DNS، یا sudo wg show برای یک peer در WireGuard که handshake اخیر داشته است. برای اثبات رسیدن بستهها، در حالی که کلاینت در حال ارسال است، sudo tcpdump -ni any udp port <port> را روی سرور اجرا کنید.
آیا هنوز میتوانم از telnet host port برای تست پورت استفاده کنم؟
این روش برای TCP کار میکند و Escape character is '^]' به معنای پذیرفته شدن اتصال است. با Ctrl+] و سپس quit از آن خارج شوید. دو دلیل باعث میشود nc -z ابزار بهتری باشد: telnet روی اکثر ایمیجهای سرور امروزی نصب نیست و nc با استفاده از -w زمانبندی (timeout) را مدیریت میکند و یک exit status برمیگرداند که میتوانید در اسکریپتها آن را تست کنید. وقتی هیچکدام در دسترس نیستند، timeout 3 bash -c '</dev/tcp/<host>/<port>' به هیچ بستهای نیاز ندارد.