VPS 的 IPv6 防火牆漏洞:UFW 如何補上缺口
Ubuntu 24.04 的 UFW 可處理 IPv6,但雲端防火牆或手寫 iptables 可能只限制 IPv4。了解如何找出 [::] 監聽服務並關閉公開入口。
一句話說明 IPv6 防火牆陷阱
您的防火牆保護 IPv4。您的 VPS 幾乎一定也有公開 IPv6 位址,而且許多服務預設會在該位址上監聽。如果防火牆只涵蓋 IPv4,或您依賴只過濾 IPv4 的雲端防火牆,那麼這些服務全都能透過 IPv6 從整個網際網路連入,即使 IPv4 端看起來已受到嚴格限制。您使用 curl 測試連接埠,看到連線遭拒,便以為環境安全。攻擊者則透過 IPv6 連線到相同的連接埠,直接進入系統。
本指南說明在一般 Ubuntu 24.04 VPS 上,這個缺口從何而來、如何確切查看您暴露了哪些服務,以及如何關閉這個缺口。UFW 不是問題所在。在現代 Ubuntu 安裝環境中,UFW 已能處理 IPv6。暴露問題來自周邊的各個層,以及您不知道正在監聽的服務。
為什麼你的 VPS 一開始就使用 IPv6
目前幾乎所有 VPS 都會配置公開 IPv6 位址,通常是完整的 /64,並與 IPv4 位址並列。請檢查你的位址:
ip -6 addr show scope global2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
inet6 2001:db8:2a::1/64 scope global本指南中的 2001:db8:2a::1 是 2001:db8::/32 提供的預留範例。這是專供文件使用的前綴,不會在網際網路上路由。伺服器在該行輸出的實際位址可從網際網路上的任何位置連線,與 IPv4 位址完全相同。接著查看目前正在監聽的服務:
sudo ss -tlnpState Recv-Q Local Address:Port Process
LISTEN 0 0.0.0.0:22 sshd
LISTEN 0 [::]:22 sshd
LISTEN 0 127.0.0.1:5432 postgres
LISTEN 0 [::]:8080 docker-proxy請仔細查看 Local Address 欄。0.0.0.0:22 表示「監聽所有 IPv4 位址」。[::]:22 表示「監聽所有 IPv6 位址」。127.0.0.1:5432 綁定至 loopback,完全不對外公開,因此 Postgres 那一行是安全的。兩行 [::] 會透過 IPv6 回應整個網際網路,而 docker-proxy 則是你可能忘記曾經啟動的服務。
大多數 daemon 預設會綁定至 ::,因為在 Linux 上,:: socket 通常也能接受 IPv4 連線。因此,全新伺服器的預設狀態是「在兩種通訊協定堆疊的所有位址上回應」。防火牆是唯一位於外部與這些服務之間的防護,因此防火牆若只處理其中一種通訊協定堆疊,就會造成實際問題。
IPv6 缺口的實際來源
常見來源有 3 種。在同一台主機上,可能只遇到其中一種,也可能同時遇到數種。
1. 僅過濾 IPv4 的雲端防火牆。 許多供應商的防火牆與 security-group 產品是在 IPv4 環境中發展而來,因此可能忽略 IPv6,或需要另外手動新增 IPv6 規則。如果你唯一使用的防火牆是供應商控制面板中的防火牆,而且未涵蓋 IPv6,那麼無論它對 IPv4 的 port 22 顯示什麼狀態,你的 [::] 服務都處於公開狀態。請閱讀供應商的防火牆文件,並特別尋找 IPv6 這個詞。
2. 只有 iptables、沒有 ip6tables 的手動設定。 iptables 指令只會處理 IPv4 tables。IPv6 使用完全獨立的指令 ip6tables,規則也彼此分開。如果你撰寫的防火牆 script 充滿 iptables -A INPUT ... 行,卻從未撰寫相應的 ip6tables 規則,IPv6 防火牆就是空的;而預設政策為 ACCEPT 的空 INPUT chain 會允許所有流量:
sudo ip6tables -L INPUT -nChain INPUT (policy ACCEPT)
target prot opt source destination這段輸出完整呈現了問題。IPv4 受到過濾,IPv6 卻接受來自任何來源的流量。
3. Docker 直接繞過防火牆發布 port。 執行 docker run -p 8080:80 時,Docker 會在 UFW 的規則之前插入自己的規則。因此,即使 ufw status 顯示該 port 被拒絕,已發布的 port 仍可連線。目前 Docker 預設會管理 ip6tables 規則,因此當容器的 network 啟用 IPv6 時,IPv6 也有相同情況。Docker 為何會繞過 UFW,以及如何正確過濾容器 port說明其運作機制與修正方式。若要了解這些已發布的 port 如何宣告,請參閱 VPS 上的 Docker Compose 基礎。
關於 UFW IPv6 開關的說明。 UFW 確實支援 IPv6,而且開關位於單一檔案中。請檢查該設定:
grep IPV6 /etc/default/ufw現代 Ubuntu 會提供 IPV6=yes,因此 UFW 會將每條規則套用至兩個 protocol stack。如果看到 IPV6=no,代表使用的是舊 image 或舊指南;這不表示 IPv6 未受保護。在此設定下,啟用中的 UFW 會丟棄所有 IPv6 流量,只有 loopback interface 例外,因此問題會反過來:原本要透過 IPv6 提供的服務會停止回應。如果希望 UFW 依照你的規則過濾 IPv6,而不是直接封鎖 IPv6,請將設定改為 yes。
確切查看對外暴露的項目
不要猜測。從外部實際測量。先列出監聽中的連接埠,並記下每個繫結至 :: 的連接埠:
sudo ss -tlnp | grep '::'接著從另一台機器連線至伺服器的公開 IPv6 位址,並測試一個你認為已關閉的連接埠:
curl -6 -v http://[2001:db8:2a::1]:8080/如果回傳頁面或 banner,表示該連接埠已在 IPv6 上開放。關閉的連接埠會回傳 Connection refused 或逾時。這兩種失敗代表的訊號不同,而 拒絕連線與逾時的差異 能告訴你主機是否已回應並拒絕連線,或是防火牆靜默丟棄了封包。若要取得完整結果,請從伺服器外部使用 nmap 掃描該 IPv6 位址:
nmap -6 2001:db8:2a::1nmap 回報在 IPv6 上開放的每個連接埠,都是整個網際網路可以連線的連接埠,無論你的 IPv4 掃描結果為何。將 IPv4 與 IPv6 掃描結果並排比較,是找出差異最快的方法:任何在 -6 上開放、但在 IPv4 上關閉的連接埠,都是防火牆遺漏的服務。
縮小防護缺口
讓 UFW 同時涵蓋 IPv4 與 IPv6,並將預設政策設為拒絕。 確認切換狀態,然後設定預設拒絕輸入流量的政策,只允許必要的流量:
sudo sed -i 's/^IPV6=no/IPV6=yes/' /etc/default/ufw
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose如果啟用 IPV6=yes 時 UFW 已經在執行,必須執行 sudo ufw reload,變更才會生效。
ufw status 會將每條規則列出 2 次,一次是一般規則,另一次則帶有 (v6) 後綴。看到 (v6) 行時,表示 UFW 正在過濾 IPv6:
22/tcp ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)如果手動管理 iptables,請在 ip6tables 中鏡像設定每條規則,或改用 nftables。nftables 的 inet 表可在同一處涵蓋 IPv4 與 IPv6,避免這類錯誤。自行撰寫規則時,使用單一 nftables inet filter table 是最乾淨的修正方式。如果 VPS 執行的是 Rocky 或 AlmaLinux,而不是 Ubuntu,便沒有 UFW 可供設定;改由 firewalld 作為管理介面,其 zone 規則會同時套用至兩種協定堆疊。
將不應公開的服務繫結至 loopback。 資料庫、管理面板或 metrics endpoint 通常完全不需要公開位址。將服務繫結至 127.0.0.1 和 ::1,讓它一開始就不會監聽可路由的位址。對 Postgres,設定 listen_addresses = 'localhost'。對應用程式伺服器,將其繫結至 127.0.0.1,並在前方放置反向代理。關閉 listener 比設定防火牆更可靠,因為這樣便沒有可供連線的服務。
不要依賴 UFW 保護 Docker 發布的連接埠。 將容器連接埠發布至特定位址,而不是所有介面,例如 -p 127.0.0.1:8080:80,讓該連接埠只能由主機及你明確設定的代理存取。容器確實需要公開時,請放在 Traefik 反向代理 後方,只發布代理的連接埠,不要逐一發布每個應用程式的連接埠。
在供應商防火牆中加入 IPv6 規則,否則就必須接受它不是 IPv6 的防火牆,改由主機上的 UFW 或 nftables 負責。
確認連接埠確實已關閉
完成變更後,再從外部執行相同的測試:
curl -6 -v http://[2001:db8:2a::1]:8080/
nmap -6 2001:db8:2a::1先前回應的連接埠現在應拒絕連線或逾時,而 nmap 應顯示該連接埠為 filtered 或 closed。若連接埠仍保持開啟,請重新檢查上述 3 個來源:服務仍繫結至 ::,前方卻沒有任何規則;Docker 規則優先於 UFW;或服務供應商的防火牆根本未處理 IPv6。
完全不讓敏感服務暴露在公用網際網路上,安全性更高。將 SSH 和管理面板置於 WireGuard VPN 後方,並封鎖這些服務的連接埠,使其只能透過通道回應,如此便不再需要處理 IPv6 暴露問題。若要降低針對仍公開服務所發動的暴力破解掃描,可在預設拒絕的防火牆上,進一步在 SSH 前方加入 Fail2ban。
如果你還不熟悉連接埠,請先閱讀 什麼是連接埠,以及服務如何監聽連接埠。
FAQ
UFW 預設會封鎖 IPv6 嗎?
在現代的 Ubuntu 24.04 安裝中,會。UFW 會從 IPV6=yes 讀取 /etc/default/ufw,並將每條規則套用至 IPv4 與 IPv6;ufw status 則會顯示帶有 (v6) 後綴的 IPv6 規則。常見問題包括依賴只篩選 IPv4 的供應商防火牆、手動撰寫的 iptables 規則沒有對應的 ip6tables 規則,或 Docker 將連接埠發布到 UFW 之外。IPV6=no(來自舊映像檔或舊教學)不會開啟 IPv6:啟用中的 UFW 會因此丟棄所有 IPv6 流量,但 loopback 除外。使用 grep IPV6 /etc/default/ufw 檢查此設定。
如何確認 VPS 在 IPv6 上暴露了哪些服務?
執行 sudo ss -tlnp,並注意每個本機位址以 [::] 開頭的監聽器。這表示它會在所有 IPv6 介面上接受連線。接著從另一台機器使用 curl -6 -v http://[YOUR:IPV6::ADDR]:PORT/ 直接測試伺服器的公開 IPv6 位址,或使用 nmap -6 YOUR:IPV6::ADDR 掃描該位址。IPv6 掃描顯示開放、但 IPv4 掃描顯示關閉的連接埠,就是需要修正的缺口。
為什麼 UFW 顯示 Docker 容器的連接埠遭封鎖,我仍然可以連線?
使用 -p 發布連接埠時,Docker 會將自己的防火牆規則插入 UFW 規則之前。因此,即使 ufw status 將該連接埠列為拒絕,發布的連接埠仍可連線。這在 IPv4 上會發生;Docker 啟用 IPv6 支援時,IPv6 也一樣。請發布至特定位址,例如 -p 127.0.0.1:8080:80,或將容器置於反向代理後方,只發布反向代理的連接埠。
如果 IPv4 防火牆設定完善,仍需要 IPv6 防火牆嗎?
需要。IPv4 與 IPv6 是分開的網路堆疊,也使用分開的防火牆規則。完善的 IPv4 規則對 IPv6 流量不起作用。如果 VPS 具有公開 IPv6 位址,而幾乎所有 VPS 都是如此,那麼任何監聽 :: 的服務都會持續透過 IPv6 接受連線,直到 IPv6 防火牆規則或 loopback 綁定位址阻止連線為止。
如何讓服務只監聽 IPv4,或只監聽 localhost?
在服務本身的設定中指定綁定位址。綁定至 127.0.0.1 可只使用 IPv4 loopback;綁定至 0.0.0.0 則會監聽所有 IPv4 位址,但不建立 IPv6 監聽器。Postgres 使用 listen_addresses,SSH 使用 ListenAddress,大多數應用程式伺服器則提供 host 或 bind 旗標。使用 sudo ss -tlnp 確認結果,並檢查 Local Address 是否不再顯示 [::]。