跳转到帖子
在手机APP中查看

一个更好的浏览方法。了解更多

PHP论坛人

主屏幕上的全屏APP,带有推送通知、徽章等。

在iOS和iPadOS上安装此APP
  1. 在Safari中轻敲分享图标
  2. 滚动菜单并轻敲添加到主屏幕
  3. 轻敲右上角的添加按钮。
在安卓上安装此APP
  1. 轻敲浏览器右上角的三个点菜单 (⋮) 。
  2. 轻敲添加到主屏幕安装APP
  3. 轻敲安装进行确认。

Debian 13 原生nftables防火牆 IPv4/IPv6 雙棧防護、Fail2ban入侵防護、Systemd開機自動啟動

精选回复

Debian 13 原生nftables防火牆 IPv4/IPv6 雙棧防護、Fail2ban入侵防護、Systemd開機自動啟動



******************************************************************************************
Fail2ban Part 1

Fail2ban暫時只設定SSH防護,等待LNMP服務都安裝配置完畢,再回來增加 監獄 (Jail) 設定
******************************************************************************************





---------------
前言
---------------

為何選擇原生 nftables?

相較於 iptables、ufw 或 firewalld,nftables 具備以下優勢:

雙棧統一管理:使用 inet 位址族,一套規則同時覆蓋 IPv4 與 IPv6,不會有遺漏

原子性載入:規則集一次性套用,不會出現套用到一半的中間狀態

內建 per-IP 計量器:速率限制更精確,無需額外核心模組

語法預驗證:nft -c -f 可在套用前驗證語法,大幅降低鎖死SSH的風險

Fail2ban 整合:與 Fail2ban 1.1.0+ 完整整合,動態封鎖攻擊IP



-----------------------------
IPv4 與 IPv6 雙棧的必要性
-----------------------------

許多管理員只設定 IPv4 防火牆,卻忽略了 IPv6。若伺服器持有 IPv6 位址而未設定對應規則,IPv6 流量將完全不受保護,形同開了一扇隱形後門

本教學使用 inet 位址族,同一套規則自動涵蓋 IPv4 與 IPv6,杜絕此類遺漏



--------------
整體架構
--------------

外部流量
   │
   ▼
nftables (inet filter)
   ├── BADPKT chain ── 記錄並丟棄非法封包(異常 TCP flags、invalid state)
   ├── INPUT chain
   │     ├── lo 介面 → ACCEPT
   │     ├── 已建立連線 (established/related) → ACCEPT(優先比對)
   │     ├── 無效/異常封包 → goto BADPKT
   │     ├── 封鎖清單 (BADIPS) → DROP
   │     ├── 私有網段白名單 → ACCEPT
   │     ├── DHCPv6 / MLD → ACCEPT(IPv6 必需)
   │     ├── ICMPv4 速率限制
   │     ├── ICMPv6 必要類型 → ACCEPT
   │     ├── SSH 速率限制(per-IP meter,僅限管理 IP)
   │     ├── Web (80/443) 速率限制(per-IP meter)
   │     └── 其餘 → DROP(記錄至 journal)
   ├── FORWARD chain → DROP(非路由器,直接丟棄)
   └── OUTPUT chain → ACCEPT
         │
         ▼
    Fail2ban(持續掃描日誌,動態封鎖暴力攻擊 IP)
         └── 操作 nftables set,將攻擊 IP 加入封鎖清單



---------------------------
安全提醒 (操作前必讀)
---------------------------

錯誤的規則會導致SSH連線立即中斷,操作前請先開啟 VNC / Serial Console / IPMI 等帶外管理備援

務必將你目前的管理IP加入允許清單 (包含 IPv6 位址)

腳本提供30秒測試模式:規則套用後 30秒自動復原,可安全驗證

雲端伺服器注意:AWS、GCP、Hetzner 等平台的內網 IP(如 10.x.x.x)若有負載平衡或監控需求,需填入 PRIVATE_IPS_V4

修改前請備份重要資料






----------
事前準備
----------

查看目前透過SSH連線的來源IP (包含 IPv6)
ss -tnp | grep :22 | grep ESTAB



