چیت‌شیت

CSF + IPSet — محدود کردن SSH فقط به IPهای ایران

هدف: روی سروری که CSF نصب است، پورت‌های SSH (22 و 2222) فقط از IPهای ایران قابل دسترسی باشند و بقیهٔ دنیا drop شوند — بدون وابستگی به قابلیت Country Allow خود CSF.

۱. بکاپ گرفتن

قبل از هر تغییری، از فایل‌های اصلی CSF بکاپ بگیر:

cp -a /etc/csf/csf.conf /etc/csf/csf.conf.bak-iran-ssh
cp -a /etc/csf/csf.allow /etc/csf/csf.allow.bak-iran-ssh

۲. ساخت فایل رنج‌های IP ایران

فایل اصلی رو بساز:

nano /etc/csf/iran_ipv4.txt

داخلش هر CIDR رو در یک خط بنویس، مثلاً:

2.144.0.0/14
5.22.0.0/15
5.34.192.0/18
...
217.219.0.0/16
185.129.0.0/16

دسترسی فایل رو محدود کن:

chmod 600 /etc/csf/iran_ipv4.txt

تعداد رنج‌ها رو چک کن:

wc -l /etc/csf/iran_ipv4.txt

(در نمونهٔ ما تعداد رنج‌ها باید برابر با تعداد خط‌های فایل باشه — مثلاً ۱۰۷.)

۳. تست فرمت رنج‌ها

قبل از استفادهٔ واقعی، صحت فرمت CIDRها رو تست کن:

while read -r cidr; do
    [ -z "$cidr" ] && continue
    ipset add iran_ssh4 "$cidr" -exist 2>/dev/null || echo "BAD: $cidr"
done < /etc/csf/iran_ipv4.txt

اگر خروجی BAD: ... دیدی، یعنی اون رنج مشکل داره. اگر هیچ BAD ندیدی، فایل سالمه.

۴. ساخت IPSet ایران

# Create the ipset for Iranian IPv4 ranges
ipset create iran_ssh4 hash:net family inet maxelem 65536 -exist
ipset flush iran_ssh4

# Load ranges from the file
while IFS= read -r cidr; do
    [ -z "$cidr" ] && continue
    case "$cidr" in
        \#*) continue ;;
    esac
    ipset add iran_ssh4 "$cidr" -exist || echo "BAD: $cidr"
done < /etc/csf/iran_ipv4.txt

بررسی نتیجه:

ipset list iran_ssh4 | grep -E 'Number of entries|Size in memory'

خروجی باید چیزی شبیه این باشه:

Size in memory: 6784
Number of entries: 107

۵. تست یک IP ایرانی

با یک IP ایرانی واقعی تست کن (اینجا به‌جای IP واقعی، یک نمونه گذاشته شده):

ipset test iran_ssh4 <IRAN_IP_EXAMPLE>

باید بگه:

<IRAN_IP_EXAMPLE> is in set iran_ssh4

۶. تست یک IP غیرایرانی

ipset test iran_ssh4 <NON_IRAN_IP_EXAMPLE>

باید بگه:

<NON_IRAN_IP_EXAMPLE> is NOT in set iran_ssh4

۷. تنظیم CSF

فایل رو باز کن:

nano /etc/csf/csf.conf

این خط رو داشته باش (دقت کن پورت‌های 22 و 2222 داخل TCP_IN نباشند):

TCP_IN = "35000:35999,20,21,25,53,853,80,110,143,443,465,587,993,995"

برای IPv6 هم همین:

TCP6_IN = "35000:35999,20,21,25,53,853,80,110,143,443,465,587,993,995"

و این‌ها باید خالی باشند (یعنی این روش دیگه به Country Allow خود CSF وابسته نیست):

CC_ALLOW_PORTS = ""
CC_ALLOW_PORTS_TCP = ""

۸. ساخت Hook دائمی CSF (مهم‌ترین بخش)

nano /usr/local/csf/bin/csfpost.sh

کل محتوای فایل:

#!/bin/bash

IPSET="/usr/sbin/ipset"
IPTABLES="/usr/sbin/iptables"
SET="iran_ssh4"
FILE="/etc/csf/iran_ipv4.txt"

# Create/rebuild Iran IPv4 set
$IPSET create "$SET" hash:net family inet maxelem 65536 -exist
$IPSET flush "$SET"

while IFS= read -r cidr; do
    [ -z "$cidr" ] && continue
    case "$cidr" in
        \#*) continue ;;
    esac
    $IPSET add "$SET" "$cidr" -exist || echo "csfpost: invalid CIDR: $cidr" >&2
done < "$FILE"

