iptables چیست و چگونه کار می‌کند؟ راهنمای قوانین و باز کردن پورت در فایروال لینوکس

iptables ابزار خط فرمان لینوکس برای پیکربندی فایروال بومی کرنل (netfilter) است؛ با آن مشخص می‌کنید کدام بسته‌های شبکه اجازه ورود به سرور، خروج از آن یا عبور از آن را دارند. سازوکارش ساده است: هر بسته از یک فهرست مرتب از قوانین عبور داده می‌شود و اولین قانونی که با آن مطابقت پیدا کند، سرنوشت بسته را تعیین می‌کند.

دقت کنید که iptables خودش فایروال نیست؛ فایروال داخل کرنل و در چارچوب netfilter است و iptables فقط رابط پیکربندی آن. به همین دلیل قوانین در حافظه کرنل می‌مانند و اگر ذخیره نشوند با اولین ریبوت می‌پرند. پیش از نوشتن هر قانونی هم بد نیست ببینید همین حالا کدام فایروال روی سرور فعال است؛ بررسی وضعیت فایروال در سرور مجازی لینوکس دستورهای این بررسی را کنار هم گذاشته است.

جدول، زنجیره و قانون

ساختار iptables سه لایه دارد. بالاترین سطح جدول‌ها (tables) است: filter برای اجازه دادن یا ندادن به ترافیک (پیش‌فرض)، nat برای تغییر آدرس مبدأ یا مقصد و پورت‌فوروارد، و mangle و raw برای کارهای تخصصی‌تر.

یک بسته از کدام زنجیره‌ها عبور می‌کند؟بسته ورودی⁦PREROUTING⁩برای این سرور است؟تصمیم⁦INPUT⁩بله: به سرویس محلیسرویس محلی⁦OUTPUT⁩⁦FORWARD⁩خیر: عبور از سرور⁦POSTROUTING⁩خروج از سرورداخل هر زنجیره، قوانین به ترتیب بررسی می‌شوند و اولینقانونِ منطبق تصمیم می‌گیرد؛ بنابراین ترتیب قوانین اهمیت داردو اگر هیچ قانونی منطبق نشد، سیاست پیش‌فرض زنجیره اعمالمی‌شود

بسته‌ای که مقصدش خود سرور است از زنجیره INPUT می‌گذرد و بسته‌ای که فقط از سرور عبور می‌کند وارد FORWARD می‌شود؛ دانستن همین مسیر، جای درست هر قانون را مشخص می‌کند.

هر جدول از چند زنجیره (chain) تشکیل شده و هر زنجیره نقطه‌ای از مسیر عبور بسته است. سه زنجیره اصلی جدول filter:

  • INPUT — بسته‌هایی که مقصدشان خودِ این سرور است؛ مثل اتصال SSH یا درخواست HTTP به وب‌سرور شما.
  • OUTPUT — بسته‌هایی که خودِ سرور تولید می‌کند و به بیرون می‌فرستد.
  • FORWARD — بسته‌هایی که فقط از سرور عبور می‌کنند؛ وقتی اهمیت دارد که سرور نقش روتر یا میزبان ماشین‌های مجازی و کانتینر را داشته باشد. روی یک سرور مجازی معمولی که فقط وب‌سایت سرویس می‌دهد، معمولاً خالی می‌ماند.

پایین‌ترین لایه قانون (rule) است: یک شرط تطبیق (پروتکل، پورت، آدرس IP، اینترفیس، وضعیت اتصال) به همراه یک هدف (target) که سرنوشت بسته را تعیین می‌کند.

هدف‌ها: ACCEPT، DROP و REJECT

ACCEPT یعنی بسته عبور کند. DROP یعنی بسته بی‌صدا دور انداخته شود؛ فرستنده هیچ پاسخی نمی‌گیرد و تا timeout منتظر می‌ماند. REJECT هم بسته را دور می‌اندازد اما پیام خطای ICMP برای فرستنده می‌فرستد.

قاعده رایج این است که برای ترافیک اینترنت DROP و برای شبکه داخلی REJECT بگذارید تا کاربران داخلی به‌جای تأخیر طولانی، خطای فوری بگیرند. هدف LOG هم بسته را ثبت می‌کند و اجازه می‌دهد به قانون بعدی برود؛ برای عیب‌یابی مفید است.

