前面在部署k8s時,都是直接關閉的防火墻。由于生產環境需要開啟防火墻,只能放行一些特定的端口, 簡單記錄一下過程。
1. firewall 與 iptables 的關系
1.1 防火墻(Firewall)
定義:
防火墻是網絡安全系統,用于監控和控制進出網絡的流量,基于預定義的規則允許或阻止數據包。它是廣義上的概念,不依賴具體工具。
功能:流量過濾(允許/拒絕), 網絡地址轉換(NAT),端口轉發,防御攻擊(如 DDoS)
1.2. iptables
定義:
iptables
是 Linux
內核中實現防火墻功能的具體工具,通過配置規則控制網絡流量。它是 Linux 系統上防火墻的底層實現之一。
核心機制:
- 表(
Tables
):按功能分類規則集,如filter
(過濾)、nat
(地址轉換)、mangle
(修改數據包頭)等。 - 鏈(
Chains
):規則生效的節點,如INPUT
(入站流量)、OUTPUT
(出站流量)、FORWARD
(轉發流量)。 - 規則(
Rules
):定義匹配條件和動作(如ACCEPT、DROP、REJECT
)。
1.3 兩者的關系
實現與工具:
防火墻是抽象的安全概念,iptables
是實現這一概念的具體工具。
iptables
通過操作 Linux
內核的 netfilter
框架(內核模塊)來執行防火墻功能。
- netfilter 是內核中的核心引擎,負責執行流量處理。
- iptables 是用戶空間的命令行工具,用于配置
netfilter
規則。 - nftables:新一代 Linux 防火墻工具,取代
iptables
,提供更簡潔的語法和更高性能。
2. 開啟 firewalld 遇到的問題
沒有在FORWARD
中放行端口。
利用iptables
或 內核版本更高的系統中使用ntf
在 INPUT
鏈中添加 需要放行的端口,
只放開了 INPUT
的端口,沒有放開 FORWARD
,因為pod id
是走的ipv4轉發, 導致pod ip
與 主機節點ip
不通,跨節點時,A節點的pod ip
與B節點 service
的 cluster ip/pod ip
也不通。
下面是一個基本的放行腳本:FORWARD
放行所有端口:
pod ip : 10.42.0.0/16 網段
cluster ip : 10.43.0.0/16 網段
#!/bin/bash# 定義允許訪問的節點IP及VIP(空格分隔)
service_ip_list=("172.10.9.2" ""172.10.9.3" ""172.10.9.4" )pod_cidr=10.42.0.0/16
service_cluster_cidr=10.43.0.0/16
# 對外端口
external_port_list="22, 80, 443, 30000-32767"# 節點之間互通端口
node_tcp_port_list="179, 2379, 2380, 6443, 10250, 10260"input_chain='filter_IN_public_allow'
forward_chain='filter_FWD_public_allow'# 添加新規則
function add_rules() {echo "[*] 添加新規則..."nft add rule inet firewalld $input_chain tcp dport { $external_port_list } acceptfor ip in "${service_ip_list[@]}"; doout=`ip addr | grep $ip`if [ -z "$out" ]; thennft add rule inet firewalld $input_chain ip saddr $ip tcp dport { $node_tcp_port_list } acceptnft add rule inet firewalld $forward_chain ip saddr $ip acceptfidonenft add rule inet firewalld $input_chain ip saddr $pod_cidr acceptnft add rule inet firewalld $input_chain ip saddr $service_cluster_cidr acceptnft add rule inet firewalld $forward_chain ip saddr $pod_cidr acceptnft add rule inet firewalld $forward_chain ip saddr $service_cluster_cidr acceptecho "[+] 規則添加完"
}
3. 排查過程
- 關閉防火墻后一切正常,可以確認是防火墻問題;
- 在INPUT 中放開所有的端口,發現問題依然存在;
- 在
FORWARD
中放開pod ip
和cluster ip
網段后立馬恢復。
可以確認是forward 影響的,之所以會想到這,是因為配置環境就設置了 net.ipv4.ip_forward = 1
那為什么會受轉發的影響呢?
4. 問題分析
Kubernetes
集群網絡有很多種實現,有很大一部分都用到了 Linux
網橋,按常理 pod IP
是一個橋接網絡,而linux bridge
是虛擬的二層轉發設備。 iptables
規則是對IPv4/IPv6/arp
三層的網絡的INPUT/OUTPUT/FORWARD
鏈進行過濾處理。同網段 pod
之間直接走的二層轉發,不用經過三層,那是什么把二層的流量轉到三層去了呢?
這是因為在配置環境時開啟了 net.bridge.bridge-nf-call-iptables = 1
這個內核參數。
net.bridge.bridge-nf-call-iptables
這個參數是Linux
內核中的一個網絡橋接模塊的配置參數。默認情況下,網橋工作在二層,也就是數據鏈路層,而iptables
的規則是在三層(網絡層)處理的,比如針對IP數據包的過濾。所以,默認情況下,網橋轉發的流量可能不會經過iptables
的FORWARD
鏈,因為FORWARD
鏈處理的是經過路由決策的三層轉發,而橋接的流量可能被認為是二層的不經過路由。
當設置這個參數為1時,系統會在橋接的流量處理過程中調用netfilter
的鉤子函數,這樣iptables
的規則就會被應用到橋接的流量上。也就是說,原本在二層轉發的數據包會被傳遞到三層的netfilter
框架中,從而被FORWARD
鏈或者其他鏈的規則處理。這樣用戶配置的iptables
規則就可以影響到橋接的流量了。
1. 默認行為(參數為0時)
網橋工作在數據鏈路層(二層),直接根據 MAC
地址轉發數據包。
不會觸發 iptables
的三層規則(如 FORWARD
鏈),因為 iptables
默認只處理經過路由決策(三層)的流量。
2. 設置 bridge-nf-call-iptables=1 后的行為
內核會將網橋的流量注入到 netfilter
框架中,使其經過 iptables
規則處理。
二層轉發的數據包會被以下 iptables
鏈處理:
- PREROUTING 鏈(在路由決策前)
- FORWARD 鏈(在轉發時)
- POSTROUTING 鏈(在路由決策后)
因此,FORWARD
鏈的規則會生效,可對橋接流量進行過濾或修改。
代碼跟蹤:
https://github.com/torvalds/linux/blob/master/net/bridge/br_netfilter_hooks.c
先注冊各種鉤子:
https://github.com/torvalds/linux/blob/9d7a0577c9db35c4cc52db90bc415ea248446472/net/bridge/br_netfilter_hooks.c#L1111
{
.....ret = nf_register_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops));if (ret)return NOTIFY_BAD;brnet->enabled = true;return NOTIFY_OK;
}
然后根據優先級執行子鉤子的回調函數:
會調用 br_nf_forward
,這就是為什么pod ip
在橋接下,iptables
的FORWARD
中沒有放行會導致 網絡不通的原因。
{.hook = br_nf_forward,.pf = NFPROTO_BRIDGE,.hooknum = NF_BR_FORWARD,.priority = NF_BR_PRI_BRNF,
},
https://github.com/torvalds/linux/blob/9d7a0577c9db35c4cc52db90bc415ea248446472/net/bridge/br_netfilter_hooks.c#L1068
https://github.com/torvalds/linux/blob/9d7a0577c9db35c4cc52db90bc415ea248446472/net/bridge/br_netfilter_hooks.c#L671
static unsigned int br_nf_forward_ip(struct sk_buff *skb,const struct nf_hook_state *state,u8 pf)
{
.....nf_bridge->physoutdev = skb->dev;NF_HOOK(pf, NF_INET_FORWARD, state->net, NULL, skb,brnf_get_logical_dev(skb, state->in, state->net),parent, br_nf_forward_finish);return NF_STOLEN;
}
5. k8s pod ip
1. Pod IP 的本質
Pod IP
是虛擬的:Kubernetes
為每個 Pod 分配一個集群內唯一的 IP(通常由 CNI 插件管理,如 Calico、Flannel、Cilium 、kube-router
等)。
二層(L2):Pod IP
在同一個子網內可以直接通過 MAC
地址通信(如同一節點的 Pod
)。
三層(L3):跨節點 Pod
通信需要經過路由或封裝(如 VXLAN、IPIP
)。
2. 為什么 Pod
流量會經過 iptables FORWARD
iptables
的 FORWARD
鏈負責處理 經過本機但目標不是本機 的流量(即“轉發”流量)。Pod
流量進入 FORWARD
鏈的典型場景:
(1) 跨節點 Pod 通信
當 Pod A(Node 1) 訪問 Pod B(Node 2):
流量從 Pod A 的虛擬網卡(如 veth)發出。
經過宿主機的網絡棧(因為 Pod 網絡是虛擬的,依賴宿主機路由)。
宿主機根據路由表將流量轉發到目標節點(通過 calico/flannel/cilium 等 CNI 插件)。
此時流量是“轉發”的(非本機流量),因此觸發 iptables FORWARD 鏈。
(2) Kubernetes 的 Service 和 NetworkPolicy
Service
的 kube-proxy
:默認使用 iptables
或 ipvs
實現負載均衡,可能修改 FORWARD
規則。
NetworkPolicy
:如果使用 Calico/Cilium
等插件,它們會通過 iptables
在 FORWARD
鏈中插入規則,實現 Pod
間的訪問控制(如 allow/deny
)。
(3) CNI 插件的工作機制
許多 CNI 插件(如 Calico)依賴 iptables 實現:
- MASQUERADE:對出集群的流量做 SNAT。
- FILTER:過濾非法流量。
- FORWARD:允許或拒絕跨節點流量。
6. 文件
6.1 /proc/sys/net/bridge/bridge-nf-call-iptables
作用
全局開關:控制所有橋接設備(如 docker0、cni0、vmbr0
)的流量是否經過宿主機的 iptables
規則(包括 FORWARD、INPUT、OUTPUT
等鏈)。
6.2 /sys/class/net//bridge/nf-call-iptables
作用
設備級開關:針對單個橋接設備(如 vmbr0、docker0
)控制其流量是否經過 iptables
。
優先級:比全局設置 (/proc/sys/net/bridge/bridge-nf-call-iptables
) 更高。
如果設備級設置為 1,即使全局為 0,該橋接設備的流量仍會經過 iptables
。
如果設備級設置為 0,即使全局為 1,該設備的流量也會繞過 iptables
。
用途
在需要精細控制的場景下使用(例如:某些橋接設備需要繞過 iptables 以提升性能)。
Proxmox VE 的 vmbr0
或自定義橋接網絡可能會用到此配置
參考:
https://imroc.cc/kubernetes/appendix/faq/why-enable-bridge-nf-call-iptables/
https://izsk.me/2021/08/18/Kubernetes-bridge-nf-call-iptables/