查詢本機對外的 IPv4 位址
curl -4 ifconfig.me


查詢本機對外的 IPv6 位址(若有)
curl -6 ifconfig.me



請記下你的管理IP (包含 IPv6 位址),稍後需填入防火牆腳本的 ADMIN_IP4 與 ADMIN_IP6



------------------
確認IPv6狀態
------------------

確認 IPv6 是否已啟用(預期輸出為 0,代表已啟用)
sysctl net.ipv6.conf.all.disable_ipv6



確認本機的 IPv6 位址
ip -6 addr show





---------------------------------
安裝nftables並清除衝突工具
---------------------------------


更新套件清單
apt update


安裝 nftables 防火牆主體、conntrack 用於連線追蹤統計,/usr/local/bin/firewall.sh stats 指令會用到
apt install -y nftables conntrack





啟用並啟動 nftables 服務
systemctl enable --now nftables





------------------------------
確認nftables版本與狀態
------------------------------

確認nftables版本,預期 nftables v1.1.3 以上
nft --version



確認nftables狀態,預期:active
systemctl status nftables





-------------------------
停用衝突的防火牆工具
-------------------------


同時啟用多套防火牆工具會造成規則衝突,請依實際安裝情況停用



若有安裝ufw,請將其停用以避免規則衝突
systemctl disable --now ufw



若有安裝firewalld,請將其停用以避免規則衝突
systemctl disable --now firewalld




若有安裝 iptables/ip6tables,請將其停用以避免規則衝突
systemctl disable --now iptables

systemctl disable --now ip6tables



----------------------
確認初始規則狀態
----------------------


應顯示空的規則集或僅有nftables預設空表
nft list ruleset


--------------------
建立防火牆腳本
--------------------


建立腳本目錄
mkdir -p /usr/local/bin




建立防火牆腳本
vi /usr/local/bin/firewall.sh



請將提供的防火牆腳本 firewall.sh 內容複製貼上

firewall.sh

ADMIN_IP4="改為您的管理員 IPv4" # 改為您的管理員 IPv4(必填)
ADMIN_IP6="" # 改為您的管理員 IPv6(沒有請留空)
SSH_PORT="22"
WEB_PORTS="{80,443}"
ADMIN_PORT="888" # 僅限管理員存取的埠

# 內網網段(VPC / 監控 / 負載平衡器)
PRIVATE_IPV4="10.0.0.0/8"
PRIVATE_IPV6="fc00::/7" # IPv6 唯一本地位址

# 手動黑名單(空格分隔 IP 或 CIDR)
BAD_IPS_V4=""
BAD_IPS_V6=""




儲存檔案並離開vi編輯器
按 Esc,輸入 :wq,按 Enter



----------------
設定腳本權限
----------------

設定腳本權限 (700 表示僅 root 可讀寫執行,提高安全性)

chmod 700 /usr/local/bin/firewall.sh


chown root:root /usr/local/bin/firewall.sh






-----------------
測試防火牆
-----------------

執行測試模式
/usr/local/bin/firewall.sh test


執行後進入 30秒測試模式:

規則立即套用(IPv4 + IPv6 同步)

30 秒內若未按下 Ctrl+C,規則自動清除,不影響後續連線

30 秒內按下 Ctrl+C → 規則保留,繼續下一步






---------------------------------------
另開終端機確認連線 (測試期間進行)
---------------------------------------

測試SSH連線 (應成功)
ssh 你的伺服器IP


測試 IPv4 對外連線 (應成功)
ping -c 3 8.8.8.8


測試 IPv6 對外連線 (若有 IPv6)
ping6 -c 3 google.com


若SSH連線正常,在30秒倒數結束前按 Ctrl+C 保留規則,繼續下一步




--------------------------
永久套用規則
--------------------------

在測試倒數結束前按 Ctrl+C 保留規則後,執行以下指令正式套用並寫入設定檔

/usr/local/bin/firewall.sh start





---------------------
確認規則已套用
---------------------


查看目前規則集
nft list ruleset


查看 input chain
nft list chain inet filter input


