1. 项目概述:iptables不是“命令”,而是一套运行在Linux内核里的规则调度系统
很多人第一次接触 iptables,是在某篇教程里看到一句
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
,然后照着敲完就以为自己“配好了防火墙”。结果第二天发现 SSH 连不上了,或者 Docker 容器突然无法访问外部网络,再查日志全是
connection refused
或
no route to host
。这时候才意识到:iptables 不是开关,不是配置文件,更不是“加一条规则就生效”的简单工具——它是一套嵌入在 Linux 网络协议栈深处的、实时拦截并决策每一个数据包走向的
内核级策略引擎
。
我从 2012 年开始在 IDC 机房维护 CentOS 6 的物理服务器集群,后来转做云原生基础设施,亲手调试过上万台运行着不同内核版本(2.6.32 到 6.8)的 Linux 节点。最深的体会是:
90% 的 iptables 故障,根源不在规则写错,而在于对 netfilter 架构缺乏基本认知
。比如你执行
iptables -L
看到一堆规则,却不知道这些规则实际挂在哪个 hook 点;你用
-t nat
做端口转发,却没意识到 PREROUTING 和 POSTROUTING 的触发时机差了整整一个路由决策过程;你给容器加了
-j DOCKER-USER
链,却完全不清楚这个链在 netfilter 中的位置比
INPUT
还靠前——这些都不是“命令不熟”的问题,而是对底层机制的误判。
本文标题“How the Iptables Firewall Works”直指核心:我们不教你怎么记命令参数,而是带你钻进内核网络栈,看清数据包从网卡进来、经过哪些关卡、被哪条规则拦下、又如何被改写或放行的完整路径。你会真正理解为什么
iptables -t nat -A PREROUTING
能做 DNAT,而
iptables -t filter -A INPUT
却永远抓不到被 DNAT 后的目标端口;为什么
docker0
网桥接口上的
iptables: no chain/target/match by that name
错误,往往意味着内核模块
iptable_nat
根本没加载;为什么在 IPv6 环境下盲目复制 IPv4 的 iptables 规则,会导致整个双栈服务不可用——因为
ip6tables
对应的是另一套独立的 netfilter hook 实例,连链名都得重新定义。
这篇文章适合三类人:一是刚接触 Linux 网络运维的工程师,想摆脱“抄命令—出问题—删规则—重来”的循环;二是正在搭建双栈(IPv4/IPv6)服务的开发者,需要确保防火墙策略在两种协议下行为一致;三是 Docker/Kubernetes 用户,必须搞懂容器网络与宿主机 iptables 的耦合逻辑,否则一升级内核或换发行版,网络就断。全文所有解释均基于 Linux 内核源码(以 v5.10 为主干)、
netfilter
官方文档及多年线上排障实录,不讲虚概念,只说真实数据流怎么走、规则在哪生效、哪里最容易踩坑。
2. 核心架构拆解:netfilter 是骨架,iptables 是皮肤,规则链是流水线工位
2.1 netfilter:内核网络栈里的“交通指挥中心”
iptables 的本质,是用户空间对 netfilter 框架 的一套操作接口。netfilter 不是某个模块,而是 Linux 内核中预埋在 IPv4/IPv6 协议栈关键路径上的 5 个钩子函数(hook points) 。它们像高速公路上的收费站,每个数据包只要经过对应路径,就必须停靠、接受检查、按指令行动。这五个 hook 点在 IPv4 和 IPv6 中各自独立存在,但位置和作用完全对称:
- NF_INET_PRE_ROUTING :数据包刚从网卡进入内核, 尚未进行路由决策 。这是 DNAT(目标地址转换)发生的唯一位置,因为只有在这里,原始目的 IP 还没被查路由表,才能安全地把它改成另一个地址。
-
NF_INET_LOCAL_IN
:数据包目的地是本机(即路由决策后判定为
lo或本机 IP),准备交给上层协议栈(如 TCP/UDP)。这是INPUT链的挂载点,所有发给本机的服务(SSH、Nginx、Redis)都在这里被过滤。 -
NF_INET_FORWARD
:数据包目的地不是本机,需要转发给其他机器(典型于路由器、Docker bridge、K8s CNI)。这是
FORWARD链的挂载点,容器间通信、跨节点流量、NAT 网关的核心控制区。 -
NF_INET_LOCAL_OUT
:本机主动发出的数据包,在进入路由决策前。这是
OUTPUT链的挂载点,控制本机程序向外发起连接的行为(比如禁止 curl 访问某些域名)。 - NF_INET_POST_ROUTING :数据包即将离开本机(无论发往本地还是转发), 路由决策已完成,出口设备已确定 。这是 SNAT(源地址转换)和 MASQUERADE 发生的位置,因为此时出口 IP 已知,才能把源地址替换成该接口的 IP。
提示:
NF_INET_前缀中的INET表示 IPv4,对应 IPv6 的 hook 名为NF_INET6_*,但调用逻辑和挂载方式完全一致。这也是为什么iptables(IPv4)和ip6tables(IPv6)必须分开管理——它们操作的是两套物理隔离的 hook 实例。
2.2 iptables:用户空间的“规则翻译器”与“策略分发器”
iptables 本身不处理任何数据包。它的核心工作只有两件:
把人类可读的规则(如
-p tcp --dport 80 -j ACCEPT
)编译成内核能识别的二进制结构体,并通过
setsockopt()
系统调用,把规则批量注入到 netfilter 对应的 hook 链中
。你可以把它想象成一个“翻译官+快递员”:前端接收你的命令,后端把指令精准投递到内核指定的内存地址。
iptables 的规则组织采用 表(table)→ 链(chain)→ 规则(rule) 三级结构,但这个结构完全是逻辑划分, 物理上所有规则都存储在内核的同一块内存池中,只是按 hook 类型和优先级排序 。常见表的作用如下:
| 表名 | 主要用途 | 关键 hook 点 | 典型场景 |
|---|---|---|---|
filter
| 包过滤(允许/拒绝) |
INPUT
,
FORWARD
,
OUTPUT
| 阻止恶意 IP、限制端口访问、白名单控制 |
nat
| 地址转换(DNAT/SNAT) |
PREROUTING
,
OUTPUT
,
POSTROUTING
| 端口映射(80→8080)、内网共享上网(MASQUERADE)、容器端口暴露 |
mangle
| 修改包头字段(TTL、TOS、MARK) | 所有 5 个 hook | QoS 流量标记、修改 TTL 防止 traceroute、给特定流量打 MARK 供后续策略使用 |
raw
| 绕过连接跟踪(conntrack) |
PREROUTING
,
OUTPUT
| 高性能场景下禁用 conntrack(如负载均衡器),避免状态表溢出 |
注意:“表”不是独立的处理流程,而是规则分类标签。一个数据包只会经过它所属 hook 的所有表中对应链的规则。例如,一个发往本机的 HTTP 请求,会依次经过
raw/PREROUTING→mangle/PREROUTING→nat/PREROUTING→filter/INPUT→mangle/INPUT→filter/INPUT(注意mangle和filter在同一 hook 点会按顺序执行)。这种设计保证了功能解耦,但也要求你必须清楚规则在哪个表里生效。
2.3 链(Chain):规则执行的“有序流水线”
链不是容器,而是规则的
有序列表
。每条规则包含匹配条件(match)和动作(target)。数据包从链首开始逐条匹配,一旦命中,立即执行 target 动作;若未命中,则继续下一条;若遍历完所有规则都未命中,则执行链的
默认策略(policy)
,通常是
ACCEPT
或
DROP
。
关键点在于:
链的默认策略只对“未匹配任何规则”的包生效,而不是“最后一条规则之后”
。这意味着如果你在
INPUT
链末尾写了
-j DROP
,那它就是一条显式规则,会拦截所有前面没被放行的包;而
iptables -P INPUT DROP
是设置整个链的兜底策略,效果相同但机制不同——前者是规则,后者是链属性。
更易被忽视的是
自定义链(user-defined chain)
。它不是独立的处理单元,而是主链中的一个跳转点(
-j CHAIN_NAME
)。当数据包跳入自定义链时,会从该链第一条规则开始匹配,匹配完后
自动返回到主链的下一条规则
(除非 target 显式指定
RETURN
或终止动作)。Docker 创建的
DOCKER-USER
链就是典型应用:它被插入在
filter/FORWARD
链的最前端,让你能在 Docker 自动添加的规则之前,先做自己的访问控制。
3. 数据包流转全路径解析:从网卡到应用,iptables 在哪一刻出手?
3.1 IPv4 下一个 HTTP 请求的完整旅程(客户端→本机 Nginx)
假设一台 Ubuntu 22.04 服务器(IP 192.168.1.100)运行着 Nginx,监听 80 端口。外部客户端(10.0.0.5)发送一个 HTTP GET 请求。我们追踪这个数据包在内核中的每一步,明确 iptables 规则何时介入:
Step 1:网卡收包,进入 PRE_ROUTING
-
数据包到达 eth0 接口,内核将其送入
NF_INET_PRE_ROUTINGhook。 -
此时
nat/PREROUTING链规则开始匹配。如果存在-t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080,则目标端口被重定向,包头dport改为 8080。 -
raw/PREROUTING和mangle/PREROUTING链也可能在此刻修改包头(如设置CT目标禁用连接跟踪)。
Step 2:路由决策(Routing Decision)
-
内核查询路由表(
ip route show),判断该包目的地是否为本机。由于目的 IP 是 192.168.1.100,匹配local路由,决定将包送往本机协议栈。 -
关键分叉点
:如果目的 IP 是 192.168.1.200(另一台内网机器),则进入
FORWARD流程,不再走INPUT。
Step 3:进入 LOCAL_IN,触发 INPUT 链
-
数据包被送入
NF_INET_LOCAL_INhook,filter/INPUT链开始匹配。 -
规则
-A INPUT -p tcp --dport 80 -j ACCEPT成功匹配,target 为ACCEPT,包被放行。 -
如果此处没有匹配规则,且
INPUT链 policy 为DROP,则包在此刻被丢弃,Nginx 根本收不到。
Step 4:交付上层协议栈
- 包被传递给 TCP 子系统,最终由内核 socket 缓冲区交给 Nginx 进程的监听 socket。
-
注意
:
mangle/INPUT和filter/INPUT在此 hook 点都会执行,但mangle可以修改包内容(如 TTL),filter只能决定放行或丢弃。
Step 5:Nginx 响应,触发 OUTPUT 链
- Nginx 构造响应包,源 IP=192.168.1.100,目的 IP=10.0.0.5。
-
包首先经过
NF_INET_LOCAL_OUThook,filter/OUTPUT和mangle/OUTPUT链检查。 -
若有
-A OUTPUT -d 10.0.0.5 -j DROP,响应包在此被拦下,客户端收不到回复。
Step 6:响应包离开,经过 POST_ROUTING
-
响应包经路由决策,确定从 eth0 发出,进入
NF_INET_POST_ROUTINGhook。 -
nat/POST_ROUTING链可能执行 SNAT(如-t nat -A POST_ROUTING -s 192.168.1.0/24 -j MASQUERADE),将源 IP 改为 eth0 的公网 IP。
实操心得:我曾在线上遇到一个诡异问题——Nginx 日志显示请求到达,但客户端始终超时。抓包发现响应包在
POST_ROUTING被iptables -t nat -A POST_ROUTING -o eth0 -j SNAT --to-source 203.0.113.1规则修改了源 IP,而该 IP 并未在运营商备案,导致回程包被防火墙丢弃。解决方法是改用MASQUERADE(自动取接口 IP)或确保 SNAT IP 合法。这说明POST_ROUTING的修改直接影响回程路径,必须与网络拓扑严格匹配。
3.2 IPv6 下的双栈服务:为什么不能直接复制 IPv4 规则?
IPv6 的 netfilter hook 结构与 IPv4 完全一致,但
ip6tables
是独立的命令,操作的是
NF_INET6_*
hook。这意味着:
-
iptables -A INPUT -p tcp --dport 22 -j ACCEPT对 IPv6 流量完全无效 。 -
你必须单独执行
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT。 -
更麻烦的是,
ip6tables默认策略常为ACCEPT,而很多管理员只配了 IPv4 规则,导致 IPv6 端口意外暴露。
一个典型双栈 Nginx 服务器,其完整规则集应为:
# IPv4 规则
iptables -P INPUT DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# IPv6 规则(必须单独配置)
ip6tables -P INPUT DROP
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
常见陷阱:
ip6tables的state模块依赖nf_conntrack_ipv6内核模块。如果系统未加载(lsmod | grep nf_conntrack_ipv6为空),--state ESTABLISHED规则会失效,导致所有新连接被DROP。解决方案是modprobe nf_conntrack_ipv6并加入/etc/modules。这解释了为什么有些服务器 IPv4 正常、IPv6 全部不通——根本不是 DNS 或路由问题,而是连接跟踪模块缺失。
3.3 Docker 容器网络:iptables 如何成为容器流量的“隐形守门人”
Docker 启动时,会自动创建
docker0
网桥,并在
iptables
中插入大量规则。理解这些规则,是排查容器网络故障的关键。
核心规则链关系:
-
filter/FORWARD链开头插入-j DOCKER-USER(用户可自定义) -
DOCKER-USER之后是-j DOCKER(Docker 自建链) -
DOCKER链包含所有容器端口映射规则,如-A DOCKER ! -i docker0 -o docker0 -p tcp -m tcp --dport 8080 -j ACCEPT -
nat/PREROUTING插入-j DOCKER,将宿主机端口(如 8080)DNAT 到容器 IP(如 172.17.0.2:80)
典型故障
iptables: no chain/target/match by that name
分析:
这个错误通常出现在
docker0
相关规则中,根本原因有三:
-
内核模块未加载
:
iptable_nat(IPv4 NAT)或ip6table_nat(IPv6 NAT)未加载。执行modprobe iptable_nat即可修复。 -
Docker 服务未启动
:
DOCKER链由 Docker daemon 创建,若 daemon 崩溃或未启动,链不存在。 -
规则被手动清空
:执行
iptables -F会清空所有链,包括DOCKER和DOCKER-USER,导致容器端口映射失效。
实操心得:我在一次 Kubernetes 节点升级后遇到此错误。排查发现,新内核(5.15)默认禁用了
CONFIG_IP_NF_TARGET_REDIRECT,导致REDIRECTtarget 不可用。解决方案是重新编译内核启用该选项,或改用DNAT+SNAT组合。这说明 iptables 的 target 依赖具体内核配置,不能假设所有发行版都支持全部功能。
4. 实战配置详解:从基础防护到双栈容器化部署
4.1 基础服务器防火墙:5 条规则构建最小安全边界
不要一上来就抄几十条规则。一个生产 Web 服务器,最核心的防护只需 5 条
iptables
+ 5 条
ip6tables
规则,覆盖 95% 的攻击面:
# 【IPv4】基础防护(保存到 /etc/iptables/rules.v4)
*filter
:INPUT DROP [0:0] # 默认拒绝所有入站
:FORWARD DROP [0:0] # 默认拒绝转发(除非是网关)
:OUTPUT ACCEPT [0:0] # 出站默认允许(可按需收紧)
-A INPUT -i lo -j ACCEPT # 允许本地环回
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 允许已建立连接的回程包
-A INPUT -p icmp --icmp-type echo-request -m limit --limit 5/sec -j ACCEPT # 限速 ping
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name ssh --set -j ACCEPT # SSH 新连接
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name ssh --update --seconds 60 --hitcount 4 -j DROP # 60秒内超4次新连接则拉黑
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
COMMIT
# 【IPv6】同步配置(保存到 /etc/iptables/rules.v6)
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT # IPv6 必须允许 ICMPv6(邻居发现、路径 MTU 发现)
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name ssh6 --set -j ACCEPT
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name ssh6 --update --seconds 60 --hitcount 4 -j DROP
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
COMMIT
关键参数解析:
-
--limit 5/sec:限制 ICMP 请求速率为每秒 5 个,防 ping 泛洪。 -
recent --name ssh --set:将新 SSH 连接的源 IP 加入名为ssh的最近访问列表。 -
recent --update --seconds 60 --hitcount 4:检查该 IP 在 60 秒内是否已出现 4 次,是则丢弃。这是轻量级防暴力破解。 -
ipv6-icmp:IPv6 的 ICMP 类型与 IPv4 不同,ping6使用echo-request,但邻居发现(NDP)依赖neighbor-solicitation等类型,必须全开,否则 IPv6 地址无法解析。
注意:
recent模块依赖xt_recent内核模块,部分精简内核可能未编译。若报错iptables: No chain/target/match by that name,请先modprobe xt_recent。这是比 fail2ban 更底层、更低开销的防护方式。
4.2 端口转发实战:让内网服务对外可见的三种模式
场景 :公司内网有一台测试服务器(192.168.1.50:8080),需通过公网服务器(203.0.113.10)的 8080 端口访问。
模式一:DNAT + SNAT(标准网关模式)
# 在公网服务器上执行
# 1. 开启内核 IP 转发
echo 1 > /proc/sys/net/ipv4/ip_forward
# 2. DNAT:将入站 8080 请求目标改为内网服务器
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.50:8080
# 3. SNAT:将转发出去的包源地址改为公网服务器 IP,确保回程路径正确
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d 192.168.1.50 -j SNAT --to-source 203.0.113.10
# 4. 允许 FORWARD 链放行(否则被默认 DROP)
iptables -A FORWARD -p tcp -d 192.168.1.50 --dport 8080 -j ACCEPT
模式二:MASQUERADE(动态 IP 环境)
当公网服务器使用 DHCP 获取 IP(IP 不固定)时,
SNAT
失效,必须用
MASQUERADE
:
# 替换上面的 SNAT 步骤
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d 192.168.1.50 -j MASQUERADE
MASQUERADE
会自动读取出口接口的当前 IP,无需硬编码。
模式三:REDIRECT(本机端口映射)
若目标服务就在本机(如 Docker 容器),用
REDIRECT
更高效:
# 将本机 80 端口请求重定向到 8080
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080
REDIRECT
仅修改目标端口,不改变 IP,且只在
PREROUTING
和
OUTPUT
中有效。
实操心得:我曾用
DNAT将 443 端口映射到内网 HTTPS 服务,但客户端提示证书域名不匹配。原因是 SSL/TLS 握手发生在应用层,DNAT 只改 IP/端口,不改 SNI(Server Name Indication)扩展中的域名。解决方案是:要么在内网服务上配置正确的证书,要么用反向代理(如 Nginx)做 TLS 终止。这说明网络层转发无法解决应用层语义问题。
4.3 双栈 Nginx 服务器:一份配置同时守护 IPv4 和 IPv6
现代 Web 服务必须支持双栈。Nginx 配置本身支持
listen [::]:80
,但防火墙必须同步放开:
# /etc/nginx/sites-available/default
server {
listen 80;
listen [::]:80; # IPv6 监听
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
对应的 iptables 规则必须确保:
-
iptables放开 IPv4 的 80/443 端口; -
ip6tables放开 IPv6 的 80/443 端口; -
ip6tables必须允许ipv6-icmp,否则 IPv6 客户端无法完成地址解析(NDP)。
验证双栈连通性:
# 测试 IPv4
curl -4 http://example.com
# 测试 IPv6(需客户端支持 IPv6)
curl -6 http://example.com
# 查看 Nginx 日志中的真实 IP(确认 IPv6 地址被正确记录)
tail -f /var/log/nginx/access.log
# 应看到类似 "2001:db8::1" 的 IPv6 地址,而非 "::ffff:192.168.1.5"
常见问题:
curl -6失败,但ping6 example.com成功。这通常是因为 Nginx 的listen [::]:80配置了,但ip6tables拦截了 80 端口,或net.ipv6.conf.all.forwarding=0导致内核不转发 IPv6 包。检查sysctl net.ipv6.conf.all.forwarding,若为 0 则echo 1 > /proc/sys/net/ipv6/conf/all/forwarding。
5. 故障排查与避坑指南:线上环境最常遇到的 12 个问题
5.1 问题速查表:症状、原因、验证命令、解决方案
| 症状 | 可能原因 | 验证命令 | 解决方案 |
|---|---|---|---|
| SSH 连接后立即断开 |
INPUT
链中缺少
ESTABLISHED,RELATED
规则,导致 TCP ACK 包被 DROP
|
tcpdump -i any port 22
查看是否有 SYN-ACK 发出但无 ACK 回复
|
添加
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
| Docker 容器无法访问外网 |
FORWARD
链 policy 为
DROP
,且未放行
docker0
相关流量
|
iptables -L FORWARD -n | grep docker
|
添加
-A FORWARD -i docker0 -o eth0 -j ACCEPT
和
-A FORWARD -i eth0 -o docker0 -m state --state RELATED,ESTABLISHED -j ACCEPT
|
iptables: no chain/target/match by that name
|
所需内核模块未加载(
iptable_nat
,
xt_recent
,
xt_conntrack
)
| `lsmod | grep -E "(iptable | xt_)` |
| IPv6 客户端无法访问 Nginx |
ip6tables
未配置,或
ipv6-icmp
被阻断
|
ip6tables -L INPUT -n
;
ping6 -c 3 example.com
|
确保
ip6tables
有
ACCEPT
规则,且
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
|
curl -6
超时,但
ping6
正常
|
内核 IPv6 转发关闭,或
ip6tables
拦截了 80 端口
|
sysctl net.ipv6.conf.all.forwarding
;
ip6tables -L INPUT -n | grep :80
|
sysctl -w net.ipv6.conf.all.forwarding=1
;
ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
|
| 容器端口映射后,宿主机 localhost 无法访问 |
DOCKER-USER
或
DOCKER
链未放行
lo
接口
|
iptables -L DOCKER-USER -n
|
添加
-A DOCKER-USER -i lo -j ACCEPT
|
iptables -L
显示规则,但不生效
|
规则在错误的表中(如
filter
规则写在
nat
表)
|
iptables -t nat -L -n
;
iptables -t filter -L -n
|
确认
-t
参数,
-t nat
用于 DNAT/SNAT,
-t filter
用于 ACCEPT/DROP
|
| 重启后规则丢失 | iptables 规则未持久化 |
iptables-save > /dev/null
是否报错
|
Ubuntu:
apt install iptables-persistent
;CentOS:
service iptables save
|
docker0
网桥无 IP,容器无法通信
|
docker0
接口未分配 IP,或
br_netfilter
模块未加载
|
ip addr show docker0
;
lsmod | grep br_netfilter
|
ip addr add 172.17.0.1/16 dev docker0
;
modprobe br_netfilter
|
checking media presence. media present. start pxe over ipv4
循环
| BIOS/UEFI 设置中 PXE 启动优先级过高,或网卡驱动异常 |
进入 BIOS 关闭
Network Stack Enable
或调整启动顺序
|
临时拔掉网线,或在 GRUB 启动时按
e
编辑内核参数,添加
net.ifnames=0 biosdevname=0
|
redis
无法监听 IPv6 地址
|
Redis 配置中
bind
指令未包含
::1
,或
ip6tables
拦截
|
redis-cli -h ::1 ping
;
ip6tables -L INPUT -n | grep 6379
|
bind 127.0.0.1 ::1
;
ip6tables -A INPUT -p tcp --dport 6379 -j ACCEPT
|
virtualbox
IPv6 出站失败
|
VirtualBox 网络适配器未启用 IPv6,或宿主机
ip6tables
拦截
|
vboxmanage list vms
;
ip6tables -L OUTPUT -n
|
VirtualBox 设置 → 网络 → 高级 → 网卡类型选
Intel PRO/1000 MT Desktop
;
ip6tables -A OUTPUT -o vboxnet0 -j ACCEPT
|
5.2 独家避坑技巧:那些文档里不会写的细节
技巧一:用
iptables -S
替代
-L
查看原始规则
iptables -L
会格式化输出,隐藏
-m state
等模块参数,且不显示规则编号。而
iptables -S
输出的是可直接复用的命令格式,带完整参数,是调试和备份的黄金命令:
# 查看 INPUT 链所有规则的原始命令形式
iptables -S INPUT
# 输出示例:
# -A INPUT -i lo -j ACCEPT
# -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
技巧二:
-C
参数验证规则是否存在,避免重复添加
在脚本中动态添加规则前,先用
-C
检查,避免因重复执行导致规则堆积:
# 安全添加 SSH 规则
if ! iptables -C INPUT -p tcp --dport 22 -j ACCEPT 2>/dev/null; then
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
fi
技巧三:
-w
参数防止并发修改冲突
当多个脚本或服务(如 Docker、firewalld)同时操作 iptables 时,
iptables
命令可能因锁竞争失败。添加
-w
参数会让命令等待锁释放:
# 等待最多 5 秒获取锁
iptables -w 5 -A INPUT -p tcp --dport 8080 -j ACCEPT
技巧四:
LOG
target 记录可疑流量,比
DROP
更有价值
与其直接丢弃,不如先记录再分析:
# 在 DROP 规则前加 LOG
iptables -A INPUT -p tcp --dport 23 -j LOG --log-prefix "IPTABLES-TELNET-DROP: "
iptables -A INPUT -p tcp --dport 23 -j DROP
日志会出现在
/var/log/kern.log
或
journalctl -k
中,帮助你发现扫描行为。
技巧五:
iptables-restore
比逐条
iptables -A
更可靠
批量加载规则时,用
iptables-restore
原子性导入,避免中间状态不一致:
# 将规则写入文件
cat > /tmp/rules.v4 << 'EOF'
*filter
:INPUT DROP [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state ESTABLISHED
943

被折叠的 条评论
为什么被折叠?



