> 原文連接:CentOS 8 都發布了,你還不會用 nftables?linux
若是你沒有生活在上個世紀,而且是雲計算或相關領域的一名搬磚者,那你應該據說最近 CentOS 8 官方正式版已經發布了,CentOS
徹底遵照 Red Hat
的再發行政策,而且致力與上游產品在功能上徹底兼容。CentOS 8 主要改動和 RedHat Enterprise Linux 8 是一致的,基於 Fedora 28 和內核版本 4.18
,其中網絡方面的主要改動是**用 nftables
框架替代 iptables
框架做爲默認的網絡包過濾工具。**若是你尚未據說過 nftables,如今是時候學習一下了。shell
nftables 是一個 netfilter
項目,旨在替換現有的 {ip,ip6,arp,eb}tables 框架,爲 {ip,ip6}tables 提供一個新的包過濾框架、一個新的用戶空間實用程序(nft)和一個兼容層。它使用現有的鉤子、連接跟蹤系統、用戶空間排隊組件和 netfilter
日誌子系統。數組
nftables 主要由三個組件組成:內核實現、libnl netlink 通訊和 nftables 用戶空間。 其中內核提供了一個 netlink
配置接口以及運行時規則集評估,libnl
包含了與內核通訊的基本函數,用戶空間能夠經過 nft
和用戶進行交互。bash
本文主要介紹用戶空間命令行工具 nft
的用法。微信
nftables 和 iptables 同樣,由表(table)、鏈(chain)和規則(rule)組成,其中表包含鏈,鏈包含規則,規則是真正的 action。與 iptables 相比,nftables 主要有如下幾個變化:網絡
iptables
規則的佈局是基於連續的大塊內存的,即數組式佈局;而 nftables
的規則採用鏈式佈局。其實就是數組和鏈表的區別,好像 Kubernetes 用戶對此應該很興奮?iptables
大部分工做在內核態完成,若是要添加新功能,只能從新編譯內核;而 nftables
的大部分工做是在用戶態完成的,添加新功能很 easy,不須要改內核。iptables
有內置的鏈,即便你只須要一條鏈,其餘的鏈也會跟着註冊;而 nftables
不存在內置的鏈,你能夠按需註冊。因爲 iptables
內置了一個數據包計數器,因此即便這些內置的鏈是空的,也會帶來性能損耗。IPv4/IPv6
雙棧管理回到 nftables,先來看一下默認的規則集是啥:數據結構
$ nft list ruleset
啥也沒有,果真是沒有內置的鏈啊(若是你關閉了 firewalld
服務)。框架
nftables 的每一個表只有一個地址簇,而且只適用於該簇的數據包。表能夠指定五個簇中的一個:ssh
nftables簇 | iptables命令行工具 |
---|---|
ip | iptables |
ip6 | ip6tables |
inet | iptables和ip6tables |
arp | arptables |
bridge | ebtables |
inet
同時適用於 IPv4 和 IPv6 的數據包,即統一了 ip
和 ip6
簇,能夠更容易地定義規則,下文的示例都將採用 inet 簇。tcp
先建立一個新的表:
$ nft add table inet my_table
列出全部的規則:
$ nft list ruleset table inet my_table { }
如今表中尚未任何規則,須要建立一個鏈來保存規則。
鏈是用來保存規則的,和表同樣,鏈也須要被顯示建立,由於 nftables 沒有內置的鏈。鏈有如下兩種類型:
建立常規鏈:
$ nft add chain inet my_table my_utility_chain
建立基本鏈:
$ nft add chain inet my_table my_filter_chain { type filter hook input priority 0 \; }
\
)用來轉義,這樣 shell 就不會將分號解釋爲命令的結尾。priority
採用整數值,能夠是負數,值較小的鏈優先處理。列出鏈中的全部規則:
$ nft list chain inet my_table my_utility_chain table inet my_table { chain my_utility_chain { } } $ nft list chain inet my_table my_filter_chain table inet my_table { chain my_filter_chain { type filter hook input priority 0; policy accept; } }
有了表和鏈以後,就能夠建立規則了,規則由語句或表達式構成,包含在鏈中。下面添加一條規則容許 SSH 登陸:
$ nft add rule inet my_table my_filter_chain tcp dport ssh accept
add
表示將規則添加到鏈的末尾,若是想將規則添加到鏈的開頭,可使用 insert
。
$ nft insert rule inet my_table my_filter_chain tcp dport http accept
列出全部規則:
$ nft list ruleset table inet my_table { chain my_filter_chain { type filter hook input priority 0; policy accept; tcp dport http accept tcp dport ssh accept } }
注意 http 規則排在 ssh 規則的前面,由於以前使用了 insert
。
也能夠將規則插入到鏈的指定位置,有兩種方法:
一、 使用 index
來指定規則的索引。add
表示新規則添加在索引位置的規則後面,inser
表示新規則添加在索引位置的規則前面。index 的值從 0 開始增長。
$ nft insert rule inet my_table my_filter_chain index 1 tcp dport nfs accept $ nft list ruleset table inet my_table { chain my_filter_chain { type filter hook input priority 0; policy accept; tcp dport http accept tcp dport nfs accept tcp dport ssh accept } } $ nft add rule inet my_table my_filter_chain index 0 tcp dport 1234 accept $ nft list ruleset table inet my_table { chain my_filter_chain { type filter hook input priority 0; policy accept; tcp dport http accept tcp dport 1234 accept tcp dport nfs accept tcp dport ssh accept } }
index
相似於 iptables 的 -I
選項,但有兩點須要注意:一是 index 的值是從 0 開始的;二是 index 必須指向一個存在的規則,好比 nft insert rule … index 0
就是非法的。
二、 使用 handle
來指定規則的句柄。add
表示新規則添加在索引位置的規則後面,inser
表示新規則添加在索引位置的規則前面。handle
的值能夠經過參數 --handle
獲取。
$ nft --handle list ruleset table inet my_table { # handle 10 chain my_filter_chain { # handle 2 type filter hook input priority 0; policy accept; tcp dport http accept # handle 4 tcp dport 1234 accept # handle 6 tcp dport nfs accept # handle 5 tcp dport ssh accept # handle 3 } } $ nft add rule inet my_table my_filter_chain handle 4 tcp dport 1234 accept $ nft insert rule inet my_table my_filter_chain handle 5 tcp dport nfs accept $ nft --handle list ruleset table inet my_table { # handle 10 chain my_filter_chain { # handle 2 type filter hook input priority 0; policy accept; tcp dport http accept # handle 4 tcp dport 2345 accept # handle 8 tcp dport 1234 accept # handle 6 tcp dport 3456 accept # handle 9 tcp dport nfs accept # handle 5 tcp dport ssh accept # handle 3 } }
在 nftables 中,句柄值是固定不變的,除非規則被刪除,這就爲規則提供了穩定的索引。而 index
的值是可變的,只要有新規則插入,就有可能發生變化。通常建議使用 handle
來插入新規則。
也能夠在建立規則時就獲取到規則的句柄值,只須要在建立規則時同時加上參數 --echo
和 --handle
。
$ nft --echo --handle add rule inet my_table my_filter_chain udp dport 3333 accept add rule inet my_table my_filter_chain udp dport 3333 accept # handle 10
單個規則只能經過其句柄刪除,首先須要找到你想刪除的規則句柄:
$ nft --handle list ruleset table inet my_table { # handle 10 chain my_filter_chain { # handle 2 type filter hook input priority 0; policy accept; tcp dport http accept # handle 4 tcp dport 2345 accept # handle 8 tcp dport 1234 accept # handle 6 tcp dport 3456 accept # handle 9 tcp dport nfs accept # handle 5 tcp dport ssh accept # handle 3 udp dport 3333 accept # handle 10 } }
而後使用句柄值來刪除該規則:
$ nft delete rule inet my_table my_filter_chain handle 8 $ nft --handle list ruleset table inet my_table { # handle 10 chain my_filter_chain { # handle 2 type filter hook input priority 0; policy accept; tcp dport http accept # handle 4 tcp dport 1234 accept # handle 6 tcp dport 3456 accept # handle 9 tcp dport nfs accept # handle 5 tcp dport ssh accept # handle 3 udp dport 3333 accept # handle 10 } }
前面的示例都是列出了全部規則,咱們還能夠根據本身的需求列出規則的一部分。例如:
列出某個表中的全部規則:
$ nft list table inet my_table table inet my_table { chain my_filter_chain { type filter hook input priority 0; policy accept; tcp dport http accept tcp dport 1234 accept tcp dport 3456 accept tcp dport nfs accept tcp dport ssh accept udp dport 3333 accept } }
列出某條鏈中的全部規則:
$ nft list chain inet my_table my_other_chain table inet my_table { chain my_other_chain { udp dport 12345 log prefix "UDP-12345" } }
nftables
的語法原生支持集合,能夠用來匹配多個 IP 地址、端口號、網卡或其餘任何條件。
集合分爲匿名集合與命名集合,匿名集合比較適合用於未來不須要更改的規則。
例如,下面的規則容許來自源 IP 處於 10.10.10.123 ~ 10.10.10.231
這個區間內的主機的流量。
$ nft add rule inet my_table my_filter_chain ip saddr { 10.10.10.123, 10.10.10.231 } accept $ nft list ruleset table inet my_table { chain my_filter_chain { type filter hook input priority 0; policy accept; tcp dport http accept tcp dport nfs accept tcp dport ssh accept ip saddr { 10.10.10.123, 10.10.10.231 } accept } }
匿名集合的缺點是,若是須要修改集合,就得替換規則。若是後面須要頻繁修改集合,推薦使用命名集合。
以前的示例中添加的規則也能夠經過集合來簡化:
$ nft add rule inet my_table my_filter_chain tcp dport { http, nfs, ssh } accept
> iptables 能夠藉助 ipset
來使用集合,而 nftables 原生支持集合,因此不須要藉助 ipset
。
nftables 也支持命名集合,命名集合是能夠修改的。建立集合須要指定其元素的類型,當前支持的數據類型有:
ipv4_addr
: IPv4 地址ipv6_addr
: IPv6 地址ether_addr
: 以太網(Ethernet)地址inet_proto
: 網絡協議inet_service
: 網絡服務mark
: 標記類型先建立一個空的命名集合:
$ nft add set inet my_table my_set { type ipv4_addr \; } $ nft list sets table inet my_table { set my_set { type ipv4_addr } }
要想在添加規則時引用集合,可使用 @
符號跟上集合的名字。下面的規則表示將集合 my_set
中的 IP 地址添加到黑名單中。
$ nft insert rule inet my_table my_filter_chain ip saddr @my_set drop $ nft list chain inet my_table my_filter_chain table inet my_table { chain my_filter_chain { type filter hook input priority 0; policy accept; ip saddr @my_set drop tcp dport http accept tcp dport nfs accept tcp dport ssh accept ip saddr { 10.10.10.123, 10.10.10.231 } accept } }
向集合中添加元素:
$ nft add element inet my_table my_set { 10.10.10.22, 10.10.10.33 } $ nft list set inet my_table my_set table inet my_table { set my_set { type ipv4_addr elements = { 10.10.10.22, 10.10.10.33 } } }
若是你向集合中添加一個區間就會報錯:
$ nft add element inet my_table my_set { 10.20.20.0-10.20.20.255 } Error: Set member cannot be range, missing interval flag on declaration add element inet my_table my_set { 10.20.20.0-10.20.20.255 } ^^^^^^^^^^^^^^^^^^^^^^^
要想在集合中使用區間,須要加上一個 flag interval
,由於內核必須提早確認該集合存儲的數據類型,以便採用適當的數據結構。
建立一個支持區間的命名集合:
$ nft add set inet my_table my_range_set { type ipv4_addr \; flags interval $ nft add element inet my_table my_range_set { 10.20.20.0/24 } $ nft list set inet my_table my_range_set table inet my_table { set my_range_set { type ipv4_addr flags interval elements = { 10.20.20.0/24 } } }
> 子網掩碼錶示法會被隱式轉換爲 IP 地址的區間,你也能夠直接使用區間 10.20.20.0-10.20.20.255
來得到相同的效果。
命名集合也支持對不一樣類型的元素進行級聯,經過級聯操做符 .
來分隔。例如,下面的規則能夠一次性匹配 IP 地址、協議和端口號。
$ nft add set inet my_table my_concat_set { type ipv4_addr . inet_proto . inet_service \; } $ nft list set inet my_table my_concat_set table inet my_table { set my_concat_set { type ipv4_addr . inet_proto . inet_service } }
向集合中添加元素:
$ nft add element inet my_table my_concat_set { 10.30.30.30 . tcp . telnet }
在規則中引用級聯類型的集合和以前同樣,但須要標明集合中每一個元素對應到規則中的哪一個位置。
$ nft add rule inet my_table my_filter_chain ip saddr . meta l4proto . tcp dport @my_concat_set accept
這就表示若是數據包的源 IP、協議類型、目標端口匹配 10.30.30.30、tcp、telnet
時,nftables 就會容許該數據包經過。
匿名集合也可使用級聯元素,例如:
$ nft add rule inet my_table my_filter_chain ip saddr . meta l4proto . udp dport { 10.30.30.30 . udp . bootps } accept
如今你應該能體會到 nftables 集合的強大之處了吧。
> nftables 級聯類型的集合相似於 ipset 的聚合類型,例如 hash:ip,port
。
字典是 nftables 的一個高級特性,它可使用不一樣類型的數據並將匹配條件映射到某一個規則上面,而且因爲是哈希映射的方式,能夠完美的避免鏈式規則跳轉的性能開銷。
例如,爲了從邏輯上將對 TCP 和 UDP 數據包的處理規則拆分開來,可使用字典來實現,這樣就能夠經過一條規則實現上述需求。
$ nft add chain inet my_table my_tcp_chain $ nft add chain inet my_table my_udp_chain $ nft add rule inet my_table my_filter_chain meta l4proto vmap { tcp : jump my_tcp_chain, udp : jump my_udp_chain } $ nft list chain inet my_table my_filter_chain table inet my_table { chain my_filter_chain { ... meta nfproto ipv4 ip saddr . meta l4proto . udp dport { 10.30.30.30 . udp . bootps } accept meta l4proto vmap { tcp : jump my_tcp_chain, udp : jump my_udp_chain } } }
和集合同樣,除了匿名字典以外,還能夠建立命名字典:
$ nft add map inet my_table my_vmap { type inet_proto : verdict \; }
向字典中添加元素:
$ nft add element inet my_table my_vmap { 192.168.0.10 : drop, 192.168.0.11 : accept }
後面就能夠在規則中引用字典中的元素了:
$ nft add rule inet my_table my_filter_chain ip saddr vmap @my_vmap
在 nftables 中,每一個表都是一個獨立的命名空間,這就意味着不一樣的表中的鏈、集合、字典等均可以有相同的名字。例如:
$ nft add table inet table_one $ nft add chain inet table_one my_chain $ nft add table inet table_two $ nft add chain inet table_two my_chain $ nft list ruleset ... table inet table_one { chain my_chain { } } table inet table_two { chain my_chain { } }
有了這個特性,不一樣的應用就能夠在相互不影響的狀況下管理本身的表中的規則,而使用 iptables
就沒法作到這一點。
固然,這個特性也有缺陷,因爲每一個表都被視爲獨立的防火牆,那麼某個數據包必須被全部表中的規則放行,纔算真正的放行,即便 table_one
容許該數據包經過,該數據包仍然有可能被 table_two
拒絕。爲了解決這個問題,nftables 引入了優先級,priority
值越高的鏈優先級越低,因此 priority
值低的鏈比 priority
值高的鏈先執行。若是兩條鏈的優先級相同,就會進入競爭狀態。
以上全部示例中的規則都是臨時的,要想永久生效,咱們能夠將規則備份,重啓後自動加載恢復,其實 nftables 的 systemd
服務就是這麼工做的。
備份規則:
$ nft list ruleset > /root/nftables.conf
加載恢復:
$ nft -f /root/nftables.conf
在 CentOS 8 中,nftables.service
的規則被存儲在 /etc/nftables.conf
中,其中 include 一些其餘的示例規則,通常位於 /etc/sysconfig/nftables.conf
文件中,但默認會被註釋掉。
但願經過本文的講解,你能對 nftables 的功能和用法有所瞭解,固然本文只涉及了一些淺顯的用法,更高級的用法能夠查看 nftables 的官方 wiki
,或者坐等我接下來的文章。相信有了本文的知識儲備,你應該能夠愉快地使用 nftables 實現 Linux 的智能分流了,具體參考這篇文章:Linux全局智能分流方案。
掃一掃下面的二維碼關注微信公衆號,在公衆號中回覆◉加羣◉便可加入咱們的雲原生交流羣,和孫宏亮、張館長、陽明等大佬一塊兒探討雲原生技術