查看封包計數
nft -a list chain inet filter input


查看統計資訊
/usr/local/bin/firewall.sh stats



-----------------------------
systemd 開機自動啟動
-----------------------------

本教學採用自訂 firewall.service 驅動腳本,而非直接依賴 nftables.service 載入靜態設定


原因:

腳本在每次啟動時動態建構規則,可整合環境變數與條件判斷

Before=network-pre.target 確保規則在網路啟動前就位,不留空窗期





建立 systemd 服務檔
vi /etc/systemd/system/firewall.service


貼上以下內容


[Unit]
Description=Custom nftables Firewall (IPv4 + IPv6 Dual Stack)
Before=network-pre.target
Wants=network-pre.target
DefaultDependencies=no

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/firewall.sh start
ExecStop=/usr/local/bin/firewall.sh stop
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target



儲存檔案並離開vi編輯器
按 Esc,輸入 :wq,按 Enter




------------------
啟用並啟動服務
------------------

重新載入 systemd 設定
systemctl daemon-reload


設定開機自動啟動
systemctl enable firewall.service


立即啟動
systemctl start firewall.service


檢查服務狀態,應顯示 active (exited)
systemctl status firewall.service


查看啟動日誌
journalctl -u firewall.service --no-pager



---------------------------------------
停用 nftables.service 避免衝突
---------------------------------------

重要:nftables.service 預設會在開機時載入 /etc/nftables.conf。若讓它與自訂 firewall.service 並存,可能發生規則衝突或重複載入



停用 nftables.service (改由 firewall.service 接管)

systemctl disable nftables.service


systemctl stop nftables.service



確認已停用,預期輸出 disabled
systemctl is-enabled nftables.service







-----------------------------------
持久化核心參數 (sysctl)
-----------------------------------

腳本中的 sysctl -w 指令只是 臨時調整,重開機後會恢復預設值。要持久化,需建立設定檔


建立
vi /etc/sysctl.d/99-hardening.conf



貼上內容



# TCP SYN Cookie(防 SYN Flood)
net.ipv4.tcp_syncookies = 1

# 忽略廣播 ping(防 Smurf 攻擊)
net.ipv4.icmp_echo_ignore_broadcasts = 1

# 忽略偽造錯誤回應
net.ipv4.icmp_ignore_bogus_error_responses = 1

# 拒絕 ICMP 重新導向(防路由劫持)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0

# 拒絕來源路由(防 IP spoofing)
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0

# 反向路徑過濾(防 IP spoofing)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# 記錄不可能的來源位址(Martian packets)
net.ipv4.conf.all.log_martians = 1





儲存檔案並離開vi編輯器
按 Esc,輸入 :wq,按 Enter



----------------------
立即套用並驗證
----------------------


立即套用
sysctl -p /etc/sysctl.d/99-hardening.conf



驗證,預期輸出:net.ipv4.tcp_syncookies = 1
sysctl net.ipv4.tcp_syncookies



------------------------
Fail2ban 安裝與配置
------------------------

Fail2ban會持續掃描系統日誌 (如 /var/log/auth.log),偵測到多次登入失敗時,自動將來源IP加入 iptables/ip6tables 封鎖規則,提供動態的入侵防護



安裝 Fail2ban 入侵防護主體、安裝 whois 查詢被封鎖IP的WHOIS資訊,方便排查攻擊來源

apt update


apt install -y fail2ban whois





確認版本,顯示為 Fail2Ban v1.1.0
fail2ban-client --version






------------------
建立自訂設定檔
------------------

為何不直接修改 jail.conf?

/etc/fail2ban/jail.conf 是套件提供的預設設定,套件更新時會被覆蓋

正確做法是建立 /etc/fail2ban/jail.local,Fail2ban啟動時 .local 的設定會覆蓋 .conf,這樣可以在套件更新後保留你的自訂設定

建立 jail.local

vi /etc/fail2ban/jail.local

貼上以下內容

jail.local.txt

儲存檔案並離開vi編輯器

按 Esc,輸入 :wq,按 Enter