سیاست پیش‌فرض (Policy) و اهمیت ترتیب

هر زنجیره یک سیاست پیش‌فرض دارد که وقتی هیچ قانونی با بسته مطابقت نکند اعمال می‌شود. دو رویکرد وجود دارد: سیاست ACCEPT و بستن موارد خاص، یا سیاست DROP و باز کردن فقط چیزی که لازم است. دومی امن‌تر است، چون سرویسی که فراموشش کرده‌اید بسته می‌ماند نه باز.

ترتیب هم تعیین‌کننده است. iptables زنجیره را از بالا به پایین می‌خواند و با اولین تطبیق کار تمام است؛ پس یک DROP عمومی در ردیف اول، همه قوانین ACCEPT بعدی را بی‌اثر می‌کند. سوئیچ -A قانون را به انتهای زنجیره اضافه و -I آن را در ابتدا (یا شماره دلخواه) درج می‌کند. برای دیدن وضعیت فعلی با شماره ردیف و شمارنده بسته‌ها:

iptables -L -n -v --line-numbers

فایروال حالت‌مند و ماژول conntrack

iptables یک فایروال حالت‌مند (stateful) است؛ کرنل جدولی از اتصال‌های در جریان نگه می‌دارد و می‌داند هر بسته به کدام اتصال تعلق دارد. وضعیت‌های اصلی: NEW (شروع اتصال تازه)، ESTABLISHED (بخشی از اتصالی که قبلاً برقرار شده)، RELATED (اتصال وابسته، مثل کانال داده FTP) و INVALID.

فایده عملی‌اش این است که با یک قانون در ابتدای INPUT، پاسخ ترافیکی که خودِ سرور آغاز کرده — مثل به‌روزرسانی بسته‌ها یا اتصال به دیتابیس بیرونی — عبور می‌کند، بدون آنکه پورت اضافه‌ای باز بماند.

یک مجموعه قانون پایه

نمونه زیر اسکلتی متداول برای یک سرور اختصاصی یا مجازی با وب‌سرور و SSH است. پیش از اجرا حتماً بخش «مراقب باشید خودتان را از SSH بیرون نیندازید» را بخوانید:

iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

اگر می‌خواهید SSH فقط از یک محدوده IP مشخص در دسترس باشد، به‌جای قانون باز پورت ۲۲ از این شکل استفاده کنید:

iptables -I INPUT 1 -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT

مقدار 192.168.1.0/24 فقط یک نمونه است و باید با IP یا رنج ثابت خودتان جایگزین شود؛ اگر رنج را اشتباه بزنید در همان لحظه دسترسی SSH را از دست می‌دهید. ضمناً «به‌جای» را جدی بگیرید: تا وقتی قانون باز پورت ۲۲ در فهرست باقی است، این قانون محدودکننده چیزی را نمی‌بندد و پورت روی کل اینترنت باز می‌ماند. پیش از بستن نشست فعلی، اتصال را با یک نشست SSH دوم امتحان کنید.

این قوانین فقط IPv4 را پوشش می‌دهند. اگر سرور IPv6 دارد باید مجموعه معادل را با ip6tables هم بنویسید، وگرنه سرویس‌ها از مسیر IPv6 باز می‌مانند.

باز کردن پورت یک سرویس تازه‌نصب

متداول‌ترین کاری که مدیر یک سرور با iptables انجام می‌دهد همین است: سرویسی نصب شده — کنترل‌پنل هاست، دیتابیس، پنل مانیتورینگ — و از بیرون در دسترس نیست، چون پورتش در فایروال اجازه ورود ندارد.