# Remove our previous SSH rules if they exist
$IPTABLES -D INPUT -p tcp -m conntrack --ctstate NEW -m tcp --dport 22 -m set --match-set chain_ALLOW src -j ACCEPT 2>/dev/null
$IPTABLES -D INPUT -p tcp -m conntrack --ctstate NEW -m tcp --dport 2222 -m set --match-set chain_ALLOW src -j ACCEPT 2>/dev/null
$IPTABLES -D INPUT -p tcp -m conntrack --ctstate NEW -m tcp -m multiport --dports 22,2222 -m set --match-set "$SET" src -j ACCEPT 2>/dev/null
$IPTABLES -D INPUT -p tcp -m conntrack --ctstate NEW -m tcp -m multiport --dports 22,2222 -j DROP 2>/dev/null

# Temporary safety: preserve explicitly allowed CSF IPs for SSH
$IPTABLES -I INPUT 1 -p tcp -m conntrack --ctstate NEW -m tcp --dport 22 -m set --match-set chain_ALLOW src -j ACCEPT
$IPTABLES -I INPUT 2 -p tcp -m conntrack --ctstate NEW -m tcp --dport 2222 -m set --match-set chain_ALLOW src -j ACCEPT

# Iran-only SSH
$IPTABLES -I INPUT 3 -p tcp -m conntrack --ctstate NEW -m tcp -m multiport --dports 22,2222 -m set --match-set "$SET" src -j ACCEPT

# Everyone else: block SSH
$IPTABLES -I INPUT 4 -p tcp -m conntrack --ctstate NEW -m tcp -m multiport --dports 22,2222 -j DROP

exit 0

دسترسی اجرا بده:

chmod 700 /usr/local/csf/bin/csfpost.sh

تست syntax:

bash -n /usr/local/csf/bin/csfpost.sh && echo "SCRIPT_OK"

باید بگه: SCRIPT_OK

۹. اجرای Hook

/usr/local/csf/bin/csfpost.sh

۱۰. Reload کردن CSF

csf -r

بعد بررسی کن:

iptables -t filter -S INPUT | head -15

باید در ابتدای INPUT چیزی شبیه این ببینی:

-A INPUT ... --dport 22 ... chain_ALLOW ... -j ACCEPT
-A INPUT ... --dport 2222 ... chain_ALLOW ... -j ACCEPT
-A INPUT ... --dports 22,2222 ... iran_ssh4 ... -j ACCEPT
-A INPUT ... --dports 22,2222 -j DROP

ترتیب خیلی مهمه:

CSF allow

Iran IP

DROP everyone else

۱۱. بررسی پایداری بعد از Reload

iptables -t filter -S INPUT | grep -E 'dport|iran_ssh4|chain_ALLOW'
ipset list iran_ssh4 | grep -E 'Number of entries|Size in memory'

۱۲. مدیریت IP موقت SSH (Safety Net)

برای احتیاط، قبل از اعمال قانون نهایی، یک IP شناخته‌شده رو موقتاً در csf.allow نگه دار:

grep -F '<TEMP_ALLOWED_IP>' /etc/csf/csf.allow

⚠️ تا وقتی SSH از IP ایرانی رو با موفقیت تست نکردی، این IP موقت رو حذف نکن.

وقتی مطمئن شدی اتصال SSH از IP ایرانی کاملاً کار می‌کند، IP موقت رو حذف کن:

csf -ar <TEMP_ALLOWED_IP>

بعد بررسی کن:

grep -F '<TEMP_ALLOWED_IP>' /etc/csf/csf.allow || echo "REMOVED"

باید بگه: REMOVED

۱۳. تست نهایی

از یک IP ایرانی:

ssh root@<SERVER_IP> -p 22
ssh root@<SERVER_IP> -p 2222

هر دو باید وصل بشن.

از یک IP غیرایرانی:

اتصال جدید به همون پورت‌ها (22 و 2222) باید بلاک بشه.

خلاصهٔ خیلی کوتاه برای دفعات بعد

معماری کلی این روش:

/etc/csf/iran_ipv4.txt

      ipset
   iran_ssh4

   csfpost.sh

  iptables INPUT

    22 + 2222

Iran     → ACCEPT
Non-Iran → DROP

فایل‌های مهم:

  • /etc/csf/csf.conf
  • /etc/csf/csf.allow
  • /etc/csf/iran_ipv4.txt
  • /usr/local/csf/bin/csfpost.sh

بکاپ‌ها:

  • /etc/csf/csf.conf.bak-iran-ssh
  • /etc/csf/csf.allow.bak-iran-ssh

⚠️ نکتهٔ مهم: این روش علاوه بر IPهای ایران، هر IP موجود در csf.allow رو هم برای SSH اجازه می‌ده. برای حالت کاملاً «فقط ایران»، بعد از اطمینان از دسترسی، باید csf.allow رو بررسی کنی و هر IP غیرایرانیِ غیرضروری رو حذف کنی.

← بازگشت به همه پست‌ها