暫時只設定SSH防護,等待LNMP服務都安裝配置完畢,再回來增加 監獄 (Jail) 設定


------------------------------------------------
Fail2ban × nftables 整合 Action
------------------------------------------------

建立 Action 設定檔
vi /etc/fail2ban/action.d/nftables-set.conf



貼上以下內容




# Fail2ban action for nftables with persistent blacklist sets
# 適用於 inet filter 表格中的 f2b-blacklist4 和 f2b-blacklist6

[Definition]

# 啟動時:確保 nftables sets 存在(若不存在則建立)
actionstart = 
    nft add set inet filter f2b-blacklist4 { type ipv4_addr\; flags timeout \; } 2>/dev/null || true
    nft add set inet filter f2b-blacklist6 { type ipv6_addr\; flags timeout \; } 2>/dev/null || true

# 從持久化檔案恢復黑名單(若有)
    [ -f /var/lib/fail2ban/nftables-blacklist4.ipset ] && \
        while read ip; do nft add element inet filter f2b-blacklist4 { $ip }; done < /var/lib/fail2ban/nftables-blacklist4.ipset

# 停止時:保留 sets,避免影響已運行的防火牆
# (若需刪除,取消下方兩行的註解)
actionstop = 
    # nft delete set inet filter f2b-blacklist4 2>/dev/null || true
    # nft delete set inet filter f2b-blacklist6 2>/dev/null || true

# 檢查 action 是否可用
actioncheck = 
    nft list set inet filter f2b-blacklist4 2>/dev/null || false

# 封鎖:加入黑名單 set,設定 timeout(自動過期)
actionban =
    nft add element inet filter f2b-blacklist4 { <ip> timeout <bantime>s } 2>/dev/null || true
    nft add element inet filter f2b-blacklist6 { <ip> timeout <bantime>s } 2>/dev/null || true

# 解封:從 set 中移除
actionunban = 
    nft delete element inet filter f2b-blacklist4 { <ip> } 2>/dev/null || true
    nft delete element inet filter f2b-blacklist6 { <ip> } 2>/dev/null || true

[Init]

# 預設封鎖時間(秒),可被 jail.local 覆蓋
bantime = 3600

# 可選:指定 nftables 路徑
nft = /usr/sbin/nft





儲存檔案並離開vi編輯器
按 Esc,輸入 :wq,按 Enter




-----------------------
cron 定期執行
-----------------------

Fail2ban 的 nftables set 為記憶體狀態,重啟後不會自動恢復

actionstart 中的持久化恢復邏輯需搭配定期備份才有效。若需跨重啟保留封鎖清單,可在 cron 定期執行


安裝cron
apt install -y cron


開機自動啟動Cron
systemctl enable cron


立即啟動Cron
systemctl start cron


檢查cron狀態,確認已運行 active (running)
systemctl status cron



檢視目前所有的Cron任務
crontab -l




編輯Cron
crontab -e



在底部貼上


# 每小時備份一次到持久化檔案
0 * * * * /usr/sbin/nft list set inet filter f2b-blacklist4 2>/dev/null | /bin/grep -oP '\d+\.\d+\.\d+\.\d+' > /var/lib/fail2ban/nftables-blacklist4.ipset



儲存檔案並離開vi編輯器
按 Esc,輸入 :wq,按 Enter







------------------------
啟用 Fail2ban
------------------------

啟用並啟動
systemctl enable --now fail2ban


確認狀態,應顯示 active (running)
systemctl status fail2ban


測試設定檔語法,預期輸出:OK: configuration test is successful
fail2ban-client -t







-------------------
重開機完整驗證
-------------------


重開機
reboot



確認IP轉發已啟用,應顯示 net.ipv4.ip_forward = 1
sysctl net.ipv4.ip_forward


確認IP轉發已啟用,應顯示 net.ipv6.conf.all.forwarding = 1
sysctl net.ipv6.conf.all.forwarding



確認防火牆規則已自動載入
nft list ruleset


nft list chain inet filter input




確認防火牆服務狀態,應顯示 active (exited)
systemctl status firewall.service



