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 غیرایرانیِ غیرضروری رو حذف کنی.
