前言:防火墙不是玄学,是规则集合
我第一次接触 iptables 的时候,被"四表五链"的名号吓住了。后来发现,它就是几张表格,每张表格有几个"检查点"(链)。数据包经过每个检查点时,查一下规则表,匹配了就执行对应的动作。说白了就是一套过滤器加转发器。这期把我踩过的坑和实操经验一起分享出来。
iptables 的核心:四表五链
先记住一个口诀:表决定干什么,链决定在哪儿干。
四表
- ·filter 表(最常用):负责过滤数据包,允许或拒绝。默认就在这个表。
- ·nat 表:负责网络地址转换,做端口转发和共享上网。
- ·mangle 表:修改数据包的 TOS、TTL 等标记字段。
- ·raw 表:配置是否启用连接跟踪,一般用不到。
五链
每个表有自己可用的链(检查点):
| 链名 | 方向 | 说明 |
|---|
| INPUT | 入站 | 进入本机的数据包 |
| OUTPUT | 出站 | 本机发出的数据包 |
| FORWARD | 转发 | 经过本机但不发往本机的包 |
| PREROUTING | 路由前 | 数据包刚进来,路由决策之前 |
| POSTROUTING | 路由后 | 数据包要出去了,路由决策之后 |
对应关系:filter 表用 INPUT/OUTPUT/FORWARD,nat 表用 PREROUTING/POSTROUTING/OUTPUT,mangle 和 raw 表用全部五链。
规则匹配逻辑
iptables 是"首条匹配即生效"。规则是按顺序逐条匹配的,一旦匹配就不再看后面的规则。这意味着你如果把"拒绝所有"写在了"允许特定IP"前面,那允许规则就永远没机会执行了。这是个特别容易踩的坑。
查看现有规则,先别急着改
在动手之前,先看看当前服务器上有什么规则。
# 查看 filter 表所有规则,带行号,数字显示不解析主机名
iptables -L -n -v --line-numbers
- ·
-L:列出规则 - ·
-n:不解析 IP 和端口为域名/服务名,显示更快更准确 - ·
-v:显示统计数据(流量计数) - ·
--line-numbers:显示每条规则的行号,方便插入和删除
# 查看 nat 表的规则
iptables -t nat -L -n -v --line-numbers
如果输出是空的,说明你的服务器目前没有任何自定义防火墙规则。这在生产环境是很危险的。
实战场景一:配置 IP 白名单
假设我们要实现:只允许公司 IP(比如 192.168.1.100)通过 SSH 连接,其他 IP 一律拦截。
# 清空 filter 表的所有规则(谨慎操作,SSH 可能断连)
iptables -F
# 设置默认策略:拒绝所有入站流量
iptables -P INPUT DROP
# 允许已建立的连接继续通行(不然你当前的 SSH 会话也会断)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 允许环回接口(localhost)的流量(很多程序依赖 lo 通信)
iptables -A INPUT -i lo -j ACCEPT
# 放行公司 IP 的 SSH 端口(22)
iptables -A INPUT -s 192.168.1.100 -p tcp --dport 22 -j ACCEPT
# 放行 HTTP/HTTPS
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
⚠️ 安全提醒:如果你在远程服务器上执行 iptables -P INPUT DROP,一定要先加好 ESTABLISHED,RELATED 规则。否则当前 SSH 会话也会被切断,你就把自己锁在外面了。我当年第一次配防火墙就犯过这个错,直接在腾讯云控制台重启服务器才救回来。
实战场景二:防 SSH 爆破
服务器开放公网 SSH 端口,每天都会有脚本小子扫描尝试登录。可以用 iptables 限制频率。
# 使用 recent 模块限制每个 IP 每分钟最多尝试 3 次 SSH 连接
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 3 --name SSH -j DROP
这条规则的意思是:对每个新连接的源 IP,记录到名为 SSH 的列表中;如果这个 IP 在 60 秒内已经建立了 3 次新连接,就丢弃后续的连接请求。这个搭配 fail2ban 效果更好。
实战场景三:NAT 端口转发
假设你有一台内网服务器 192.168.1.10 运行着 Web 服务(端口 3000),想让公网通过本机 80 端口访问它。
# 允许 IP 转发
echo 1 > /proc/sys/net/ipv4/ip_forward
# PREROUTING 链上做目标地址转换:把到达本机 80 端口的流量转到内网服务器
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.10:3000
# POSTROUTING 链上做源地址转换:让内网服务器以为流量来自本机(而非公网客户端)
iptables -t nat -A POSTROUTING -d 192.168.1.10 -p tcp --dport 3000 -j MASQUERADE
# FORWARD 链放行这些流量
iptables -A FORWARD -p tcp -d 192.168.1.10 --dport 3000 -j ACCEPT
iptables -A FORWARD -p tcp -s 192.168.1.10 --sport 3000 -j ACCEPT
这里的 MASQUERADE 会自动用本机出口 IP 替换源 IP。如果你的本机 IP 是固定的,可以用 SNAT --to-source 本机IP 替代,性能更好。
⚠️ 安全提醒:做端口转发时,别把 ssh 端口(22)直接转发到内网。攻击者可能通过这个跳板攻击内网机器。建议只开放必要的业务端口。
规则持久化:不然重启就没了
iptables 的规则是保存在内存中的,重启服务器就会丢失。需要手动保存和恢复。
# 保存当前规则到文件
iptables-save > /etc/iptables/rules.v4
# 从文件恢复规则
iptables-restore < /etc/iptables/rules.v4
对于 Debian/Ubuntu,可以安装 iptables-persistent 让系统自动加载:
# 安装持久化工具
apt-get install iptables-persistent
# 手动保存规则
netfilter-persistent save
对于 CentOS/RHEL 7+,默认使用 firewalld,但底层还是 iptables。可以用 service iptables save 保存。
规则顺序:血的教训
iptables 的规则顺序就是执行顺序。如果你在 -P INPUT DROP(默认拒绝)之后追加了一条允许规则,它永远不会被匹配到,因为默认策略已经丢弃了。
# 错误示范:这条规则永远不会生效
iptables -P INPUT DROP
iptables -A INPUT -s 192.168.1.100 -j ACCEPT
# 正确做法:允许规则在拒绝之前
iptables -A INPUT -s 192.168.1.100 -j ACCEPT
iptables -P INPUT DROP
如果需要插入到指定位置,用 -I 指定行号:
# 插入到第 3 行
iptables -I INPUT 3 -s 10.0.0.1 -j ACCEPT
删除规则也要小心:
# 删除第 5 条规则
iptables -D INPUT 5
查看连接跟踪表:conntrack 辅助排查
iptables 内部维护了一个连接跟踪表(connection tracking),记录所有经过的数据流。某些类型的攻击(比如端口扫描、DDoS)会在连接跟踪表里留下痕迹。
# 查看当前连接跟踪表
cat /proc/net/nf_conntrack
# 统计各协议连接数
cat /proc/net/nf_conntrack | awk '{print $1}' | sort | uniq -c | sort -rn
如果连接跟踪表满了,新连接会被丢弃,导致业务中断。可以查看当前上限和实际使用的数量:
# 查看最大连接跟踪数
sysctl net.netfilter.nf_conntrack_max
# 查看当前跟踪的连接数
sysctl net.netfilter.nf_conntrack_count
如果 nf_conntrack_count 接近 nf_conntrack_max,说明连接跟踪表不够用了。可以调大上限:
# 临时调整(重启后失效)
sysctl -w net.netfilter.nf_conntrack_max=262144
# 永久调整
# echo 'net.netfilter.nf_conntrack_max=262144' >> /etc/sysctl.conf
或者对不需要跟踪的流量,在 raw 表里跳过跟踪:
# 对 Web 流量跳过连接跟踪
iptables -t raw -A PREROUTING -p tcp --dport 80 -j NOTRACK
iptables -t raw -A OUTPUT -p tcp --sport 80 -j NOTRACK
不过 NOTRACK 会影响 NAT 功能,如果你在做端口转发就不要用这个了。
iptables 与 nftables:新老交替
nftables 是 iptables 的下一代替代品,从 Debian 10、RHEL 8、Ubuntu 20.04 开始成为默认防火墙框架。但很多生产环境的文档和运维脚本仍然依赖 iptables 语法。
两个主要区别:
- ·iptables 的命令行是独立的(iptables、ip6tables、ebtables、arptables),nftables 统一成
nft 一个命令。 - ·iptables 的规则是一维列表,nftables 支持嵌套集合,性能更好,规则更简洁。
- ·nftables 的语法更像编程语言的表达式,学习曲线稍陡。
不过大多数场景下,你仍然可以用 iptables 命令操作(系统底层会通过兼容层调用 nftables),日常运维完全够用。
用脚本管理规则集:不要每次都手打
生产服务器的 iptables 规则往往很多条,手打太容易出错。我习惯把所有规则写进一个 Shell 脚本,每次修改就改脚本再执行一遍。
# 写一个防火墙规则脚本 /etc/iptables/rules.sh
# 清空所有规则和自定义链
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
# 设置默认策略:入站拒绝,出站允许
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 放行本地回环和已建立的连接
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 允许内网段访问
iptables -A INPUT -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -s 172.16.0.0/12 -j ACCEPT
iptables -A INPUT -s 192.168.0.0/16 -j ACCEPT
# 允许公司办公网访问 SSH
iptables -A INPUT -s 公司外网IP -p tcp --dport 22 -j ACCEPT
# 放行业务端口
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables-save > /etc/iptables/rules.v4
写完之后 chmod +x /etc/iptables/rules.sh,每次修改就运行这个脚本。比一条条敲命令安全多了。
排查思路:规则没生效怎么办
如果配置了规则发现不生效,按这几步排查:
1. iptables -L -n -v --line-numbers 确认规则是否存在。
2. 检查 -t 表是否正确(很多人把 DNAT 写到 filter 表了)。
3. 查看计数器 -v,如果计数器一直为 0,说明规则根本没被匹配到。
4. iptables -F 清空规则,从简单规则开始逐步加,确认每步生效。
5. 检查 iptables -L -n 的默认策略(Chain policy),如果默认策略是 DROP,放行规则没有覆盖到的流量都会被丢弃。
⚠️ 安全提醒:在生产环境操作 iptables 前,建议先写一个定时恢复脚本(crontab 里放一条 iptables-restore < /etc/iptables/rules.v4 过 5 分钟执行),防止把自己锁在外面。
总结
iptables 的本质就是四张表五条链组成的包过滤框架。掌握了 filter 表做访问控制、nat 表做端口转发、规则顺序决定成败、持久化防止重启丢失,就覆盖了 90% 的日常运维场景。如果你用的是较新的发行版,可以开始了解 nftables,但 iptables 的思维模型在 nftables 里同样适用,迁移成本并不高。