پورت‌هایی را ببندید که ufw پوشش نمی‌دهد

portguard یک اسکریپت bash است که هر دقیقه با cron اجرا می‌شود. به هر پورت TCP در حال گوش‌دادن همان‌طور وصل می‌شود که یک ماشین دیگر در شبکه وصل می‌شد، و هر پورتی را که جواب بدهد می‌بندد.

دانلود آخرین نسخه
یک ماشین در شبکه محلی از شکاف ufw به پورت منتشرشده 7777 در Docker می‌رسد؛ سپس یک قانون drop از portguard شکاف را می‌بندد. یک ماشین در شبکه محلی 192.0.2.50 ufw 7777 از ufw رد می‌شود portguard همین‌طور آزمایش می‌کند nft drop کانتینر Docker پورت منتشرشده 7777
ufw برای پورت 7777 هیچ قانونی نشان نمی‌دهد، ولی آزمایش به آن می‌رسد. portguard قانون جاافتاده را اضافه می‌کند.

چرا ufw پورت‌ها را از قلم می‌اندازد

Docker وقتی با دسترسی root اجرا شود، پورت را با قانون‌های فایروال خودش منتشر می‌کند و ترافیک آن قانون‌ها از ufw رد می‌شود. ممکن است پورتی از شبکه در دسترس باشد در حالی که ufw status چیزی برایش نشان نمی‌دهد.

خواندن قانون‌ها نشان نمی‌دهد پورتی واقعاً در دسترس است یا نه، برای همین portguard آن‌ها را نمی‌خواند. از یک network namespace جداگانه واقعاً وصل می‌شود و فقط به پاسخ پورت نگاه می‌کند.

هر دقیقه چه اتفاقی می‌افتد

  1. پورت‌های TCP را که روی ماشین گوش می‌دهند فهرست می‌کند.
  2. هر کدام را از یک network namespace موقت آزمایش می‌کند، پس فایروال آزمایش را یک ماشین شبکه محلی می‌بیند.
  3. برای هر پورتی که جواب داد، یک قانون drop در nftables برای اتصال‌های ورودی تازه می‌گذارد.
  4. برای هر پورت بسته‌شده یک هشدار syslog می‌نویسد و یک اعلان حیاتی روی دسکتاپ نشان می‌دهد.

نصب

فایل‌های portguard و portguard.cron را از صفحه نسخه‌ها بگیرید و با دسترسی root سر جایشان بگذارید. پیش از نصب کار cron، مقدار ALLOWED_PORTS را تنظیم کنید: فهرست پیش‌فرض خالی است و اولین اجرا همهٔ پورت‌های در دسترس، از جمله SSH، را می‌بندد.

# the script: 0755, owned by root
sudo install -m 0755 -o root -g root portguard /usr/local/sbin/portguard
sudoedit /usr/local/sbin/portguard   # set ALLOWED_PORTS first, e.g. "22" for SSH: an empty list blocks SSH too
# the cron job: 0644, owned by root
sudo install -m 0644 -o root -g root portguard.cron /etc/cron.d/portguard

برای برداشتن یک مسدودسازی، جدولی را که portguard ساخته پاک کنید:

sudo nft delete table inet portguard

برای اینکه عمداً پورتی باز بماند، شماره‌اش را به ALLOWED_PORTS در /usr/local/sbin/portguard اضافه کنید. این کار پورتی را که portguard پیش‌تر بسته است دوباره باز نمی‌کند: ابتدا جدول را پاک کنید. با این کار همهٔ مسدودسازی‌ها برداشته می‌شود و اجرای بعدی بقیه را دوباره می‌بندد.

چه چیزهایی را پوشش نمی‌دهد

  • فقط پروتکل TCP پوشش داده می‌شود و شنونده‌های UDP آزمایش نمی‌شوند.
  • قانون‌های ufw که فقط به شبکه‌های مبدأ، رابط‌ها یا نشانی‌های مقصد مشخصی اجازه می‌دهند شبیه‌سازی نمی‌شوند.
  • حالت مسیریابی (routed gateway) در Docker شبیه‌سازی نمی‌شود.
  • IPv6 باید روی میزبان فعال باشد: آزمایش یک نشانی IPv6 اضافه می‌کند و اگر این کار شکست بخورد، اجرا هشدار می‌دهد و هیچ پورتی را بررسی نمی‌کند.
  • شنونده‌ای که به یک رابط واحد وصل شده باشد (IPv4 یا IPv6، مثلاً با SO_BINDTODEVICE یا دامنهٔ %eth0) فقط ثبت می‌شود و آزمایش نمی‌شود.
  • پورتی که از قبل بسته شده، با افزودن به ALLOWED_PORTS باز نمی‌شود؛ برای برداشتن همهٔ مسدودسازی‌ها جدول را پاک کنید.

این پروژه با پروانه Apache-2.0 منتشر شده است. برای گزارش مشکل امنیتی به bb@cocode.dk نامه بنویسید.