CentOS 8 都發布了,你還不會用 nftables?

> 原文連接: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 的用法。微信

1. nftables VS iptables

nftables 和 iptables 同樣,由表(table)、鏈(chain)和規則(rule)組成,其中表包含鏈,鏈包含規則,規則是真正的 action。與 iptables 相比,nftables 主要有如下幾個變化:網絡

  • iptables 規則的佈局是基於連續的大塊內存的,即數組式佈局;而 nftables 的規則採用鏈式佈局。其實就是數組和鏈表的區別,好像 Kubernetes 用戶對此應該很興奮?
  • iptables 大部分工做在內核態完成,若是要添加新功能,只能從新編譯內核;而 nftables 的大部分工做是在用戶態完成的,添加新功能很 easy,不須要改內核。
  • iptables 有內置的鏈,即便你只須要一條鏈,其餘的鏈也會跟着註冊;而 nftables 不存在內置的鏈,你能夠按需註冊。因爲 iptables 內置了一個數據包計數器,因此即便這些內置的鏈是空的,也會帶來性能損耗。
  • 簡化了 IPv4/IPv6 雙棧管理
  • 原生支持集合、字典和映射

回到 nftables,先來看一下默認的規則集是啥:數據結構

$ nft list ruleset

啥也沒有,果真是沒有內置的鏈啊(若是你關閉了 firewalld 服務)。框架

2. 建立表

nftables 的每一個表只有一個地址簇,而且只適用於該簇的數據包。表能夠指定五個簇中的一個:ssh

nftables簇 iptables命令行工具
ip iptables
ip6 ip6tables
inet iptables和ip6tables
arp arptables
bridge ebtables

inet 同時適用於 IPv4 和 IPv6 的數據包,即統一了 ipip6 簇,能夠更容易地定義規則,下文的示例都將採用 inet 簇。tcp

先建立一個新的表:

$ nft add table inet my_table

列出全部的規則:

$ nft list ruleset
table inet my_table {
}

如今表中尚未任何規則,須要建立一個鏈來保存規則。

3. 建立鏈

鏈是用來保存規則的,和表同樣,鏈也須要被顯示建立,由於 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;
        }
}

4. 建立規則

有了表和鏈以後,就能夠建立規則了,規則由語句或表達式構成,包含在鏈中。下面添加一條規則容許 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

5. 刪除規則

單個規則只能經過其句柄刪除,首先須要找到你想刪除的規則句柄:

$ 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
        }
}

6. 列出規則

前面的示例都是列出了全部規則,咱們還能夠根據本身的需求列出規則的一部分。例如:

列出某個表中的全部規則:

$ 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"
    }
}

7. 集合

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

8. 字典

字典是 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

9. 表與命名空間

在 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 值高的鏈先執行。若是兩條鏈的優先級相同,就會進入競爭狀態。

10. 備份與恢復

以上全部示例中的規則都是臨時的,要想永久生效,咱們能夠將規則備份,重啓後自動加載恢復,其實 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 文件中,但默認會被註釋掉。

11. 總結

但願經過本文的講解,你能對 nftables 的功能和用法有所瞭解,固然本文只涉及了一些淺顯的用法,更高級的用法能夠查看 nftables 的官方 wiki,或者坐等我接下來的文章。相信有了本文的知識儲備,你應該能夠愉快地使用 nftables 實現 Linux 的智能分流了,具體參考這篇文章:Linux全局智能分流方案

微信公衆號

掃一掃下面的二維碼關注微信公衆號,在公衆號中回覆◉加羣◉便可加入咱們的雲原生交流羣,和孫宏亮、張館長、陽明等大佬一塊兒探討雲原生技術

相關文章
相關標籤/搜索