ساعت دو بامداد است و SSH قطع شده. آخرین کاری که کردید این بود که یک قاعدهٔ DROP به زنجیرهٔ INPUT اضافه کردید و حالا نه پینگ جواب میدهد نه پورت ۲۲. اگر کنسول KVM یا حالت rescue نداشته باشید، این وضعیت یعنی بازنصب سرور. کل ماجرای «فایروال سرور بدون قطع دسترسی» یک نکتهٔ ساده است: هرگز قاعدهای را اعمال نکنید که راه برگشت را میبندد، مگر اینکه از قبل راه برگشت را باز کرده باشید.
قبل از هر قاعده، مسیر نجات را بسازید
ترتیب کار مهم است. اول مطمئن شوید از پنل سرور به کنسول یا حالت rescue دسترسی دارید. اگر ندارید، همین حالا درخواست فعالسازی بدهید. بدون این، هر اشتباه کوچکی در فایروال سرور به یک حادثهٔ چندساعته تبدیل میشود. اگر دسترسی را از دست دادید، مسیر بازیابی از حالت rescue و بازنصب سرور شروع میشود؛ اما بهتر است هرگز به آن نرسید.
قدم دوم، باز کردن یک نشست SSH دوم و نگهداشتن آن است. نشست اول را نبندید. قاعده را در نشست اول اعمال کنید و از نشست دوم تست کنید. اگر نشست دوم قطع شد، نشست اول هنوز زنده است و میتوانید قاعده را پاک کنید. این تکنیک ساده بیشتر از هر ابزار دیگری جلوی قطعی را گرفته است.
قاعدهٔ زماندار: تایمر نجات
قدم سوم، تایمر است. قبل از اعمال قاعده، یک اسکریپت بگذارید که اگر در ۵ دقیقهٔ آینده تأیید نکردید، فایروال را به حالت قبل برگرداند:
cat > /root/fw-rollback.sh <<'EOF'
#!/bin/bash
sleep 300
iptables -F
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
EOF
chmod +x /root/fw-rollback.sh
nohup /root/fw-rollback.sh &
حالا قاعده را اعمال کنید. اگر همهچیز درست بود، با kill %1 یا پیدا کردن PID اسکریپت آن را بکشید. اگر اشتباه کردید، ۵ دقیقه بعد فایروال خودش پاک میشود و دسترسی برمیگردد. این عدد را کمتر از ۳ دقیقه نگذارید؛ در سرورهای پرترافیک، اجرای iptables -F روی زنجیرههای بزرگ چند ثانیه طول میکشد و شما فرصت تست ندارید.
کدام پورتها را واقعاً باید باز بگذارید
پاسخ کوتاه: فقط پورتهایی که سرویس فعال رویشان گوش میدهد. با ss -tulpn لیست واقعی را بگیرید، نه لیست حدسی. خروجی معمول یک وبسرور تازه:
ss -tulpn | grep LISTEN
tcp LISTEN 0 128 0.0.0.0:22 users:(("sshd",pid=812))
tcp LISTEN 0 511 0.0.0.0:80 users:(("nginx",pid=1204))
tcp LISTEN 0 511 0.0.0.0:443 users:(("nginx",pid=1204))
tcp LISTEN 0 70 127.0.0.1:3306 users:(("mysqld",pid=1502))
دقت کنید که MySQL روی 127.0.0.1 گوش میدهد، نه 0.0.0.0. یعنی حتی اگر پورت ۳۳۰۶ را در فایروال باز بگذارید، از بیرون قابل دسترسی نیست. اما اگر یکبار در my.cnf مقدار bind-address را به 0.0.0.0 تغییر داده باشید، همان پورت باز شده به یک درِ پشتی تبدیل میشود. راهنمای امنیت MySQL و محدودکردن دسترسیها این حالت را کامل پوشش میدهد.
پورت SSH را جابهجا نکنید، مگر دلیل داشته باشید
تغییر پورت ۲۲ به چیزی مثل ۲۲۲۲ یک لایهٔ امنیتی واقعی نیست؛ فقط نویز لاگ را کم میکند. مهاجمی که اسکن کامل پورتها را اجرا میکند، پورت جدید را در چند ثانیه پیدا میکند. اگر این کار را میکنید، حتماً در /etc/ssh/sshd_config هم Port 2222 را اضافه کنید و سرویس را ریاستارت کنید، وگرنه بعد از ریبوت SSH بالا نمیآید. من در عمل پورت پیشفرض را نگه میدارم و بهجایش PasswordAuthentication no و PermitRootLogin prohibit-password را فعال میکنم. این دو خط بیشتر از تغییر پورت اثر دارند.
iptables یا ufw؟ انتخاب عملی
اگر تنها یک سرور دارید و قواعد سادهاند، ufw را انتخاب کنید. اگر روی Kubernetes، Docker با شبکهٔ bridge، یا NAT داخلی کار میکنید، مستقیم سراغ iptables یا nftables بروید. دلیلش این است که Docker قواعد خودش را در زنجیرهٔ DOCKER-USER مینویسد و ufw آنها را نمیبیند؛ نتیجه این میشود که پورت ۸۰۸۰ کانتینر شما از بیرون باز است، در حالی که ufw status میگوید مسدود است. اگر روی VPS داکر اجرا میکنید، راهاندازی داکر روی VPS را قبل از تنظیم فایروال بخوانید.
| معیار | ufw | iptables خام |
|---|---|---|
| سرعت راهاندازی | چند دقیقه | چند ساعت |
| خوانایی قواعد | بالا | پایین |
| سازگاری با Docker | ضعیف | کامل |
| ذخیرهسازی دائمی | خودکار | نیاز به iptables-persistent |
| عیبیابی | محدود | کامل با LOG |
یک نکتهٔ عملی دربارهٔ ufw: دستور ufw allow 22/tcp را قبل از ufw enable اجرا کنید. اگر enable را اول بزنید، ufw پیشفرض incoming را deny میکند و اگر SSH روی پورت غیراستاندارد باشد، همان لحظه قطع میشوید. خود ufw یک هشدار میدهد که «این ممکن است نشست فعلی را قطع کند» و اکثر افراد آن را نادیده میگیرند.
اینجا اشتباه میکنند
رایجترین اشتباهی که دیدهام این است: فرد قاعدهٔ iptables -A INPUT -j DROP را در انتهای زنجیره اضافه میکند، بعد سرور را ریبوت میکند و SSH بالا نمیآید. چرا؟ چون قواعد iptables بهصورت پیشفرض بعد از ریبوت پاک میشوند و سیاست پیشفرض زنجیره به ACCEPT برمیگردد؛ اما اگر با iptables-persistent ذخیره کرده باشید، همان قاعدهٔ DROP بعد از بوت دوباره اعمال میشود و این بار نشست نجات هم ندارید. علامتش این است که سرور پینگ میخورد (چون ICMP در قاعدهٔ شما استثنا شده) اما پورت ۲۲ تایماوت میشود. پینگ جواب میدهد و همین گمراهکننده است؛ خیلیها فکر میکنند مشکل از سرویس SSH است و systemctl restart sshd میزنند، در حالی که بسته هرگز به sshd نمیرسد.
اشتباه دوم: اعمال قاعده روی زنجیرهٔ OUTPUT. کسی که میخواهد سرور «فقط به بیرون وصل شود» سیاست OUTPUT را به DROP تغییر میدهد و بعد نمیفهمد چرا apt update کار نمیکند یا DNS حل نمیشود. اگر OUTPUT را محدود میکنید، حداقل اینها را باز بگذارید:
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT
خط ESTABLISHED,RELATED را در INPUT هم بگذارید، وگرنه پاسخ بستههای خروجی برنمیگردد و همهچیز نیمهکاره میماند. این خط را همیشه اولین قاعدهٔ زنجیره بگذارید؛ ترتیب قواعد در iptables معنادار است و اولین تطابق برنده میشود.
تست بدون ریسک: از کجا مطمئن شویم
از داخل سرور تست نکنید. curl localhost همیشه جواب میدهد چون از رابط loopback میگذرد و فایروال را دور میزند. از یک ماشین بیرونی با nmap تست کنید:
nmap -Pn -p 22,80,443,3306,8080 your-server-ip
خروجی open یعنی بسته رد میشود، filtered یعنی DROP شده، و closed یعنی فایروال اجازه داده اما سرویسی گوش نمیدهد. تفاوت filtered و closed را جدی بگیرید؛ خیلیها closed را با مشکل فایروال اشتباه میگیرند و ساعتها قاعده عوض میکنند.
برای سرویسهای وب، بعد از هر تغییر فایروال یک تست سرعت و دسترسی از بیرون بگیرید. اگر سایت روی سرور اختصاصی یا سرور اختصاصی میزبانی میشود و ترافیک بالاست، قواعد را در ساعات کمترافیک اعمال کنید؛ هر بار reload فایروال چند میلیثانیه وقفه در پردازش بستهها ایجاد میکند که در بار سنگین محسوس است. اگر لود سرور بعد از تغییرات بالا رفت، راهنمای تشخیص لود بالای سرور نقطهٔ شروع خوبی است.
ذخیرهسازی دائمی و بازبینی دورهای
قواعد را ذخیره کنید، وگرنه بعد از ریبوت همهچیز برمیگردد به حالت اول:
apt install iptables-persistent
netfilter-persistent save
iptables-save > /etc/iptables/rules.v4
هر سه ماه یکبار iptables -L -n -v --line-numbers بزنید و ببینید کدام قاعده شمارندهٔ صفر دارد. قاعدهای که در ۹۰ روز حتی یک بسته هم نگرفته، احتمالاً دیگر لازم نیست و فقط سطح حمله را بزرگ میکند. این بازبینی را جدی بگیرید؛ فایروالها معمولاً فقط اضافه میشوند و هیچوقت پاک نمیشوند.
اگر تازه در حال راهاندازی زیرساخت هستید و نمیخواهید از روز اول با iptables خام درگیر شوید، یک هاست لینوکس با دسترسی root کامل نقطهٔ شروع مناسبی است؛ همانجا میتوانید قواعد را با ufw بسازید و بعد به iptables مهاجرت کنید. برای انتقال فایلهای پیکربندی بین سرورها هم انتقال فایل با scp و rsync کار را سریع میکند.
پرسشهای پرتکرار
چرا بعد از فعالکردن فایروال SSH قطع شد ولی پینگ جواب میدهد؟
چون قاعدهٔ ICMP را باز گذاشتهاید اما پورت ۲۲ را نبستهاید. پینگ در لایهٔ ۳ کار میکند و ربطی به وضعیت پورت TCP ندارد. اول با کنسول وارد شوید و iptables -L INPUT -n --line-numbers را ببینید، سپس قاعدهٔ مسدودکنندهٔ پورت ۲۲ را با iptables -D INPUT <شماره> حذف کنید.
آیا تغییر پورت SSH امنیت را واقعاً بالا میبرد؟
نه بهاندازهای که تصور میشود. اسکن کامل پورتها چند ثانیه طول میکشد و پورت جدید پیدا میشود. اثر واقعیاش کاهش لاگهای تلاش ناموفق است. اگر میخواهید امنیت واقعی اضافه کنید، احراز هویت با کلید و غیرفعالکردن رمز عبور را انتخاب کنید.
چطور بفهمم کدام پورتها از بیرون باز هستند؟
از داخل سرور با ss -tulpn ببینید چه سرویسی روی چه آدرسی گوش میدهد، و از یک ماشین بیرونی با nmap -Pn -p- your-server-ip اسکن کنید. تفاوت این دو خروجی دقیقاً همان چیزی است که فایروال مسدود یا باز کرده است.
قواعد iptables بعد از ریبوت پاک میشوند؟
بله، مگر اینکه با netfilter-persistent save یا iptables-save در مسیر /etc/iptables/rules.v4 ذخیره کرده باشید. اگر از ufw استفاده میکنید، قواعد خودکار ذخیره میشوند و نیازی به کار اضافه نیست.