iptables 防火墙:从入门到进阶
概述
iptables 是 Linux 内核 netfilter 框架的用户空间工具,自 2001 年起成为 Linux 防火墙的事实标准。它通过规则表(table)和规则链(chain)的结构,对进出系统的网络数据包进行过滤、修改和转发控制。
虽然 nftables 已被各大发行版列为默认,但 iptables 依然广泛存在于现有生产环境,理解 iptables 也是学习 nftables 的基础。
核心概念
数据包流向
理解 iptables 必须先理解数据包在 Linux 内核中的流转路径:
┌──────────┐
│ INPUT │ → 本地进程
└──────────┘
▲
│
┌──────────┐ ┌──────────┐ ┌──────────┐
│ PREROUTING│──▶│ FORWARD │──▶│ POSTROUTING│
│ (路由前) │ │ (转发) │ │ (路由后) │
└──────────┘ └──────────┘ └──────────┘
│
▼
┌──────────┐
│ OUTPUT │ ← 本地进程
└──────────┘
数据包经过的链(Chain):
| 方向 | 经过链 |
|---|---|
| 入站 → 本地进程 | PREROUTING → INPUT |
| 本地进程 → 出站 | OUTPUT → POSTROUTING |
| 经过本机转发 | PREROUTING → FORWARD → POSTROUTING |
四表五链
iptables 的核心结构是 4 张表(Table) 和 5 条链(Chain)。
5 条内置链:
| 链名 | 作用 | 方向 |
|---|---|---|
INPUT |
处理入站数据包(目标为本机) | 入站 |
OUTPUT |
处理出站数据包(本机发出) | 出站 |
FORWARD |
处理转发的数据包(路由经过本机) | 转发 |
PREROUTING |
路由决策前处理数据包 | 入站(路由前) |
POSTROUTING |
路由决策后处理数据包 | 出站(路由后) |
4 张表:
| 表名 | 作用 | 挂载的链 |
|---|---|---|
filter |
包过滤(默认表,最常用) | INPUT、OUTPUT、FORWARD |
nat |
网络地址转换(NAT、端口转发) | PREROUTING、OUTPUT、POSTROUTING |
mangle |
数据包修改(TTL、TOS、标记等) | 全部 5 链 |
raw |
连接跟踪豁免(NOTRACK) | PREROUTING、OUTPUT |
规则匹配与动作
规则由两部分组成:
iptables -A <链> <匹配条件> -j <动作>
常见匹配条件:
| 匹配 | 示例 | 说明 |
|---|---|---|
| 源 IP | -s 192.168.1.0/24 |
匹配来源地址 |
| 目标 IP | -d 10.0.0.1 |
匹配目标地址 |
| 协议 | -p tcp |
匹配协议(tcp/udp/icmp) |
| 端口 | --dport 80 |
匹配目标端口 |
| 网卡 | -i eth0 |
匹配入站网卡 |
| 状态 | -m state --state NEW |
匹配连接状态 |
| 并发 | -m connlimit --connlimit-above 10 |
限制并发连接数 |
常见动作(target):
| 动作 | 说明 |
|---|---|
ACCEPT |
允许通过 |
DROP |
丢弃(不回复,客户端会等到超时) |
REJECT |
拒绝(回复拒绝消息,客户端立即知道被拒) |
LOG |
记录日志(不阻止,继续匹配下一条) |
SNAT |
源地址转换(用于 NAT 出站) |
DNAT |
目标地址转换(用于端口转发) |
MASQUERADE |
动态 SNAT(用于 PPPoE 拨号等动态 IP) |
RETURN |
返回上级链 |
安装
Debian / Ubuntu
# 安装 iptables(通常系统自带)
sudo apt update
sudo apt install iptables
# 额外工具:规则持久化
sudo apt install iptables-persistent
# IPv6 也需要的话
sudo apt install ip6tables
RHEL / CentOS 7 / Rocky 8
# CentOS 7 默认使用 legacy iptables
sudo yum install iptables iptables-services
# 启动并启用
sudo systemctl start iptables
sudo systemctl enable iptables
# CentOS 8+ / Rocky 9 有 iptables-nft 兼容层
sudo dnf install iptables-nft
查看当前版本
iptables --version
# 输出示例
# iptables v1.8.7 (nf_tables) → 运行在 nftables 后端兼容模式
# iptables v1.4.21 (legacy) → 传统 iptables 模式
快速上手
查看规则
# 查看 filter 表规则
iptables -L -n -v
# 查看 nat 表规则
iptables -t nat -L -n -v
# 查看规则编号(便于删除/插入)
iptables -L --line-numbers
# 仅查看 INPUT 链
iptables -L INPUT -n -v
参数说明:
| 参数 | 含义 |
|---|---|
-L |
列出规则 |
-n |
不解析 IP 为域名(显示更快) |
-v |
显示详细信息(包计数、字节数) |
--line-numbers |
显示规则编号 |
-t nat |
指定表(默认是 filter) |
基本防火墙规则
# ===== 默认策略 =====
# 将 INPUT 默认策略设为 DROP(拒绝所有入站)
iptables -P INPUT DROP
# 将 OUTPUT 默认策略设为 DROP(拒绝所有出站——严格模式)
iptables -P OUTPUT DROP
# 将 FORWARD 默认策略设为 DROP
iptables -P FORWARD DROP
# ===== 允许已建立的连接 =====
# 允许已建立的和相关的连接(一定要加,否则 SSH 都会断)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# ===== 允许回环接口 =====
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# ===== 开放常用服务 =====
# 允许 SSH(很重要,远程服务器一定要先开这个再改默认策略)
iptables -A INPUT -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
# 允许 ICMP(Ping)
iptables -A INPUT -p icmp -j ACCEPT
# 允许 DNS 出站(如果默认 OUTPUT 是 DROP)
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
# ===== 限制 SSH 暴力破解 =====
# 每分钟最多 5 个 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 5 --name SSH -j DROP
一个完整的示例脚本
#!/bin/bash
# 清空所有规则
iptables -F
iptables -X
iptables -t nat -F
iptables -t mangle -F
# 默认策略:拒绝所有入站和转发,允许所有出站
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 允许回环
iptables -A INPUT -i lo -j ACCEPT
# 允许已建立的连接
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 允许 SSH(改端口的话改这里)
iptables -A INPUT -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
# 允许 Ping
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# 记录被拒绝的包(可选)
iptables -A INPUT -j LOG --log-prefix "IPTABLES-DROP: " --log-level 4
进阶用法
NAT(网络地址转换)
场景 1:共享上网(MASQUERADE)
# 启用 IP 转发
echo 1 > /proc/sys/net/ipv4/ip_forward
# 内网网段从 eth0 出站时做 SNAT
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
场景 2:端口转发(DNAT)
# 将本机 8080 端口转发到内网 10.0.0.2:80
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 10.0.0.2:80
# 需要 FORWARD 链也放行
iptables -A FORWARD -p tcp -d 10.0.0.2 --dport 80 -j ACCEPT
限速(rate limiting)
# 限制 ICMP 每秒不超过 10 个
iptables -A INPUT -p icmp -m limit --limit 10/second -j ACCEPT
iptables -A INPUT -p icmp -j DROP
# 限制 SSH 连接速率
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m limit --limit 3/min -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j DROP
黑名单 IP
# 屏蔽某个 IP
iptables -A INPUT -s 1.2.3.4 -j DROP
# 屏蔽整个网段
iptables -A INPUT -s 10.0.0.0/24 -j DROP
# 从文件加载 IP 黑名单
for ip in $(cat blacklist.txt); do
iptables -A INPUT -s $ip -j DROP
done
规则管理操作
# 在指定位置插入规则(第 3 条)
iptables -I INPUT 3 -p tcp --dport 8080 -j ACCEPT
# 删除指定规则(通过编号)
iptables -D INPUT 3
# 删除指定规则(通过内容匹配)
iptables -D INPUT -p tcp --dport 8080 -j ACCEPT
# 替换某条规则
iptables -R INPUT 3 -p tcp --dport 9090 -j ACCEPT
# 清空某条链
iptables -F INPUT
# 清空所有规则
iptables -F
iptables -X
iptables -t nat -F
iptables -t mangle -F
规则持久化
iptables 的规则在重启后会丢失,需要手动保存和恢复。
Debian / Ubuntu(iptables-persistent)
# 安装
sudo apt install iptables-persistent
# 保存当前规则
sudo netfilter-persistent save
# 或手动保存
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6
# 手动恢复
sudo iptables-restore < /etc/iptables/rules.v4
sudo ip6tables-restore < /etc/iptables/rules.v6
RHEL / CentOS(iptables-services)
# 保存规则
sudo service iptables save
# 或手动保存
sudo iptables-save > /etc/sysconfig/iptables
# 重新加载
sudo iptables-restore < /etc/sysconfig/iptables
通用方法(systemd 启动脚本)
# 创建一个 systemd oneshot 服务
sudo cat > /etc/systemd/system/iptables-restore.service << 'EOF'
[Unit]
Description=Restore iptables rules
Before=network-pre.target
[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4
ExecStart=/sbin/ip6tables-restore /etc/iptables/rules.v6
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable iptables-restore
查错与调试
# 查看规则计数器(判断包是否命中了某条规则)
iptables -L -n -v
# 实时监控匹配日志
tail -f /var/log/kern.log | grep IPTABLES-DROP
# 检查计数是否在增长
watch -n 1 'iptables -L INPUT -n -v'
# 临时完全放行(调试用)
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -F
# 排查连接跟踪表
cat /proc/net/nf_conntrack | head
常见问题
Q:我执行 iptables 规则后 SSH 断了?
你将默认策略设为了 DROP,但没有先放行 SSH。
解决方法(通过 VPS 管理面板的 VNC/控制台):
# 先临时放行(如果还能操作)
iptables -P INPUT ACCEPT
# 或者让机房重启(如果设置了 iptables-restore 里有 SSH 规则)
预防措施: 永远先加 SSH 放行规则,再改默认策略;或使用 crontab 定时清空规则作为保险。
# crontab 保险:每 5 分钟清空防火墙(调试期间用)
*/5 * * * * /sbin/iptables -F
Q:iptables 和 iptables-legacy、iptables-nft 有什么区别?
iptables-legacy— 原始的 iptables,操作ip_tables内核模块iptables-nft— 兼容层,语法一样但底层用nf_tables内核模块- 现代发行版默认
iptables → iptables-nft
# 查看
iptables --version
# iptables v1.8.7 (nf_tables) ← 这是 nft 兼容模式
# iptables v1.4.21 (legacy) ← 这是传统模式
Q:规则太多了,性能会变差吗?
会。iptables 是链式遍历,规则匹配完才停止。规则集很大时(数百条以上),建议:
- 将常用允许规则放在前面
- 使用
ipset管理 IP 集合(而不是一条条写) - 考虑迁移到 nftables(集合+映射性能更好)
Q:如何统计各规则的匹配次数?
iptables -L INPUT -n -v
# 第一列就是包计数,第二列是字节数
# 如果计数长时间不变,说明这条规则没匹配到
总结
| 操作 | 命令 |
|---|---|
| 查看 filter 表规则 | iptables -L -n -v |
| 查看 nat 表规则 | iptables -t nat -L -n -v |
| 允许端口 | iptables -A INPUT -p tcp --dport 80 -j ACCEPT |
| 拒绝 IP | iptables -A INPUT -s 1.2.3.4 -j DROP |
| 端口转发 | iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.2:80 |
| 保存规则 | iptables-save > /etc/iptables/rules.v4 |
| 恢复规则 | iptables-restore < /etc/iptables/rules.v4 |
| 清空所有规则 | iptables -F |
| 修改默认策略 | iptables -P INPUT DROP |
最后的建议:如果你是新手,不要直接用 iptables 配置防火墙 —— 先试试 ufw。如果你在管理生产环境,建议逐步迁移到 nftables。iptables 虽然经典,但已经是"上一个时代"的技术了。