確認 fail2ban 服務狀態,應顯示為 active (running)
systemctl status fail2ban.service


查看所有啟用的監獄
fail2ban-client status


查看 SSH 監獄狀態
fail2ban-client status sshd


確認白名單已正確載入 (應顯示你的管理員IPv4與IPv6、你的VPS私有IP)
fail2ban-client get sshd ignoreip



確認 Fail2ban 已整合 nftables,應顯示 f2b- 開頭的規則

(若剛設好,可能尚無觸發,無輸出為正常)

nft list ruleset | grep f2b




確認核心參數已持久化

已啟用,應顯示 net.ipv4.tcp_syncookies = 1
sysctl net.ipv4.tcp_syncookies




已禁用,應顯示 net.ipv4.conf.all.accept_redirects = 0
sysctl net.ipv4.conf.all.accept_redirects








確認服務監聽狀態

ss -tlnp | grep :22


0.0.0.0:22 → IPv4 監聽
[::]:22         → IPv6 監聽

兩個都有才代表雙棧正常



確認 nftables.service 已停用,應顯示 disabled
systemctl is-enabled nftables.service





------------------
測試封鎖功能
------------------

使用 RFC 5737 / RFC 3849 保留的文件用 IP 進行測試,不影響任何真實主機


手動封鎖測試 IPv4 (RFC 5737 文件用IP)
fail2ban-client set sshd banip 192.0.2.100


手動封鎖測試 IPv6 (RFC 3849 文件用IP)
fail2ban-client set sshd banip 2001:db8::100


查看監獄狀態 (Banned IP 欄位應顯示上述被封鎖的IP)
fail2ban-client status sshd



確認 nftables 規則已產生
nft list ruleset | grep f2b



測試完畢後,解除封鎖
fail2ban-client set sshd unbanip 192.0.2.100


fail2ban-client set sshd unbanip 2001:db8::100



---------------------
常見陷阱與排錯指南
---------------------

陷阱 1:IPv6 防火牆未設定

症狀:IPv4 連線正常,IPv6 流量完全開放

說明:使用 inet 位址族已同時涵蓋 IPv4 與 IPv6,本教學不會有此問題。若自行修改規則只用 ip 族,則需額外建立 ip6 族規則

驗證:
nft list ruleset | grep 'inet\|ip6'


應看到 table inet filter,代表雙棧皆受保護




陷阱 2:ICMPv6 被封鎖

症狀:IPv6 連線時通時不通,或無法取得 IPv6 位址 (SLAAC 失效)

解決:確認 IN_ICMPV6_ALLOWED 包含 133-136 (NDP 鄰居探索) 類型

nft list chain inet filter input | grep icmpv6





陷阱 3:雲端內網IP被封鎖

症狀:重開機後負載平衡器或監控系統無法連線 (常見於 AWS/GCP/Hetzner)

解決:在腳本的 PRIVATE_IPS_V4 填入對應的私有網段 (如 10.0.0.0/8)




陷阱 4:忘記加入管理 IP

症狀:執行 firewall.sh start 後SSH立即斷線

解決:透過帶外管理 (VNC/Console) 執行:

nft flush ruleset

然後修正腳本中的管理IP再重新測試




陷阱 5:服務只監聽 IPv4

症狀:nftables 規則正確,但 IPv6 用戶仍無法連線論壇

解決:確認 NGINX 監聽 ::80 而非 0.0.0.0:80:

ss -tlnp | grep :80

# :::80  → 雙棧(正確)

# 0.0.0.0:80 → 僅 IPv4(需修改 NGINX 設定)



NGINX 設定應包含:
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;





陷阱 6:sysctl 參數重開機後消失

症狀:重開機後 net.ipv4.tcp_syncookies 回到預設值 0

解決:確認已完成 持久化核心參數sysctl的設定,建立 /etc/sysctl.d/99-hardening.conf



陷阱 7:nftables.service 與 firewall.service 衝突

症狀:重開機後規則被 nftables.service 覆蓋或清空

解決:確認已停用 nftables.service:

systemctl is-enabled nftables.service


