iptables 以及 Go 操作庫
iptables
是 Linux 系統中的一個非常強大的防火牆工具,它可以用於過濾、修改、重定向和處理網絡數據包。iptables 是基於 Netfilter 框架的,Netfilter 是 Linux 內核的一部分,它提供了在內核級別處理網絡數據包的能力。
iptables 的主要功能包括:
-
數據包過濾:iptables 可以根據各種條件(如源 IP 地址、目的 IP 地址、協議類型、端口號等)來決定是否允許數據包通過。這是實現防火牆的基本功能。
-
網絡地址轉換(NAT):iptables 可以修改數據包的源 IP 地址或目的 IP 地址,這是實現網絡地址轉換(NAT)的基本功能。
-
數據包重定向和修改:iptables 可以將數據包重定向到其 a 他的 IP 地址或端口,或者修改數據包的其他屬性。
iptables 的規則是基於表和鏈的。表是規則的集合,鏈是表中的一個部分,它代表了數據包處理的一個階段。iptables 有五個預定義的表:filter
(用於數據包過濾)、nat
(用於網絡地址轉換)、mangle
(用於數據包修改)、raw
(用於配置不進行連接跟蹤的數據包)和security
(用於強制訪問控制)。iptables 是一個非常複雜和強大的工具,它需要深入的網絡知識和 Linux 系統知識才能有效地使用。然而,對於網絡管理員和系統管理員來說,學習和掌握 iptables 是非常有價值的,因爲它可以提供非常強大和靈活的網絡控制能力。
數據包過濾
iptables 的數據包過濾功能是通過 filter 表來實現的,這是 iptables 的默認表。filter 表有三個內置的鏈:INPUT、FORWARD 和 OUTPUT。
-
INPUT 鏈用於處理到達本機的數據包。
-
FORWARD 鏈用於處理經過本機轉發的數據包。
-
OUTPUT 鏈用於處理本機發送的數據包。
你可以在這些鏈上添加規則來過濾數據包。每個規則都有一個匹配條件和一個目標(動作)。當一個數據包滿足匹配條件時,就會執行相應的目標。
以下是一些基本的例子:
- 阻止來自特定 IP 地址的數據包:
iptables -A INPUT -s 192.168.1.100 -j DROP
這個命令在 INPUT 鏈上添加了一個規則,該規則會匹配所有源 IP 地址爲 192.168.1.100 的數據包,並將它們丟棄(DROP)。
- 只允許來自特定端口的數據包:
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 數據包都會被丟棄。
- 阻止所有的數據包:
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。
-
PREROUTING 鏈用於處理到達本機之前的數據包。
-
POSTROUTING 鏈用於處理離開本機之後的數據包。
-
OUTPUT 鏈用於處理本機生成的數據包。
以下是一些基本的例子:
- 端口轉發:
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
這個命令在 PREROUTING 鏈上添加了一個規則,該規則會匹配所有目的端口爲 80 的 TCP 數據包,並將它們重定向(REDIRECT)到端口 8080。這是實現端口轉發的一種方法。
- 源 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)的一種方法,通常用於在私有網絡中訪問公共網絡。
- 目的 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
表來實現的。
- 數據包重定向:數據包重定向主要是通過 nat 表的 REDIRECT 目標來實現的。REDIRECT 目標會將數據包的目的 IP 地址和 / 或目的端口改爲新的值。以下是一個例子:
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
這個命令在 PREROUTING 鏈上添加了一個規則,該規則會匹配所有目的端口爲 80 的 TCP 數據包,並將它們重定向到端口 8080。
- 數據包修改:數據包修改主要是通過 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。
-
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。
-
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