یک تصور قدیمی و نادقیق را همین‌جا اصلاح کنیم: این‌طور نیست که «لینوکس به‌صورت پیش‌فرض بیشتر پورت‌ها را بسته نگه می‌دارد». روی یک نصب مینیمال، سیاست زنجیره‌ها معمولاً ACCEPT و فهرست قوانین خالی است، یعنی از دید netfilter همه چیز باز است. آنچه در عمل پورت‌ها را می‌بندد، فایروال پیش‌فرض توزیع (firewalld در خانواده RHEL و ufw در اوبونتو)، قالب آماده ارائه‌دهنده سرور، یا فایروالی مثل CSF است که کنترل‌پنل‌ها نصب می‌کنند. پس قدم اول این است که ببینید کدام لایه روی سرور شما فعال است — همان بررسی‌ای که در ابتدای این مقاله به آن اشاره شد؛ نوشتن قانون iptables روی سروری که firewalld مدیریتش می‌کند فقط وقت تلف کردن است.

قدم دوم مطمئن شدن از این است که سرویس واقعاً روی آن پورت در حال شنیدن است؛ اگر سرویس بالا نیامده یا فقط روی 127.0.0.1 گوش می‌دهد، هیچ قانون فایروالی مشکل را حل نمی‌کند:

ss -lntp

حالا قانون باز کردن پورت. مثال زیر پورت ۲۲۲۲ (کنترل‌پنل دایرکت‌ادمین) را باز می‌کند؛ برای هر پورت دیگری فقط شماره را عوض کنید — مثلاً --dport 7778 برای کلوکسو:

iptables -I INPUT -p tcp --dport 2222 -j ACCEPT

چرا -I و نه -A؟ چون در بسیاری از مجموعه‌قوانین آماده، آخرین قانون زنجیره INPUT یک DROP یا REJECT عمومی است؛ اگر قانون تازه را با -A به انتها اضافه کنید، بعد از آن DROP قرار می‌گیرد و هرگز اجرا نمی‌شود. -I بدون شماره، قانون را در ردیف اول درج می‌کند. اگر می‌خواهید جای دقیق‌تری داشته باشد، اول با --line-numbers شماره‌ها را ببینید و بعد شماره ردیف را به -I بدهید. برای سرویسی که روی UDP کار می‌کند -p udp بگذارید (سوکت‌های UDP در حال شنیدن هم با ss -lnup دیده می‌شوند، نه با ss -lntp که فقط TCP را نشان می‌دهد) و اگر سرور IPv6 فعال دارد، همان قانون را با ip6tables هم تکرار کنید.

محدود کردن دسترسی به یک IP یا رنج مشخص تقریباً همیشه ایده بهتری از باز کردن پورت به روی کل اینترنت است — به‌ویژه برای پورت کنترل‌پنل و دیتابیس:

iptables -I INPUT -p tcp --dport 2222 -s 203.0.113.10 -j ACCEPT

پورت‌های رایج کنترل‌پنل‌ها

  • دایرکت‌ادمین — پورت ۲۲۲۲.
  • cPanel/WHM — ۲۰۸۲ و ۲۰۸۳ برای cPanel و ۲۰۸۶ و ۲۰۸۷ برای WHM (نسخه‌های ۲۰۸۳ و ۲۰۸۷ روی SSL هستند و فقط همین دو تا را باز کنید).
  • Webmin — پورت ۱۰۰۰۰.
  • کلوکسو — پورت‌های ۷۷۷۷ و ۷۷۷۸؛ این کنترل‌پنل امروز دیگر توسعه فعالی ندارد و برای سرور تازه انتخاب مناسبی نیست.

معادل همین کار در firewalld و ufw

اگر روی سرور شما firewalld یا ufw فعال است، قانون را با ابزار خودشان اضافه کنید نه مستقیم با iptables؛ در غیر این صورت با اولین reload یا ریبوت، قانون دستی از بین می‌رود. در AlmaLinux، Rocky Linux، RHEL و CentOS:

firewall-cmd --permanent --add-port=2222/tcp
firewall-cmd --reload

و در اوبونتو و دبیان با ufw:

ufw allow 2222/tcp
ufw status numbered

همین منطق درباره CSF هم صدق می‌کند: اگر روی سرور CSF نصب است، قوانین دستی iptables با اولین csf -r پاک می‌شوند. پورت را به فهرست TCP_IN (و در صورت نیاز TCP_OUT) در فایل /etc/csf/csf.conf اضافه کنید و بعد csf -r بزنید.