若顯示 enabled,執行:
systemctl disable nftables.service






陷阱 8:Fail2ban 白名單未加入 VPS私有IP

症狀:Fail2ban 誤封鎖雲端平台的健康檢查IP,導致服務中斷

解決:在 jail.local 的 ignoreip 加入VPS分配的私有IP

查看VPS的私有IP
ip addr show | grep 'inet '

確認白名單
fail2ban-client get sshd ignoreip





陷阱 9:論壇靜態資源或API被速率限制誤封

症狀:正常用戶在短時間內大量載入頁面 (CDN 回源、搜尋爬蟲) 被速率限制

解決:調高 WEB_RATE_LIMIT,或針對已知的 CDN / 爬蟲 IP 段加入白名單

# 例如允許 Cloudflare IP 不受速率限制(需手動更新 IP 段)
# 在腳本 PRIVATE_IPS_V4 中加入 Cloudflare IP 段
# 最新 IP 段:https://www.cloudflare.com/ips/




陷阱 10:網卡名稱不是 eth0

症狀:NAT 規則套用後,SNAT 功能無效

解決:Debian 13 在部分主機商的網卡名稱可能為 ens3、ens18、enp1s0 等,需先確認

找出實際的網卡名稱,再修改 nft-nat-vpc.sh 中的 IFACE 變數
ip link show



陷阱 11:bantime 單位問題(nftables timeout)

症狀:Fail2ban 封鎖後封鎖時間不符預期,IP 提早或延遲解封

說明:nftables timeout 接受帶單位的字串(如 3600s、1h)。nftables-set.conf 中 actionban 的 <bantime> 變數由 Fail2ban 傳入的是秒數整數,需在 action 中手動加上 s 後綴:

nft add element inet filter f2b-blacklist4 { <ip> timeout <bantime>s }




陷阱 12:Fail2ban 重啟後黑名單消失

說明:Fail2ban 的 nftables set 為記憶體狀態,重啟後不會自動恢復。actionstart 中的持久化恢復邏輯需搭配定期備份才有效。若需跨重啟保留封鎖清單,可在 cron 定期執行:







------------------------
日常維護指令速查
------------------------

查看完整規則集
nft list ruleset


查看 input chain (含規則)
nft list chain inet filter input


即時監控被丟棄的封包
journalctl -k -f | grep -E "BADPKT|DROP|INPUT_DROP"


查看核心日誌
dmesg -w | grep -E "BADPKT|DROP"



----------------
管理防火牆
----------------

手動啟動防火牆
/usr/local/bin/firewall.sh start


手動停止防火牆 (清除所有規則)
/usr/local/bin/firewall.sh stop


測試模式 (修改規則後先測試)
/usr/local/bin/firewall.sh


透過 systemd
systemctl start firewall.service

systemctl stop  firewall.service

systemctl restart firewall.service

systemctl status firewall.service


--------------------
管理 Fail2ban
--------------------

查看所有監獄
fail2ban-client status


查看特定監獄 (如 sshd)
fail2ban-client status sshd


手動封鎖 IP
fail2ban-client set sshd banip 1.2.3.4


解除封鎖 IP
fail2ban-client set sshd unbanip 1.2.3.4


重新載入設定
fail2ban-client reload


查看日誌
journalctl -u fail2ban.service --no-pager -n 50


------------------
查看服務日誌
------------------

防火牆服務日誌 (本次開機)
journalctl -u firewall.service --boot


Fail2ban 服務日誌 (本次開機)
journalctl -u fail2ban.service --boot


即時監控所有相關日誌
journalctl -f | grep -E "fail2ban|nftables|BADPKT|DROP"

本帖最后于,由Jack编辑

创建帐户或登录后发表意见

帐户

导航

搜索

搜索

配置浏览器推送通知

Chrome (安卓)
  1. 轻敲地址栏旁的锁形图标。
  2. 轻敲权限 → 通知。
  3. 调整你的偏好。
Chrome (台式电脑)
  1. 点击地址栏中的挂锁图标。
  2. 选择网站设置。
  3. 找到通知选项并调整你的偏好。