nftables 與 OpenVPN 的結(jié)合實(shí)踐

本文對比了 linux 環(huán)境各類防火墻工具,還展示了 iptables 規(guī)則如何保存到文件并翻譯成 nftables 規(guī)則,并給出了 nftables 與 openvpn 配合對混合云內(nèi)網(wǎng)用戶訪問權(quán)限的精準(zhǔn)控制方案。
先看下目前的架構(gòu):
1. 選型與對比
鑒于之前寫的 VPN 權(quán)限管理項(xiàng)目的缺點(diǎn),以及對比 iptables(ipset)、nftables、ebpf-iptables 后,確定過濾網(wǎng)絡(luò)數(shù)據(jù)包的底層工具還是選用 nftables 而不是 iptables+ipset。
理由如下:
-
ebpf 很優(yōu)秀,但需要 C 編程,開發(fā)效率低下且目及處無人會(huì) C;其 golang 庫小眾年久失修; -
紅帽自己的 iptables 和 ipset 替代方案 nftables 納入考慮;而且有 golang 的比較成熟的庫; -
iptables+ipset,優(yōu)點(diǎn)是只需要熟悉一下 ipset,有 golang 的兩個(gè)庫;缺點(diǎn)是既然 1+1=2,為什么要用兩個(gè)東西,封裝學(xué)習(xí)兩個(gè) golang 庫?
再次總結(jié)下 nftables 的優(yōu)點(diǎn):
-
相較于 ebpf,是完整的防火墻實(shí)現(xiàn),有命令行;ebpf 是底層,有特別小眾的防火墻應(yīng)用 【方便能上手】 -
內(nèi)置查找表而不是線性處理 【處理速度更快】 -
以原子方式應(yīng)用規(guī)則,而不是獲取、更新和存儲(chǔ)完整的規(guī)則集 【這一點(diǎn)對開發(fā)管理極其有利】
2. 實(shí)踐應(yīng)用考慮
舊的管理只有 iptables,生產(chǎn)上沒用過 ipset,現(xiàn)在決定直接用 nftables 替代 iptables;
舊的 iptables 規(guī)則有三部分,按照從用戶到目標(biāo)服務(wù)器的順序?yàn)椋?/p>
-
openvpn 的基礎(chǔ) iptables 規(guī)則,把來自 openvpn 虛擬 IP 網(wǎng)段的用戶的請求 全部通過 openvpn 服務(wù)器的 eth0 網(wǎng)卡轉(zhuǎn)發(fā)出去 也就是我們平時(shí)所說的 IPtables 的 NAT 規(guī)則,并進(jìn)行原地址偽裝例如 iptables -t nat -A POSTROUTING -s 10.10.0.0/16 -o eth0 -j MASQUERADE10.10.0.0/16 為 openvpn 用戶虛擬 IP 網(wǎng)段【在 VPN 中樞 需要改變?yōu)?nft】 -
各個(gè) VPC 中 wireguard 中繼器的配置中中繼器負(fù)責(zé)路由整個(gè) VPC 流量的路由【中繼器不作改動(dòng) 還用 iptables】 -
真正作用于用戶訪問內(nèi)網(wǎng)地址的業(yè)務(wù)規(guī)則【在 VPN 中樞服務(wù)器 需要改變?yōu)?nft】
3. openvpn 權(quán)限控制原理
VPN 權(quán)限管理的核心是 masquerade,即源地址偽裝:VPN 用戶訪問內(nèi)網(wǎng)的流量全都在 VPN 服務(wù)器這里進(jìn)行路由和轉(zhuǎn)發(fā)。
當(dāng)一個(gè)數(shù)據(jù)包走到 VPN 服務(wù)器時(shí),netfilter 將數(shù)據(jù)包的源 IP 偽裝成本機(jī)(VPN 服務(wù)器)的地址,然后根據(jù)規(guī)則將數(shù)據(jù)包送往不同的地址。
例如nft insert rule ip nat postrouting oifname eth0 ip saddr 10.121.0.3 ip daddr 192.168.3.0/24 tcp dport 80 counter jump masquerade,從 VPN 虛擬 IP10.121.0.3 到 192.168.3.0/24 的 80 端口的流量,前者源地址被 masquerade 成本機(jī)地址,然后就成了本機(jī)發(fā)往 192.168.3.0/24 的數(shù)據(jù)包,再參考路由表,發(fā)現(xiàn)路由表有這樣一條路由,于是數(shù)據(jù)包就從本機(jī)的 etho 接口轉(zhuǎn)給了 wg0 接口。wg0 就是 wireguard 服務(wù)的接口,于是數(shù)據(jù)包就發(fā)給了 wireguard 內(nèi)網(wǎng)的對應(yīng) peer 上,再由該 peer 通過 eth0 網(wǎng)卡轉(zhuǎn)發(fā)到本地網(wǎng)絡(luò)即可。
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default gateway 0.0.0.0 UG 100 0 0 eth0
192.168.0.0 0.0.0.0 255.255.0.0 U 0 0 0 wg0
因此,我要這里要保證:
-
各個(gè)云的 ACL 和安全組配置好、wg 中繼器 VPC 的路由配置好 -
wireguard 混合云的各個(gè) VPC 與這臺(tái) VPN 服務(wù)器(中樞)全通 -
所有的控制點(diǎn)都放在 VPN 中樞,用是否有用戶到目的地的規(guī)則來控制訪問權(quán)限,粒度可以精細(xì)到【哪個(gè)用戶>>目的 ip-協(xié)議-端口】
因此業(yè)務(wù)場景下我們要先準(zhǔn)備好基礎(chǔ)規(guī)則:
4. nftables 替換 iptables 規(guī)則步驟及測試
-
維護(hù) iptables 基本的規(guī)則,未來將此 iptables 規(guī)則全部轉(zhuǎn)換為 nftables 規(guī)則
# 先清空規(guī)則,然后INPUT OUTPUT FORWARD全接受,如果drop會(huì)讓22端口也被干掉!
iptables -F
iptables -X
iptables -Z
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
# 查詢nat表所有規(guī)則
iptables -t nat -L --line-numbers
# 刪除nat表POSTROUTING鏈規(guī)則的第一條
iptables -t nat -D POSTROUTING 1
-
然后到 ubantu 用翻譯工具翻譯成 nftables 規(guī)則:
必要:ubantu linux 內(nèi)核高于 4.8 才能支持 iptables 翻譯工具!
# 保存iptables規(guī)則
iptables-save > default.rules
# 將文件default.rules發(fā)送到一臺(tái)有iptables翻譯nftables規(guī)則的ubantu上并輸入nft命令
iptables-restore-translate -f default.rules
得到 nft 命令,這個(gè)命令在關(guān)掉 iptables 以及 iptables 的 nat 后進(jìn)入 nftables 的交互模式執(zhí)行:
# Translated by iptables-restore-translate v1.6.1 on Tue Jul 6 16:30:39 2021
add table ip filter
add chain ip filter INPUT { type filter hook input priority 0; policy accept; }
add chain ip filter FORWARD { type filter hook forward priority 0; policy accept; }
add chain ip filter OUTPUT { type filter hook output priority 0; policy accept; }
add table ip nat
add chain ip nat PREROUTING { type nat hook prerouting priority 0; policy accept; }
add chain ip nat INPUT { type nat hook input priority 0; policy accept; }
add chain ip nat OUTPUT { type nat hook output priority 0; policy accept; }
add chain ip nat POSTROUTING { type nat hook postrouting priority 0; policy accept; }
add rule ip nat POSTROUTING oifname eth0 ip saddr 10.121.6.6 ip daddr 192.168.5.77 counter masquerade
add rule ip nat POSTROUTING oifname eth0 ip saddr 10.121.6.6 ip daddr 10.10.210.11 counter masquerade
# Completed on Tue Jul 6 16:30:39 2021
-
關(guān)閉 iptables 服務(wù)&卸載 iptables 的相關(guān) nat 模塊,只有先關(guān)閉 iptables 的 nat 模塊,流量才能走 nft 的 nat
# 停止iptables服務(wù)
systemctl stop iptables.service
# 查看并確定服務(wù)是停止?fàn)顟B(tài)
systemctl status iptables.service
# 卸載nat模塊
modprobe -v -r ip6table_nat
modprobe -v -r iptable_nat
modprobe -v -r ip_nat_ftp
-
跑 nft 命令生成對應(yīng)的 nft 規(guī)則
# -i參數(shù)進(jìn)入交互模式
nft -i
# 執(zhí)行從iptables基礎(chǔ)規(guī)則轉(zhuǎn)換來的nftables規(guī)則
add table ip filter
add chain ip filter INPUT { type filter hook input priority 0; policy accept; }
add chain ip filter FORWARD { type filter hook forward priority 0; policy accept; }
add chain ip filter OUTPUT { type filter hook output priority 0; policy accept; }
add table ip nat
add chain ip nat PREROUTING { type nat hook prerouting priority 0; policy accept; }
add chain ip nat INPUT { type nat hook input priority 0; policy accept; }
add chain ip nat OUTPUT { type nat hook output priority 0; policy accept; }
add chain ip nat POSTROUTING { type nat hook postrouting priority 0; policy accept; }
add rule ip nat POSTROUTING oifname eth0 ip saddr 10.121.6.6 ip daddr 192.168.5.71 counter masquerade
add rule ip nat POSTROUTING oifname eth0 ip saddr 10.121.6.6 ip daddr 10.10.210.18 counter masquerade
# 增加測量流量規(guī)則
add rule filter INPUT counter
add rule nat POSTROUTING counter
# 查看目前的nft規(guī)則
nft list ruleset -a
table ip filter {
chain INPUT {
type filter hook input priority 0; policy accept;
counter packets 2530 bytes 583977 # handle 4
}
chain FORWARD {
type filter hook forward priority 0; policy accept;
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
}
}
table ip nat {
chain PREROUTING {
type nat hook prerouting priority 0; policy accept;
}
chain INPUT {
type nat hook input priority 0; policy accept;
}
chain OUTPUT {
type nat hook output priority 0; policy accept;
}
chain POSTROUTING {
type nat hook postrouting priority 0; policy accept;
counter packets 335 bytes 22534 # handle 11
oifname "eth0" ip saddr 10.121.6.6 ip daddr 192.168.5.77 counter packets 0 bytes 0 masquerade # handle 12
oifname "eth0" ip saddr 10.121.6.6 ip daddr 10.10.210.11 counter packets 0 bytes 0 masquerade # handle 13
}
}
-
測試
這邊 openvpn 的服務(wù)端配置的用戶虛擬 IP 網(wǎng)段是 10.121.0.0/16,subnet 拓?fù)淠J?/p>
客戶端(win10)連通 openvpn 后,ping 192.168.5.77 和 ping 10.10.210.11
通過查看流量可以看出流量通過了nft的POSTROUTING鏈的兩條規(guī)則:
table ip filter {
chain INPUT {
type filter hook input priority 0; policy accept;
counter packets 12481 bytes 4032765 # handle 4
}
chain FORWARD {
type filter hook forward priority 0; policy accept;
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
}
}
table ip nat {
chain PREROUTING {
type nat hook prerouting priority 0; policy accept;
}
chain INPUT {
type nat hook input priority 0; policy accept;
}
chain OUTPUT {
type nat hook output priority 0; policy accept;
}
chain POSTROUTING {
type nat hook postrouting priority 0; policy accept;
counter packets 2318 bytes 156490 # handle 11
oifname "eth0" ip saddr 10.121.6.6 ip daddr 192.168.5.77 counter packets 3 bytes 180 masquerade # handle 12
oifname "eth0" ip saddr 10.121.6.6 ip daddr 10.10.210.11 counter packets 3 bytes 180 masquerade # handle 13
}
}
這里可以看到
oifname "eth0" ip saddr 10.121.6.6 ip daddr 192.168.5.77 counter packets 3 bytes 180 masquerade # handle 12
oifname "eth0" ip saddr 10.121.6.6 ip daddr 10.10.210.11 counter packets 3 bytes 180 masquerade # handle 13
兩條規(guī)則有流量經(jīng)過,我們在 vpn 客戶端也得到了 ICMP 的正確返回。
因?yàn)閷?shí)際生產(chǎn)上需要先給大家統(tǒng)一分配,等穩(wěn)定后再優(yōu)化為細(xì)粒度的權(quán)限控制,最終的規(guī)則寫在 nftables 日志實(shí)踐一文中了,另外還有 openvpn 服務(wù)端的配置也沒放出來,未來寫上層權(quán)限控制平臺(tái)的時(shí)候,一并給出。
5. 單個(gè) iptables 規(guī)則翻譯成 nftables 規(guī)則
VPN 用戶的 權(quán)限主要依賴于防火墻的 masquerade,因此用 iptables 時(shí)候,用程序生成最多的就是地址偽裝規(guī)則,例如:
iptables -t nat -I POSTROUTING -s 10.121.0.3 -d 192.168.3.0/24 -o eth0 -p tcp --dport 80 -j MASQUERADE;
含義是虛擬 IP 為 10.121.0.3 的用戶到 192.168.3.0/24 網(wǎng)段的流量,偽裝成本機(jī)發(fā)送的流量。
因?yàn)橐呀?jīng)考慮完全用 nftables 替代 iptables 了所以要考慮用 nftables 來組織規(guī)則,該怎么寫呢?我嘗試執(zhí)行了自己猜測的語句:
nft add rule nat postrouting ip saddr 10.121.0.3 tcp sport 80 oifname eth0 counter masquerade
但是還是沒有對目標(biāo)地址進(jìn)行翻譯-d 192.168.3.0/24
于是改寫成規(guī)則:nft add rule nat postrouting ip saddr 10.121.0.3 tcp sport 80 ip daddr 192.168.3.0/24 tcp dport 80 oifname eth0 counter masquerade會(huì)有語法錯(cuò)誤:
同事買了一臺(tái) ubantu 安裝了翻譯工具apt install iptables-nftables-compat
翻譯出的句子如下:
iptables-translate -t nat -I postrouting -s 10.121.0.3 -d 192.168.3.0/24 -o eth0 -p tcp --dport 80 -j masquerade;
nft insert rule ip nat postrouting oifname eth0 ip saddr 10.121.0.3 ip daddr 192.168.3.0/24 tcp dport 80 counter jump masquerade
我的問題在于,在 centos7 內(nèi)核 5.12 的環(huán)境下 yum search 翻譯工具沒找到,沒想到去安裝一臺(tái) ubantu;其次語法上確實(shí)在官方文檔沒找到這么復(fù)雜的案例,因此沒遵循語法寫錯(cuò)了
6. 參考資料
-
Moving from iptables to nftables[1]
腳注
Moving from iptables to nftables: https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwiki.nftables.org%2Fwiki-nftables%2Findex.php%2FMoving_from_iptables_to_nftables


你可能還喜歡
點(diǎn)擊下方圖片即可閱讀

云原生是一種信仰 ??
關(guān)注公眾號
后臺(tái)回復(fù)?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具,只需一條命令,連 ssh 都不需要!

點(diǎn)擊 "閱讀原文" 獲取更好的閱讀體驗(yàn)!
發(fā)現(xiàn)朋友圈變“安靜”了嗎?

