iptables 以及 Go 操作庫

iptables 是 Linux 系統中的一個非常強大的防火牆工具,它可以用於過濾、修改、重定向和處理網絡數據包。iptables 是基於 Netfilter 框架的,Netfilter 是 Linux 內核的一部分,它提供了在內核級別處理網絡數據包的能力。

iptables 的主要功能包括:

  1. 數據包過濾:iptables 可以根據各種條件(如源 IP 地址、目的 IP 地址、協議類型、端口號等)來決定是否允許數據包通過。這是實現防火牆的基本功能。

  2. 網絡地址轉換(NAT):iptables 可以修改數據包的源 IP 地址或目的 IP 地址,這是實現網絡地址轉換(NAT)的基本功能。

  3. 數據包重定向和修改:iptables 可以將數據包重定向到其 a 他的 IP 地址或端口,或者修改數據包的其他屬性。

iptables 的規則是基於表和鏈的。表是規則的集合,鏈是表中的一個部分,它代表了數據包處理的一個階段。iptables 有五個預定義的表:filter(用於數據包過濾)、nat(用於網絡地址轉換)、mangle(用於數據包修改)、raw(用於配置不進行連接跟蹤的數據包)和security(用於強制訪問控制)。iptables 是一個非常複雜和強大的工具,它需要深入的網絡知識和 Linux 系統知識才能有效地使用。然而,對於網絡管理員和系統管理員來說,學習和掌握 iptables 是非常有價值的,因爲它可以提供非常強大和靈活的網絡控制能力。

數據包過濾

iptables 的數據包過濾功能是通過 filter 表來實現的,這是 iptables 的默認表。filter 表有三個內置的鏈:INPUT、FORWARD 和 OUTPUT。

你可以在這些鏈上添加規則來過濾數據包。每個規則都有一個匹配條件和一個目標(動作)。當一個數據包滿足匹配條件時,就會執行相應的目標。

以下是一些基本的例子:

  1. 阻止來自特定 IP 地址的數據包:
iptables -A INPUT -s 192.168.1.100 -j DROP

這個命令在 INPUT 鏈上添加了一個規則,該規則會匹配所有源 IP 地址爲 192.168.1.100 的數據包,並將它們丟棄(DROP)。

  1. 只允許來自特定端口的數據包:
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -j DROP

這兩個命令在 INPUT 鏈上添加了兩個規則。第一個規則會匹配所有目的端口爲 22 的 TCP 數據包,並將它們接受(ACCEPT)。第二個規則會匹配所有 TCP 數據包,並將它們丟棄(DROP)。因爲 iptables 的規則是按順序處理的,所以這兩個規則的效果是隻允許來自端口 22 的數據包,其他的 TCP 數據包都會被丟棄。

  1. 阻止所有的數據包:
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP

這三個命令將 INPUT、FORWARD 和 OUTPUT 鏈的默認策略(P)設置爲 DROP。這意味着所有的數據包都會被丟棄,除非有規則明確允許它們。

你需要 root 權限才能運行 iptables 命令。

網絡地址轉換

iptables 的網絡地址轉換(NAT)功能是通過 nat 表來實現的。nat 表有三個內置的鏈:PREROUTING、POSTROUTING 和 OUTPUT。

以下是一些基本的例子:

  1. 端口轉發:
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

這個命令在 PREROUTING 鏈上添加了一個規則,該規則會匹配所有目的端口爲 80 的 TCP 數據包,並將它們重定向(REDIRECT)到端口 8080。這是實現端口轉發的一種方法。

  1. 源 NAT(SNAT):
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 192.168.1.100

這個命令在 POSTROUTING 鏈上添加了一個規則,該規則會匹配所有出口接口爲 eth0 的數據包,並將它們的源 IP 地址改爲 192.168.1.100。這是實現源 NAT(SNAT)的一種方法,通常用於在私有網絡中訪問公共網絡。

  1. 目的 NAT(DNAT):
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.100:8080

這個命令在 PREROUTING 鏈上添加了一個規則,該規則會匹配所有目的端口爲 80 的 TCP 數據包,並將它們的目的 IP 地址和端口改爲 192.168.1.100:8080。這是實現目的 NAT(DNAT)的一種方法,通常用於實現負載均衡和端口轉發。

數據包重定向和修改

iptables 的數據包重定向和修改功能主要是通過 nat 表和 mangle 表來實現的。

  1. 數據包重定向:數據包重定向主要是通過 nat 表的 REDIRECT 目標來實現的。REDIRECT 目標會將數據包的目的 IP 地址和 / 或目的端口改爲新的值。以下是一個例子:
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

這個命令在 PREROUTING 鏈上添加了一個規則,該規則會匹配所有目的端口爲 80 的 TCP 數據包,並將它們重定向到端口 8080。

  1. 數據包修改:數據包修改主要是通過 mangle 表來實現的。mangle 表可以用於修改數據包的各種屬性,如 TOS(服務類型)、TTL(生存時間)等。以下是一個例子:
iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TOS --set-tos Minimize-Delay

這個命令在 PREROUTING 鏈上添加了一個規則,該規則會匹配所有目的端口爲 80 的 TCP 數據包,並將它們的 TOS 設置爲 Minimize-Delay。

配置不進行連接跟蹤

raw 表在 iptables 中主要用於配置不進行連接跟蹤的數據包。連接跟蹤(connection tracking)是 Netfilter(iptables 的底層框架)的一個功能,它可以跟蹤網絡連接的狀態,並根據狀態來匹配數據包。

raw 表有兩個內置的鏈:PREROUTING 和 OUTPUT。

在 raw 表中,你可以使用 NOTRACK 目標來指定不進行連接跟蹤的數據包。以下是一個例子:

iptables -t raw -A PREROUTING -p tcp --dport 80 -j NOTRACK

這個命令在 PREROUTING 鏈上添加了一個規則,該規則會匹配所有目的端口爲 80 的 TCP 數據包,並將它們標記爲不進行連接跟蹤。

請注意,NOTRACK 目標在較新的 Linux 系統中已經被 CT 目標替代,你可以使用 CT 目標的 --notrack 選項來達到同樣的效果:

iptables -t raw -A PREROUTING -p tcp --dport 80 -j CT --notrack

安全

security 表在 iptables 中主要用於配置強制訪問控制(Mandatory Access Control,MAC)的安全策略。這個表通常與 SELinux(Security-Enhanced Linux)等安全模塊一起使用。

security 表有三個內置的鏈:INPUT、OUTPUT 和 FORWARD。

在 security 表中,你可以使用 SECMARK 和 CONNSECMARK 目標來設置數據包的安全標記(secmark)。以下是一個例子:

iptables -t security -A OUTPUT -p tcp --dport 22 -j SECMARK --selctx system_u:object_r:sshd_port_t:s0

這個命令在 OUTPUT 鏈上添加了一個規則,該規則會匹配所有目的端口爲 22 的 TCP 數據包,並將它們的安全標記設置爲 system_u:object_r:sshd_port_t:s0。

請注意,security 表的使用通常需要深入的 SELinux 知識,而且它只在啓用了 SELinux 的系統中有效。

同樣,你需要 root 權限才能運行 iptables 命令。如果你在運行命令時遇到權限問題,你可以使用 sudo 命令來獲取 root 權限。

iptables 在 k8s 中的應用

在 Kubernetes(K8s)中,iptables 主要用於實現服務發現和負載均衡。

Kubernetes 使用 iptables 的 NAT 表來將到達服務的 IP 地址和端口的數據包重定向到後端 Pod 的 IP 地址和端口。這是通過在 PREROUTING 和 OUTPUT 鏈上添加 DNAT 規則來實現的。

例如,假設你有一個服務,它的 IP 地址是 10.0.0.1,端口是 80,並且它有兩個後端 Pod,它們的 IP 地址分別是 172.17.0.2 和 172.17.0.3。Kubernetes 可能會添加如下的 iptables 規則:

iptables -t nat -A PREROUTING -p tcp -d 10.0.0.1 --dport 80 -j DNAT --to-destination 172.17.0.2:80
iptables -t nat -A PREROUTING -p tcp -d 10.0.0.1 --dport 80 -j DNAT --to-destination 172.17.0.3:80

這些規則會將到達 10.0.0.1:80 的 TCP 數據包重定向到 172.17.0.2:80 或 172.17.0.3:80。

此外,Kubernetes 還使用 iptables 的 filter 表來實現網絡策略。網絡策略可以用於控制 Pod 之間的網絡通信。這是通過在 FORWARD 鏈上添加 ACCEPT 或 DROP 規則來實現的。

請注意,Kubernetes 的 iptables 規則是由 kube-proxy 組件自動管理的,通常不需要手動修改。在較新的 Kubernetes 版本中,你還可以選擇使用 IPVS 或 eBPF 來替代 iptables。

使用 Go 操作庫配置 iptables

雖然你可以直接使用 iptables 命令來配置 iptables,但是有時候我們還是需要程序化操作 iptables,實現動態的調整 iptables 規則,這裏給你介紹一個 Go 庫: go-iptables[1], 它是由 coreos 公司出品,它對 iptables 命令做了封裝,可以在程序中調用。

package main

import (
 "fmt"

 "github.com/coreos/go-iptables/iptables"
)

func main() {
 tables, err := iptables.New()
 if err != nil {
  panic(err)
 }

 chains, err := tables.ListChains("nat")
 if err != nil {
  panic(err)
 }

 for _, chain := range chains {
  fmt.Println("chain:", chain)
  rules, err := tables.List("nat", chain)
  if err != nil {
   panic(err)
  }
  for _, r := range rules {
   fmt.Println("rule:", r)
  }
 }

 err = tables.Insert("nat""PREROUTING", 1, "-p""udp""--dport""80""-j""REDIRECT""--to-ports""8080")
 if err != nil {
  panic(err)
 }

 err = tables.Append("nat""PREROUTING""-p""udp""--dport""81""-j""REDIRECT""--to-ports""8081")
 if err != nil {
  panic(err)
 }

 ok, err := tables.Exists("nat""PREROUTING""-p""udp""--dport""80""-j""REDIRECT""--to-ports""8080")
 if err != nil {
  panic(err)
 }
 fmt.Println("exist:", ok)
 ok, err = tables.Exists("nat""PREROUTING""-p""udp""--dport""90""-j""REDIRECT""--to-ports""8090")
 if err != nil {
  panic(err)
 }
 fmt.Println("exist:", ok)

 err = tables.Delete("nat""PREROUTING""-p""udp""--dport""80""-j""REDIRECT""--to-ports""8080")
 if err != nil {
  panic(err)
 }
 ok, err = tables.Exists("nat""PREROUTING""-p""udp""--dport""80""-j""REDIRECT""--to-ports""8080")
 if err != nil {
  panic(err)
 }
 fmt.Println("exist:", ok)

 err = tables.ClearChain("nat""PREROUTING")
 if err != nil {
  panic(err)
 }
 ok, err = tables.Exists("nat""PREROUTING""-p""udp""--dport""80""-j""REDIRECT""--to-ports""8008")
 if err != nil {
  panic(err)
 }
 fmt.Println("exist:", ok)

}

可以看到,它提供了增刪改查的方法,可以方便的操作 iptables。

參考資料

[1]

go-iptables: https://github.com/coreos/go-iptables

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/yAmwqgGunLI7WL5NrjG9vw