پورت را باز کردم ولی سرویس هنوز در دسترس نیست

  • سرویس روی آن پورت گوش نمی‌دهد یا فقط به 127.0.0.1 بایند شده؛ خروجی ss -lntp را دوباره نگاه کنید.
  • قانون پایین‌تر از یک DROP یا REJECT قرار گرفته است؛ با iptables -L -n -v --line-numbers ترتیب و شمارنده بسته‌ها را ببینید. اگر شمارنده قانون شما صفر مانده، اصلاً به آن نرسیده‌اید.
  • کلاینت از مسیر IPv6 وصل می‌شود ولی قانون فقط در IPv4 اضافه شده است.
  • لایه دیگری جلوتر ترافیک را می‌گیرد: firewalld، ufw، CSF یا فایروال شبکه ارائه‌دهنده.
  • قانون اضافه شده اما ذخیره نشده و سرور ریبوت شده است؛ بخش بعدی دقیقاً درباره همین است.

ذخیره قوانین برای بعد از ریبوت

روش ذخیره‌سازی به توزیع بستگی دارد. در دبیان و اوبونتو معمولاً بسته iptables-persistent نصب می‌شود و دستور زیر قوانین را در /etc/iptables/rules.v4 و rules.v6 می‌نویسد:

netfilter-persistent save

در خانواده RHEL (شامل CentOS، AlmaLinux و Rocky Linux) بسته iptables-services این کار را انجام می‌دهد و خروجی در /etc/sysconfig/iptables ذخیره می‌شود:

service iptables save

save فعل systemd نیست و این کار را اسکریپت خودِ بسته iptables-services انجام می‌دهد؛ روی نصب‌های مینیمال جدید که فرمان service را ندارند، معادل دقیق و همیشه‌کار همین دستور این است:

iptables-save > /etc/sysconfig/iptables

در خانواده RHEL از نسخه ۷ به بعد (CentOS 7، AlmaLinux، Rocky Linux و RHEL) فایروال پیش‌فرض firewalld است؛ برای استفاده از این روش باید iptables-services را جداگانه نصب و firewalld را غیرفعال کنید، وگرنه قوانین یکدیگر را بازنویسی می‌کنند.

مستقل از توزیع، ابزارهای پشتیبان‌گیری و بازگردانی همیشه در دسترس‌اند؛ پیش از هر تغییر بزرگ از آن‌ها استفاده کنید:

iptables-save > /root/iptables.backup
iptables-restore < /root/iptables.backup

ویرایش مستقیم فایل قوانین

در آموزش‌های قدیمی زیاد دیده می‌شود که فایل قوانین را با ویرایشگری مثل vi یا nano باز کنید، خط مربوط به پورت را دستی اضافه کنید و بعد سرویس فایروال را ریستارت کنید. این روش هنوز کار می‌کند اما چند نکته دارد:

  • مسیر /etc/sysconfig/iptables فقط در خانواده RHEL و فقط وقتی iptables-services نصب باشد وجود دارد. در دبیان و اوبونتو فایل معادل /etc/iptables/rules.v4 است.
  • در vi برای ذخیره و خروج باید :wq بزنید — دو نقطه اول می‌آید، نه آخر.
  • ریستارت سرویس، قوانین در حال اجرا را پاک می‌کند و فایل را از نو می‌خواند؛ یعنی هر قانونی که ذخیره نکرده‌اید از بین می‌رود و یک اشتباه نگارشی در فایل می‌تواند سرور را روی شبکه ببندد.
  • دستور ریستارت روی توزیع‌های امروزی که systemd دارند این است و شکل قدیمی service iptables restart فقط به همین ترجمه می‌شود (این یونیت هم فقط وقتی وجود دارد که iptables-services نصب باشد؛ در دبیان و اوبونتو معادلش systemctl restart netfilter-persistent است):
systemctl restart iptables

در عمل مسیر امن‌تر این است که قانون را با خود iptables اضافه کنید، درست کار کردنش را ببینید و بعد با service iptables save یا netfilter-persistent save دائمی‌اش کنید؛ این‌طور هیچ‌وقت با فایلی که هنوز تست نشده سرویس را ریستارت نمی‌کنید.

