前面在部署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/