مراقب باشید خودتان را از SSH بیرون نیندازید

رایج‌ترین حادثه هنگام کار با iptables قطع شدن دسترسی مدیر است. دو سناریوی کلاسیک: تغییر سیاست INPUT به DROP قبل از افزودن قانون اجازه SSH، و اجرای iptables -F وقتی سیاست روی DROP است — چون flush فقط قوانین را پاک می‌کند و سیاست را دست‌نخورده می‌گذارد، نتیجه‌اش بسته شدن کامل سرور روی شبکه است.

  • اول قانون ACCEPT پورت SSH را اضافه کنید، بعد سیاست را روی DROP بگذارید.
  • هنگام آزمایش یک نشست SSH باز را دست‌نخورده نگه دارید و تغییرات را در نشست دوم انجام دهید؛ اگر نشست دوم قطع شد، از اولی وضعیت را برگردانید.
  • پیش از تغییرات پرریسک یک بازگردانی زمان‌بندی‌شده بگذارید (نیازمند نصب at). دستور زیر پنج دقیقه بعد سیاست‌ها را باز و قوانین را پاک می‌کند؛ اگر همه چیز درست بود با atrm لغوش کنید:
echo "iptables -P INPUT ACCEPT; iptables -P FORWARD ACCEPT; iptables -F" | at now + 5 minutes

مهم‌تر از همه، مطمئن شوید یک راه دسترسی خارج از شبکه دارید: کنسول، KVM یا پنل مدیریت سرور. اگر چنین راهی نبود، بازگرداندن دسترسی معمولاً از مسیر پشتیبانی فنی ارائه‌دهنده سرور انجام می‌شود.

nftables و firewalld: جانشین‌های امروزی

iptables هنوز همه‌جا حاضر است، اما در توزیع‌های جدید پیاده‌سازی اصلی نیست. از دبیان ۱۰ و RHEL 8 به بعد nftables جایگزین شده و دستور iptables در عمل لایه سازگاری iptables-nft است که فرمان‌های قدیمی را ترجمه می‌کند. nftables چهار ابزار جدا (iptables، ip6tables، arptables و ebtables) را در یک نحو واحد ادغام کرده است. برای دیدن معادل nft یک قانون:

iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT

در سطحی بالاتر هم firewalld (پیش‌فرض در RHEL، CentOS و فدورا) و ufw (در اوبونتو) قرار دارند که مفهوم zone و سرویس را اضافه می‌کنند و تغییرات را بدون بارگذاری مجدد کل قوانین اعمال می‌کنند. یک هشدار عملی: هم‌زمان با firewalld قوانین را مستقیم با iptables دستکاری نکنید، وگرنه تغییرات دستی با اولین reload از بین می‌رود. انتقال یک مجموعه قانون قدیمی را هم با برنامه مشخص و در بازه کم‌ترافیک انجام دهید. اگر هنوز در مرحله انتخاب و طراحی زیرساخت هستید، بهتر است این تصمیم را کنار بقیه معماری سرور بررسی کنید؛ مشاوره زیرساخت آنلاین سرور برای همین مرحله انتخاب و طراحی است.

جمع‌بندی

iptables با سه مفهوم کار می‌کند: جدول‌ها که حوزه کار را مشخص می‌کنند، زنجیره‌ها که نقطه‌ای از مسیر بسته را نشان می‌دهند، و قوانینی که به ترتیب تا اولین تطبیق بررسی می‌شوند. سیاست پیش‌فرض را روی DROP بگذارید، قانون ESTABLISHED,RELATED را بالای زنجیره قرار دهید و همیشه یک راه بازگشت برای SSH داشته باشید. برای باز کردن پورت یک سرویس هم ترتیب کار همین سه قدم است: مطمئن شوید سرویس در حال شنیدن است، قانون را با -I بالاتر از DROP عمومی درج کنید و در پایان با ابزار ذخیره‌سازی توزیع خودتان دائمی‌اش کنید — و اگر روی سرور firewalld، ufw یا CSF فعال است، قانون را با ابزار خودِ آن‌ها بنویسید نه مستقیم با iptables.