Linux 防火墙完全指南:iptables + nftables + UFW + firewalld | 2026 最新实践
你有没有遇到过这些场景:
- VPS 刚装好,SSH 还开着 22 端口,结果一天被扫描几万次
- 跑了个 Web 服务在 8080 端口,外面怎么都访问不了——防火墙忘了开
- Docker 容器端口映射好好的,加了一条 iptables 规则后全炸了
- 想做个端口转发,把 80 端口转到内部 3000,查了半天不知道怎么配
- 听说 nftables 是未来,但不知道和 iptables 什么关系,该学哪个
Linux 防火墙就是解决这些问题的核心——它是服务器的第一道防线,控制着什么流量能进、什么流量能出。
在 Linux 的世界里,防火墙不只是「开/关端口」那么简单。从内核的 netfilter 框架到用户态的 iptables/nftables 命令,再到 UFW/firewalld 简化前端,这是一套层次分明、功能强大的网络控制系统。
本文将从底层原理到实战配置,把 iptables、nftables、UFW、firewalld 一次讲透。
一、Linux 防火墙架构总览
1.1 netfilter:内核中的防火墙引擎
Linux 防火墙的底层是 netfilter——Linux 内核中的网络包过滤框架。它不是独立程序,而是内核协议栈中的「检查点」。
每个网络数据包在内核中流转时,会经过 5 个钩子(hook)点,netfilter 在这些点上检查数据包:
数据包进入 → [PREROUTING] → 路由判断 → [INPUT] → 本机进程 ↓ 本机进程发出 → [OUTPUT] → 路由判断 → [POSTROUTING] → 数据包发出 ↓ 转发数据包 → [FORWARD] → [POSTROUTING] → 数据包发出| 钩子点 | 作用 | 对应链 |
|---|---|---|
NF_IP_PRE_ROUTING | 数据包进入网卡后、路由判断前 | PREROUTING |
NF_IP_LOCAL_IN | 目标是本机的数据包、交给本机进程前 | INPUT |
NF_IP_FORWARD | 数据包转发经过本机时 | FORWARD |
NF_IP_LOCAL_OUT | 本机进程发出的数据包 | OUTPUT |
NF_IP_POST_ROUTING | 数据包发出网卡前 | POSTROUTING |
1.2 四表五链
netfilter 用**表(table)和链(chain)**组织规则:
四张表(按优先级从高到低):
| 表 | 作用 | 包含的链 |
|---|---|---|
| raw | 在连接跟踪前处理,标记不走 conntrack | PREROUTING, OUTPUT |
| mangle | 修改数据包的 TOS/TTL/Mark 等 | 所有五链 |
| nat | 网络地址转换(DNAT/SNAT/MASQUERADE) | PREROUTING, OUTPUT, POSTROUTING, INPUT |
| filter | 过滤数据包(ACCEPT/DROP/REJECT) | INPUT, OUTPUT, FORWARD |
五条链:
| 链 | 作用 | 典型场景 |
|---|---|---|
| PREROUTING | 数据包进入后、路由前 | DNAT(端口转发)、目标地址转换 |
| INPUT | 发往本机的数据包 | 控制外部访问本机端口 |
| FORWARD | 经过本机转发的数据包 | 路由器/网关场景 |
| OUTPUT | 本机发出的数据包 | 控制本机发出的流量 |
| POSTROUTING | 数据包发出前 | SNAT(源地址转换)、MASQUERADE |
数据包处理流程(简化版):
┌─────────┐ 入站数据包 ───────────→ │ PREROUTING │ └────┬────┘ │ ┌────────┴────────┐ │ 路由判断:目标地址 │ └────────┬────────┘ ┌─────────┴──────────┐ ↓ ↓ 目标是本机 需要转发 │ │ ┌──────┴──────┐ ┌───────┴───────┐ │ INPUT │ │ FORWARD │ └──────┬──────┘ └───────┬───────┘ │ │ ┌──────┴──────┐ │ │ 本机进程 │ │ └──────┬──────┘ │ │ │ ┌──────┴──────┐ │ │ OUTPUT │ │ └──────┬──────┘ │ │ │ └────────┬──────────┘ ↓ ┌──────┴──────┐ │ POSTROUTING │ └──────┬──────┘ ↓ 出站数据包1.3 iptables vs nftables vs UFW vs firewalld
| 工具 | 定位 | 适用场景 | 语法复杂度 |
|---|---|---|---|
| iptables | 经典用户态工具 | 精细控制、NAT、已有规则维护 | ★★★★★ |
| nftables | iptables 继任者 | 新系统、高性能需求 | ★★★★ |
| UFW | Ubuntu/Debian 前端 | 简单端口管理 | ★★ |
| firewalld | RHEL/CentOS 前端 | 区域管理、运行时/永久配置 | ★★★ |
一句话选择:简单开端口用 UFW/firewalld,精细控制用 iptables/nftables。
二、iptables 基础操作
2.1 安装与验证
# Ubuntu/Debiansudo apt install iptables
# CentOS/RHELsudo dnf install iptables
# 检查版本iptables --version# iptables v1.8.9 (nf_tables) ← 注意:现代系统默认使用 nf_tables 后端
# 查看是否使用 nftables 后端iptables --version | grep -o 'nf_tables\|legacy'重要:现代 Linux 发行版(Ubuntu 22.04+、CentOS 9+)的
iptables命令实际上是iptables-nft——使用 iptables 语法但底层操作 nftables 内核。这是兼容层,不影响使用。
2.2 查看规则
# 查看所有规则(filter 表)sudo iptables -L# 或 sudo iptables -L -t filter(-t filter 可省略,默认就是 filter)
# 带行号、详细信息查看(推荐)sudo iptables -L -n -v --line-numbers# -L 列出规则# -n 不解析 IP 和端口(更快)# -v 显示详细信息(包计数、字节数)# --line-numbers 显示行号(删除时需要)
# 查看指定链sudo iptables -L INPUT -n -v --line-numbers
# 查看其他表sudo iptables -t nat -L -n -v # NAT 表sudo iptables -t mangle -L -n -v # mangle 表sudo iptables -t raw -L -n -v # raw 表
# 查看规则计数(排查哪条规则在匹配)sudo iptables -L INPUT -n -v# pkts bytes target prot opt in out source destination# 120 7200 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22# 0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:33062.3 规则管理命令
# 追加规则(添加到链的末尾)sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# 插入规则(添加到指定位置,默认位置 1)sudo iptables -I INPUT 1 -p tcp --dport 443 -j ACCEPT# 这会把规则插到第 1 行,原来的规则向下移动
# 替换规则sudo iptables -R INPUT 3 -p tcp --dport 80 -j ACCEPT# 替换第 3 行的规则
# 删除指定行号的规则sudo iptables -D INPUT 3
# 删除匹配的规则(不需要行号)sudo iptables -D INPUT -p tcp --dport 80 -j ACCEPT
# 清空所有规则(危险!会断开所有连接)sudo iptables -F# 清空指定链sudo iptables -F INPUT# 清空指定表sudo iptables -t nat -F
# 清空计数器sudo iptables -Zsudo iptables -Z INPUT 3 # 只清空第 3 行的计数器
# 设置默认策略sudo iptables -P INPUT DROP # 默认拒绝所有入站sudo iptables -P FORWARD DROP # 默认拒绝所有转发sudo iptables -P OUTPUT ACCEPT # 默认允许所有出站2.4 命令速查表
| 参数 | 含义 | 示例 |
|---|---|---|
-A | Append,追加到末尾 | iptables -A INPUT ... |
-I | Insert,插入到指定位置 | iptables -I INPUT 1 ... |
-R | Replace,替换指定行 | iptables -R INPUT 3 ... |
-D | Delete,删除 | iptables -D INPUT 3 |
-F | Flush,清空 | iptables -F INPUT |
-L | List,列出 | iptables -L -n -v |
-P | Policy,设置默认策略 | iptables -P INPUT DROP |
-Z | Zero,清零计数器 | iptables -Z |
-t | Table,指定表 | iptables -t nat -L |
-n | Numeric,不解析 | iptables -L -n |
-v | Verbose,详细信息 | iptables -L -v |
三、规则匹配条件
3.1 基本匹配条件
# 协议匹配iptables -A INPUT -p tcp ... # TCP 协议iptables -A INPUT -p udp ... # UDP 协议iptables -A INPUT -p icmp ... # ICMP(ping)iptables -A INPUT -p all ... # 所有协议(默认)
# 端口匹配(需要 -p tcp 或 -p udp)iptables -A INPUT -p tcp --dport 80 -j ACCEPT # 目标端口 80iptables -A INPUT -p tcp --sport 1024 -j DROP # 源端口 1024iptables -A INPUT -p tcp --dport 80:443 -j ACCEPT # 端口范围 80-443iptables -A INPUT -p tcp -m multiport --dports 80,443,8080 -j ACCEPT # 多端口
# IP 地址匹配iptables -A INPUT -s 192.168.1.100 -j DROP # 源 IPiptables -A INPUT -s 192.168.1.0/24 -j ACCEPT # 源 IP 段iptables -A INPUT -d 10.0.0.1 -j DROP # 目标 IPiptables -A INPUT -s 192.168.1.100,192.168.1.200 -j DROP # 多个 IP
# 网卡接口匹配iptables -A INPUT -i eth0 -j ACCEPT # 入口网卡iptables -A OUTPUT -o eth0 -j ACCEPT # 出口网卡iptables -A INPUT -i lo -j ACCEPT # 本地回环(重要!)
# 状态匹配(最常用,依赖 conntrack 模块)iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT# 或旧语法:iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT# ESTABLISHED 已建立连接的后续包# RELATED 相关连接(如 FTP 数据通道、ICMP 错误)# NEW 新连接的第一个包# INVALID 无效数据包(无法识别的连接状态)3.2 高级匹配条件
# --- TCP 标志匹配 ---# 只允许 SYN 包(新连接)iptables -A INPUT -p tcp --tcp-flags SYN,RST,ACK,FIN SYN -j ACCEPT
# 阻止伪造的 Xmas 包iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP
# 阻止 Null 包iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
# --- 速率限制 ---# 限制 SSH 连接:每分钟最多 5 次,突发 10 次iptables -A INPUT -p tcp --dport 22 -m limit --limit 5/min --limit-burst 10 -j ACCEPTiptables -A INPUT -p tcp --dport 22 -j DROP
# 限制 ping:每秒 1 次,突发 5 次iptables -A INPUT -p icmp -m limit --limit 1/sec --limit-burst 5 -j ACCEPTiptables -A INPUT -p icmp -j DROP
# --- connlimit 连接数限制 ---# 限制每个 IP 对 SSH 的并发连接数最多 3 个iptables -A INPUT -p tcp --dport 22 -m connlimit --connlimit-above 3 -j DROP
# 限制每个 IP 对 Web 的并发连接数最多 50 个iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j DROP
# --- recent 模块(防暴力破解) ---# 60 秒内访问 22 端口超过 5 次的 IP 加入黑名单iptables -A INPUT -p tcp --dport 22 -m recent --name SSH --setiptables -A INPUT -p tcp --dport 22 -m recent --name SSH --update --seconds 60 --hitcount 5 -j DROPiptables -A INPUT -p tcp --dport 22 -j ACCEPT
# --- 字符串匹配 ---# 阻止包含特定字符串的数据包iptables -A INPUT -p tcp -m string --string "GET /admin" --algo bm -j DROP
# --- MAC 地址匹配 ---iptables -A INPUT -m mac --mac-source 00:11:22:33:44:55 -j DROP
# --- TTL 匹配 ---# 阻止 TTL 为 1 的数据包(防止路由追踪)iptables -A INPUT -m ttl --ttl-eq 1 -j DROP
# --- owner 匹配(仅 OUTPUT 链) ---# 阻止 UID 1000 的用户访问 80 端口iptables -A OUTPUT -p tcp --dport 80 -m owner --uid-owner 1000 -j DROP
# --- 时间匹配 ---# 工作时间(9:00-18:00)允许访问 SSHiptables -A INPUT -p tcp --dport 22 -m time --timestart 09:00 --timestop 18:00 --weekdays Mon,Tue,Wed,Thu,Fri -j ACCEPT3.3 匹配条件速查表
| 匹配模块 | 作用 | 示例 |
|---|---|---|
-p | 协议 | -p tcp / -p udp / -p icmp |
--dport | 目标端口 | --dport 80 / --dport 80:443 |
--sport | 源端口 | --sport 1024 |
-s | 源 IP | -s 192.168.1.0/24 |
-d | 目标 IP | -d 10.0.0.1 |
-i | 入口网卡 | -i eth0 |
-o | 出口网卡 | -o eth0 |
conntrack | 连接状态 | --ctstate ESTABLISHED,RELATED |
multiport | 多端口 | --dports 80,443,8080 |
limit | 速率限制 | --limit 5/min --limit-burst 10 |
connlimit | 并发连接数 | --connlimit-above 3 |
recent | 近期记录 | --seconds 60 --hitcount 5 |
string | 字符串匹配 | --string "GET /admin" |
mac | MAC 地址 | --mac-source 00:11:22:33:44:55 |
time | 时间段 | --timestart 09:00 --timestop 18:00 |
ttl | TTL 值 | --ttl-eq 64 |
owner | 用户/组 | --uid-owner 1000 |
tcp-flags | TCP 标志 | --tcp-flags SYN,RST,ACK SYN |
3.4 目标动作(Target)
# --- 基本动作 ----j ACCEPT # 允许数据包通过-j DROP # 丢弃数据包(不响应,发送方会超时重试)-j REJECT # 拒绝并回复错误(发送方立即知道被拒绝)-j REJECT --reject-with tcp-reset # 用 TCP RST 拒绝(更礼貌,不像 DROP 那样让发送方等待)-j LOG # 记录日志(不终止规则匹配,继续匹配下一条)
# --- 日志记录 ---# 记录被丢弃的包iptables -A INPUT -j LOG --log-prefix "IPTABLES-DROP: " --log-level 4# 限制日志频率(防止日志洪水)iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "IPTABLES-DROP: "
# --- 跳转到自定义链 ----j MY_CHAIN # 跳转到自定义链 MY_CHAIN# 自定义链中未匹配的包会返回原链继续匹配
# --- NAT 动作(nat 表专用)----j SNAT --to-source 1.2.3.4 # 源地址转换-j DNAT --to-destination 10.0.0.2 # 目标地址转换-j MASQUERADE # 动态源地址转换(适合动态 IP)-j REDIRECT --to-ports 8080 # 本地端口重定向
# --- Mark 标记(mangle 表)----j MARK --set-mark 1 # 给数据包打标记DROP vs REJECT 对比:
| 特性 | DROP | REJECT |
|---|---|---|
| 响应 | 无响应 | 发送 ICMP unreachable / TCP RST |
| 发送方体验 | 超时等待 | 立即收到拒绝 |
| 安全性 | 更高(不暴露防火墙存在) | 较低(暴露了防火墙) |
| 网络友好性 | 差(导致超时重试) | 好(发送方立即知道) |
| 适用场景 | 外部攻击防护 | 内部网络、调试 |
最佳实践:对外用 DROP(不给攻击者信息),对内用 REJECT(让用户快速知道被拒绝)。
四、NAT 与端口转发
4.1 NAT 基础
NAT(Network Address Translation)是防火墙最重要的功能之一。内核维护一张 conntrack(连接跟踪)表,记录每个连接的状态。
| NAT 类型 | 动作 | 应用场景 |
|---|---|---|
| DNAT | 修改目标地址 | 端口转发、负载均衡 |
| SNAT | 修改源地址 | 固定 IP 出站伪装 |
| MASQUERADE | 动态 SNAT | 动态 IP 出站伪装(如家庭宽带) |
| REDIRECT | 本地 DNAT | 本地端口重定向 |
4.2 端口转发(DNAT)
# --- 场景:把外部 80 端口转发到内网 10.0.0.2:8080 ---
# 步骤 1:在 PREROUTING 链做 DNATsudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.2:8080
# 步骤 2:在 FORWARD 链允许转发sudo iptables -A FORWARD -p tcp -d 10.0.0.2 --dport 8080 -j ACCEPT
# 步骤 3:开启 IP 转发(如果还没开)sudo sysctl -w net.ipv4.ip_forward=1# 永久生效:echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-ipforward.confsudo sysctl -p
# 步骤 4(如果内网机器回包需要走本机):做 SNATsudo iptables -t nat -A POSTROUTING -p tcp -d 10.0.0.2 --dport 8080 -j MASQUERADE# --- 场景:本地端口重定向 ---# 把 80 端口重定向到 8080(不需要 IP 转发)sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080
# 本机内部访问也重定向(OUTPUT 链)sudo iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-ports 8080# --- 场景:根据来源 IP 转发到不同后端 ---# 192.168.1.0/24 → 10.0.0.2:8080sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -s 192.168.1.0/24 -j DNAT --to-destination 10.0.0.2:8080
# 其他来源 → 10.0.0.3:8080sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.3:80804.3 出站伪装(MASQUERADE / SNAT)
# --- 场景:内网通过本机访问外网 ---# MASQUERADE:适合动态 IP(如拨号上网、DHCP)sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# SNAT:适合固定 IP(性能比 MASQUERADE 稍好)sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 1.2.3.4
# SNAT 到端口范围sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 1.2.3.4:20000-30000MASQUERADE vs SNAT:
| 特性 | MASQUERADE | SNAT |
|---|---|---|
| IP 地址 | 自动获取网卡当前 IP | 手动指定固定 IP |
| 适用场景 | 动态 IP(DHCP/拨号) | 固定 IP(VPS/专线) |
| 性能 | 略低(每次查网卡 IP) | 略高(IP 固定) |
| 断网恢复 | 自动适应新 IP | 需手动改规则 |
4.4 NAT 完整示例:家庭路由器
#!/bin/bash# 家庭路由器 NAT 配置脚本
# 开启 IP 转发sysctl -w net.ipv4.ip_forward=1
# 清空旧规则iptables -Fiptables -t nat -Fiptables -F FORWARD
# 允许已建立连接iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 允许内网 → 外网转发iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT # eth1=内网, eth0=外网
# 拒绝外网 → 内网主动连接iptables -A FORWARD -i eth0 -o eth1 -j DROP
# MASQUERADE:内网出站伪装iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# 设置默认策略iptables -P FORWARD DROP4.5 查看 NAT 规则
# 查看 NAT 表所有规则sudo iptables -t nat -L -n -v --line-numbers
# 查看 PREROUTING 链(DNAT)sudo iptables -t nat -L PREROUTING -n -v
# 查看 POSTROUTING 链(SNAT/MASQUERADE)sudo iptables -t nat -L POSTROUTING -n -v
# 查看连接跟踪表sudo cat /proc/net/nf_conntrack | head -20
# 查看连接跟踪统计sudo cat /proc/sys/net/netfilter/nf_conntrack_count # 当前连接数sudo cat /proc/sys/net/netfilter/nf_conntrack_max # 最大连接数
# 调整连接跟踪表大小(高并发服务器需要调大)echo 'net.netfilter.nf_conntrack_max = 131072' | sudo tee -a /etc/sysctl.d/99-conntrack.confsudo sysctl -p五、UFW:Ubuntu/Debian 的简化防火墙
5.1 安装与启用
# 安装sudo apt install ufw
# 启用(会自动设置默认策略)sudo ufw enable# 默认策略:入站拒绝,出站允许
# 查看状态sudo ufw statussudo ufw status verbose # 详细信息sudo ufw status numbered # 带行号
# 禁用sudo ufw disable
# 重置(清除所有规则,恢复默认)sudo ufw reset5.2 基本规则
# --- 端口管理 ---
# 允许 SSHsudo ufw allow 22/tcpsudo ufw allow ssh # 按服务名(/etc/services 中定义)
# 允许 HTTP/HTTPSsudo ufw allow 80/tcpsudo ufw allow 443/tcp
# 允许端口范围sudo ufw allow 3000:3100/tcp
# 允许指定 IP 访问sudo ufw allow from 192.168.1.100
# 允许指定 IP 访问指定端口sudo ufw allow from 192.168.1.100 to any port 22
# 允许指定 IP 段访问指定端口sudo ufw allow from 192.168.1.0/24 to any port 3306
# 允许指定网卡sudo ufw allow in on eth0 to any port 80
# --- 拒绝规则 ---
# 拒绝端口sudo ufw deny 3306/tcp
# 拒绝指定 IPsudo ufw deny from 1.2.3.4
# 拒绝指定 IP 到指定端口sudo ufw deny from 1.2.3.4 to any port 22
# --- 删除规则 ---
# 按行号删除sudo ufw status numberedsudo ufw delete 3
# 按规则删除sudo ufw delete allow 80/tcp5.3 默认策略
# 设置默认策略sudo ufw default deny incoming # 默认拒绝入站(推荐)sudo ufw default allow outgoing # 默认允许出站(推荐)sudo ufw default deny forward # 默认拒绝转发
# 查看默认策略sudo ufw status verbose# Default: deny (incoming), allow (outgoing), deny (forwarding)5.4 日志
# 开启日志sudo ufw logging onsudo ufw logging medium # 日志级别:low/medium/high/full
# 关闭日志sudo ufw logging off
# 日志位置:/var/log/ufw.log# 日志格式:# [UFW BLOCK] IN=eth0 SRC=1.2.3.4 DST=10.0.0.1 PROTO=TCP DPT=225.5 UFW 完整配置示例
#!/bin/bash# VPS 初始化防火墙配置
# 重置sudo ufw --force reset
# 默认策略sudo ufw default deny incomingsudo ufw default allow outgoing
# 允许 SSH(限制来源 IP)sudo ufw allow from YOUR_IP to any port 22 proto tcp
# 允许 HTTP/HTTPSsudo ufw allow 80/tcpsudo ufw allow 443/tcp
# 允许特定端口sudo ufw allow 8080/tcp comment 'Web 服务'
# 速率限制 SSH(防暴力破解)sudo ufw limit 22/tcp
# 启用日志sudo ufw logging medium
# 启用sudo ufw --force enable
# 验证sudo ufw status numbered
ufw limit:在 30 秒内如果有 6 次以上的连接尝试,自动阻止该 IP。非常适合保护 SSH。
六、firewalld:RHEL/CentOS 的区域防火墙
6.1 核心概念
firewalld 使用**区域(Zone)**概念,不同区域有不同的信任级别:
| 区域 | 信任级别 | 默认行为 |
|---|---|---|
trusted | 最高 | 允许所有流量 |
public | 低 | 拒绝大部分,只允许选中的 |
external | 低 | 拒绝大部分,出站做伪装 |
home | 中 | 允许家庭网络常用服务 |
internal | 中 | 允许内部网络常用服务 |
work | 中 | 允许工作网络常用服务 |
dmz | 低 | 只允许选中的(非军事区) |
block | 最低 | 拒绝所有 |
drop | 最低 | 丢弃所有(不响应) |
6.2 安装与基本操作
# 安装sudo dnf install firewalld
# 启动并设为开机自启sudo systemctl start firewalldsudo systemctl enable firewalld
# 查看状态sudo firewall-cmd --statesudo firewall-cmd --status
# 查看默认区域sudo firewall-cmd --get-default-zone
# 查看所有区域sudo firewall-cmd --get-zones
# 查看当前活动区域sudo firewall-cmd --get-active-zones
# 查看区域详细信息sudo firewall-cmd --zone=public --list-all6.3 端口与服务管理
# --- 服务管理 ---# firewalld 预定义了常见服务,用服务名操作更方便
# 查看所有预定义服务sudo firewall-cmd --get-services
# 添加服务(运行时,重启后失效)sudo firewall-cmd --zone=public --add-service=httpsudo firewall-cmd --zone=public --add-service=httpssudo firewall-cmd --zone=public --add-service=ssh
# 永久添加服务(需要 reload 才生效)sudo firewall-cmd --zone=public --add-service=http --permanentsudo firewall-cmd --reload
# 移除服务sudo firewall-cmd --zone=public --remove-service=http --permanentsudo firewall-cmd --reload
# --- 端口管理 ---
# 添加端口sudo firewall-cmd --zone=public --add-port=8080/tcp --permanentsudo firewall-cmd --zone=public --add-port=3000-3100/tcp --permanentsudo firewall-cmd --reload
# 移除端口sudo firewall-cmd --zone=public --remove-port=8080/tcp --permanentsudo firewall-cmd --reload
# 查看已开放的端口和服务sudo firewall-cmd --zone=public --list-portssudo firewall-cmd --zone=public --list-services6.4 区域切换与网卡绑定
# 更改默认区域sudo firewall-cmd --set-default-zone=public
# 将网卡绑定到区域sudo firewall-cmd --zone=public --change-interface=eth0 --permanent
# 查看网卡绑定的区域sudo firewall-cmd --get-zone-of-interface=eth0
# 将特定 IP 绑定到特定区域(source)sudo firewall-cmd --zone=trusted --add-source=192.168.1.100 --permanentsudo firewall-cmd --reload6.5 富规则(Rich Rules)
firewalld 的富规则提供更细粒度的控制:
# 基本语法rule [family="ipv4|ipv6"] [source] [destination] service|port|protocol|icmp-block|masquerade|forward-port [log] [audit] [accept|reject|drop]
# 只允许 192.168.1.0/24 访问 SSHsudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="ssh" accept' --permanent
# 限制 SSH 连接频率sudo firewall-cmd --zone=public --add-rich-rule='rule service name="ssh" limit value="3/m" accept' --permanent
# 拒绝 1.2.3.4 的所有流量sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="1.2.3.4" reject' --permanent
# 端口转发sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" forward-port port="80" protocol="tcp" to-port="8080"' --permanent
# 端口转发到另一台机器sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" forward-port port="80" protocol="tcp" to-addr="10.0.0.2" to-port="8080"' --permanent
# 日志记录sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="1.2.3.4" log prefix="BAD-IP:" level="warning" limit value="3/m" drop' --permanent
# 查看所有富规则sudo firewall-cmd --zone=public --list-rich-rules
# 删除富规则sudo firewall-cmd --zone=public --remove-rich-rule='rule family="ipv4" source address="1.2.3.4" reject' --permanentsudo firewall-cmd --reload6.6 firewalld 完整配置示例
#!/bin/bash# CentOS/RHEL VPS 初始化防火墙配置
# 设置默认区域sudo firewall-cmd --set-default-zone=public
# 添加基本服务sudo firewall-cmd --zone=public --add-service=ssh --permanentsudo firewall-cmd --zone=public --add-service=http --permanentsudo firewall-cmd --zone=public --add-service=https --permanent
# 添加自定义端口sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent
# 只允许特定 IP 访问 MySQLsudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.100" service name="mysql" accept' --permanentsudo firewall-cmd --zone=public --add-service=mysql --permanent # 注意:先加富规则再移除公共服务
# 限制 SSH 速率sudo firewall-cmd --zone=public --add-rich-rule='rule service name="ssh" limit value="3/m" accept' --permanent
# 重载sudo firewall-cmd --reload
# 验证sudo firewall-cmd --zone=public --list-all七、nftables:下一代防火墙
7.1 为什么需要 nftables
iptables 的问题:
- 语法分散(iptables/ip6tables/arptables/ebtables 四套工具)
- 规则只能逐条添加,不能批量操作
- 不支持集合和字典,大量 IP 需要逐条规则
- 每条规则都需要用户态→内核态切换,性能低
nftables 的改进:
- 统一框架(一个工具管理所有协议族)
- 原生支持集合、字典、映射
- 批量规则提交,性能提升 20-50%
- 语法更简洁一致
- 更好的错误提示
7.2 安装
# Ubuntu/Debian(通常已预装)sudo apt install nftables
# CentOS/RHELsudo dnf install nftables
# 启动并设为开机自启sudo systemctl enable --now nftables
# 检查版本nft --version# nftables 1.0.6 (Lester Gooch)
# 查看当前规则集sudo nft list ruleset7.3 基本语法
# --- 表管理 ---# 创建表sudo nft add table inet filter
# 查看所有表sudo nft list tables
# 删除表sudo nft delete table inet filter
# --- 链管理 ---# 创建链sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'sudo nft add chain inet filter output '{ type filter hook output priority 0; policy accept; }'sudo nft add chain inet filter forward '{ type filter hook forward priority 0; policy drop; }'
# 查看链sudo nft list chain inet filter input
# --- 规则管理 ---# 添加规则sudo nft add rule inet filter input iif "lo" acceptsudo nft add rule inet filter input ct state established,related acceptsudo nft add rule inet filter input tcp dport 22 acceptsudo nft add rule inet filter input tcp dport { 80, 443 } accept
# 插入规则到指定位置sudo nft insert rule inet filter input tcp dport 8080 accept
# 在指定规则后插入sudo nft add rule inet filter input position 3 tcp dport 8443 accept
# 删除规则(需要 handle 号)sudo nft -a list chain inet filter input # -a 显示 handlesudo nft delete rule inet filter input handle 5
# 清空链sudo nft flush chain inet filter input7.4 集合与字典
# --- 集合(set)---# 定义 IP 集合sudo nft add set inet filter blacklist '{ type ipv4_addr; flags interval; }'
# 添加元素sudo nft add element inet filter blacklist '{ 1.2.3.4, 5.6.7.8, 10.0.0.0/8 }'
# 使用集合sudo nft add rule inet filter input ip saddr @blacklist drop
# 动态添加/删除sudo nft add element inet filter blacklist '{ 9.10.11.12 }'sudo nft delete element inet filter blacklist '{ 1.2.3.4 }'
# --- 字典(map)---# 定义端口→动作映射sudo nft add map inet filter port_policy '{ type inet_service : verdict; }'sudo nft add element inet filter port_policy '{ 22 : accept, 80 : accept, 443 : accept, 3306 : drop }'
# 使用字典sudo nft add rule inet filter input tcp dport vmap @port_policy7.5 完整配置示例
#!/bin/bash# nftables 完整防火墙配置
# 清空现有规则sudo nft flush ruleset
# 创建表和链sudo nft -f - << 'EOF'
table inet firewall { # 入站链 chain input { type filter hook input priority 0; policy drop;
# 本地回环 iif "lo" accept
# 已建立连接 ct state established,related accept
# 无效数据包 ct state invalid drop
# ICMP(ping)速率限制 icmp type echo-request limit rate 5/second accept
# SSH 速率限制 tcp dport 22 limit rate 3/minute burst 10 packets accept
# HTTP/HTTPS tcp dport { 80, 443 } accept
# 自定义端口 tcp dport 8080 accept
# 日志记录被拒绝的包 limit rate 10/minute log prefix "NFT-DROP: " level warn }
# 出站链 chain output { type filter hook output priority 0; policy accept; }
# 转发链 chain forward { type filter hook forward priority 0; policy drop;
# 已建立连接 ct state established,related accept
# 内网 → 外网 iif "eth1" oif "eth0" accept }}
table inet nat { # PREROUTING 链(DNAT) chain prerouting { type nat hook prerouting priority -100; policy accept;
# 端口转发:80 → 10.0.0.2:8080 tcp dport 80 dnat to 10.0.0.2:8080 }
# POSTROUTING 链(SNAT/MASQUERADE) chain postrouting { type nat hook postrouting priority 100; policy accept;
# 内网出站伪装 oif "eth0" ip saddr 192.168.1.0/24 masquerade }}
EOF
# 保存规则sudo nft list ruleset | sudo tee /etc/nftables.conf
# 验证sudo nft list ruleset7.6 iptables vs nftables 语法对比
| 操作 | iptables | nftables |
|---|---|---|
| 创建表 | (内置) | nft add table inet filter |
| 创建链 | (内置) | nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }' |
| 允许端口 | iptables -A INPUT -p tcp --dport 80 -j ACCEPT | nft add rule inet filter input tcp dport 80 accept |
| 允许 IP | iptables -A INPUT -s 1.2.3.4 -j ACCEPT | nft add rule inet filter input ip saddr 1.2.3.4 accept |
| 多端口 | -m multiport --dports 80,443 | tcp dport { 80, 443 } |
| 状态匹配 | -m state --state ESTABLISHED | ct state established |
| 速率限制 | -m limit --limit 5/min | limit rate 5/minute |
| NAT | iptables -t nat -A PREROUTING ... -j DNAT --to 10.0.0.2 | nft add rule inet nat prerouting ... dnat to 10.0.0.2 |
| 保存规则 | iptables-save > /etc/iptables/rules.v4 | nft list ruleset > /etc/nftables.conf |
八、Docker 与 iptables 的交互
8.1 Docker 如何使用 iptables
Docker 自动管理 iptables 规则来控制容器网络。启动 Docker 时,它会创建以下自定义链:
# Docker 创建的链(filter 表)DOCKER-USER # 用户自定义规则(Docker 不会覆盖)DOCKER # 容器端口映射规则DOCKER-ISOLATION-STAGE-1 # 网络隔离阶段 1DOCKER-ISOLATION-STAGE-2 # 网络隔离阶段 2
# Docker 创建的链(nat 表)DOCKER # 容器端口映射 DNAT 规则# 查看 Docker 创建的 iptables 规则sudo iptables -L DOCKER -n -vsudo iptables -t nat -L DOCKER -n -vsudo iptables -L DOCKER-USER -n -v8.2 常见冲突与解决方案
问题 1:iptables -F 清空规则后 Docker 容器无法访问
# 错误做法:直接清空所有规则sudo iptables -F # 这会清空 Docker 的规则,容器网络会断开
# 正确做法:重启 Docker 让它重新创建规则sudo systemctl restart docker
# 或者只清空 Docker 之外的自定义链# 先备份规则sudo iptables-save > /tmp/iptables-backup.rules# 然后只修改需要的链,不动 DOCKER 相关链问题 2:自定义规则导致容器无法访问
# 在 DOCKER-USER 链中添加规则(Docker 不会覆盖这个链)# 例如:只允许 1.2.3.4 访问容器 80 端口sudo iptables -I DOCKER-USER -i eth0 -p tcp --dport 80 ! -s 1.2.3.4 -j DROP
# 或者:禁止容器之间互相访问sudo iptables -I DOCKER-USER -i docker0 -o docker0 -j DROP
# 查看 DOCKER-USER 链sudo iptables -L DOCKER-USER -n -v问题 3:Docker 容器端口映射后外部无法访问
# 检查 DOCKER 链是否有 DNAT 规则sudo iptables -t nat -L DOCKER -n -v# 应该看到类似:# DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80
# 检查 FORWARD 链是否允许sudo iptables -L FORWARD -n -v | grep 172.17
# 如果被你的防火墙规则拦截了# 在 DOCKER-USER 链中添加允许规则sudo iptables -I DOCKER-USER -p tcp --dport 8080 -j ACCEPT8.3 禁用 Docker 的 iptables 管理(不推荐)
# 如果确实需要完全控制 iptables(不推荐,会破坏容器网络)# 编辑 /etc/docker/daemon.json{ "iptables": false}
# 重启 Dockersudo systemctl restart docker
# 然后需要手动配置容器网络的 iptables 规则(非常麻烦)最佳实践:不要禁用 Docker 的 iptables 管理。如果你需要自定义规则,放在
DOCKER-USER链中。
九、安全加固实战
9.1 生产级防火墙脚本
#!/bin/bash# ============================================================# 生产级 iptables 防火墙脚本# 适用:VPS / 云服务器# 策略:默认拒绝入站,允许出站,最小化开放端口# ============================================================
# --- 变量定义 ---SSH_PORT=22WEB_PORTS="80,443"CUSTOM_PORT=8080TRUSTED_IP="YOUR_TRUSTED_IP" # 替换为你的管理 IP
# --- 清空所有规则 ---iptables -Fiptables -t nat -Fiptables -t mangle -Fiptables -X # 删除自定义链iptables -t nat -Xiptables -t mangle -X
# --- 设置默认策略 ---iptables -P INPUT DROPiptables -P FORWARD DROPiptables -P OUTPUT ACCEPT
# --- 1. 本地回环 ---iptables -A INPUT -i lo -j ACCEPTiptables -A OUTPUT -o lo -j ACCEPT
# --- 2. 已建立连接 ---iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# --- 3. 无效数据包 ---iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# --- 4. 防 SYN Flood ---# 限制 SYN 包速率iptables -A INPUT -p tcp ! --syn -m conntrack --ctstate NEW -j DROPiptables -A INPUT -p tcp --syn -m limit --limit 10/s --limit-burst 20 -j ACCEPTiptables -A INPUT -p tcp --syn -j DROP
# --- 5. 防端口扫描 ---iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP # NULL 扫描iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP # XMAS 扫描iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP # SYN+FIN 扫描iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP # SYN+RST 扫描iptables -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP # FIN+RST 扫描
# --- 6. ICMP 控制 ---iptables -A INPUT -p icmp -m limit --limit 1/s --limit-burst 5 -j ACCEPTiptables -A INPUT -p icmp -j DROP
# --- 7. SSH 保护 ---# 限制来源 IP(如果指定了可信 IP)if [ -n "$TRUSTED_IP" ] && [ "$TRUSTED_IP" != "YOUR_TRUSTED_IP" ]; then iptables -A INPUT -p tcp --dport $SSH_PORT -s $TRUSTED_IP -j ACCEPTelse # 速率限制(每分钟 5 次,突发 10 次) iptables -A INPUT -p tcp --dport $SSH_PORT -m limit --limit 5/min --limit-burst 10 -j ACCEPT iptables -A INPUT -p tcp --dport $SSH_PORT -j DROPfi
# --- 8. Web 服务 ---iptables -A INPUT -p tcp -m multiport --dports $WEB_PORTS -j ACCEPT
# --- 9. 自定义端口 ---iptables -A INPUT -p tcp --dport $CUSTOM_PORT -j ACCEPT
# --- 10. 日志记录 ---iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "IPTABLES-DROP: " --log-level 4
# --- 11. 最终 DROP ---iptables -A INPUT -j DROP
# --- 保存规则 ---if command -v iptables-save &> /dev/null; then if [ -d /etc/iptables ]; then iptables-save > /etc/iptables/rules.v4 elif [ -d /etc/sysconfig ]; then iptables-save > /etc/sysconfig/iptables fifi
echo "防火墙规则已配置并保存"echo "已开放端口:SSH($SSH_PORT) Web($WEB_PORTS) Custom($CUSTOM_PORT)"iptables -L -n --line-numbers9.2 防暴力破解方案
# --- 方案 1:iptables recent 模块 ---# 60 秒内 SSH 连接超过 5 次的 IP 加入黑名单,保持 600 秒iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --name SSH_BRUTE --setiptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --name SSH_BRUTE --update --seconds 60 --hitcount 5 -j DROPiptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
# --- 方案 2:fail2ban + iptables ---# 安装 fail2bansudo apt install fail2ban # Ubuntusudo dnf install fail2ban # CentOS
# 配置 /etc/fail2ban/jail.localcat << 'EOF' | sudo tee /etc/fail2ban/jail.local[sshd]enabled = trueport = sshfilter = sshdlogpath = /var/log/auth.logmaxretry = 3findtime = 600bantime = 3600banaction = iptables-multiportEOF
sudo systemctl restart fail2bansudo fail2ban-client status sshd
# --- 方案 3:connlimit 限制并发 ---# 每个 IP 最多 3 个 SSH 并发连接iptables -A INPUT -p tcp --dport 22 -m connlimit --connlimit-above 3 -j DROP9.3 安全加固检查清单
| 检查项 | 配置 | 优先级 |
|---|---|---|
| 默认策略 | INPUT DROP, OUTPUT ACCEPT | ★★★★★ |
| 本地回环 | 允许 lo 接口 | ★★★★★ |
| 已建立连接 | ct state ESTABLISHED,RELATED ACCEPT | ★★★★★ |
| SSH 来源限制 | 只允许可信 IP | ★★★★★ |
| SSH 速率限制 | limit/connlimit/recent | ★★★★ |
| SYN Flood 防护 | limit —limit 10/s | ★★★★ |
| 端口扫描防护 | tcp-flags 检查 | ★★★ |
| ICMP 限制 | limit 1/s | ★★★ |
| 日志记录 | LOG + limit | ★★ |
| 规则持久化 | iptables-save | ★★★★★ |
| fail2ban | 部署并配置 | ★★★★ |
| 不必要端口 | 全部关闭 | ★★★★★ |
十、规则持久化
10.1 Ubuntu/Debian
# 安装持久化工具sudo apt install iptables-persistent
# 保存当前规则(安装时会提示)sudo netfilter-persistent save# 规则保存到 /etc/iptables/rules.v4 和 /etc/iptables/rules.v6
# 手动保存sudo iptables-save | sudo tee /etc/iptables/rules.v4sudo ip6tables-save | sudo tee /etc/iptables/rules.v6
# 恢复规则sudo netfilter-persistent reload# 或手动恢复sudo iptables-restore < /etc/iptables/rules.v4
# 开机自动加载(netfilter-persistent 已设为开机自启)sudo systemctl enable netfilter-persistent10.2 CentOS/RHEL
# 保存规则sudo iptables-save | sudo tee /etc/sysconfig/iptablessudo ip6tables-save | sudo tee /etc/sysconfig/ip6tables
# 恢复规则sudo iptables-restore < /etc/sysconfig/iptables
# 如果使用 firewalld,不需要手动保存# firewalld 的 --permanent 选项会自动持久化10.3 nftables 持久化
# 保存规则到配置文件sudo nft list ruleset | sudo tee /etc/nftables.conf
# 开机自启sudo systemctl enable nftables
# 手动加载规则sudo nft -f /etc/nftables.conf
# 重载规则sudo systemctl reload nftables10.4 使用脚本管理(推荐)
# 创建防火墙脚本sudo tee /usr/local/bin/firewall-setup.sh << 'EOF'#!/bin/bash# 防火墙初始化脚本
iptables -Fiptables -P INPUT DROPiptables -P FORWARD DROPiptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPTiptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTiptables -A INPUT -p tcp --dport 22 -m limit --limit 5/min --limit-burst 10 -j ACCEPTiptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPTiptables -A INPUT -j DROP
# 持久化保存iptables-save > /etc/iptables/rules.v4 2>/dev/null || iptables-save > /etc/sysconfig/iptables 2>/dev/nullEOF
sudo chmod +x /usr/local/bin/firewall-setup.sh
# 用 systemd 服务在开机时运行sudo tee /etc/systemd/system/firewall-setup.service << 'EOF'[Unit]Description=Firewall Setup ScriptAfter=network.target
[Service]Type=oneshotExecStart=/usr/local/bin/firewall-setup.shRemainAfterExit=yes
[Install]WantedBy=multi-user.targetEOF
sudo systemctl daemon-reloadsudo systemctl enable firewall-setup.service十一、常见场景实战配置
场景 1:VPS 基础防护
# 最小化开放:SSH + HTTP + HTTPSiptables -Fiptables -P INPUT DROPiptables -P OUTPUT ACCEPTiptables -A INPUT -i lo -j ACCEPTiptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTiptables -A INPUT -p tcp --dport 22 -j ACCEPTiptables -A INPUT -p tcp --dport 80 -j ACCEPTiptables -A INPUT -p tcp --dport 443 -j ACCEPTiptables -A INPUT -j DROP# 保存iptables-save > /etc/iptables/rules.v4场景 2:只允许特定 IP 访问 SSH
# 只允许 1.2.3.4 访问 SSHiptables -A INPUT -p tcp --dport 22 -s 1.2.3.4 -j ACCEPTiptables -A INPUT -p tcp --dport 22 -j DROP
# 允许 IP 段iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPTiptables -A INPUT -p tcp --dport 22 -j DROP场景 3:端口转发到 Docker 容器
# 把外部 8080 端口转发到 Docker 容器 172.17.0.2:80# 注意:Docker -p 8080:80 已经自动做了,这里演示手动方式
# DNATiptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80# 允许转发iptables -A FORWARD -p tcp -d 172.17.0.2 --dport 80 -j ACCEPT# MASQUERADE(让容器回包走本机)iptables -t nat -A POSTROUTING -p tcp -d 172.17.0.2 --dport 80 -j MASQUERADE场景 4:透明代理(重定向到代理端口)
# 把所有 HTTP 流量重定向到本地代理端口 7890iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 7890# 把所有 HTTPS 流量重定向到本地代理端口 7891iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 7891
# 或者把指定 IP 段的流量重定向iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 80 -j REDIRECT --to-ports 7890场景 5:IP 黑白名单
# --- 白名单方式(推荐) ---# 只允许指定 IP 访问,拒绝其他所有iptables -N WHITELIST # 创建自定义链iptables -A WHITELIST -s 1.2.3.4 -j ACCEPTiptables -A WHITELIST -s 192.168.1.0/24 -j ACCEPTiptables -A WHITELIST -j DROP
# 应用到 INPUT 链iptables -A INPUT -j WHITELIST
# --- 黑名单方式 ---# 拒绝指定 IP,允许其他所有iptables -I INPUT 1 -s 1.2.3.4 -j DROPiptables -I INPUT 2 -s 5.6.7.8 -j DROP
# --- 使用 ipset(大量 IP 时更高效) ---sudo apt install ipset# 创建集合sudo ipset create blacklist hash:ip# 添加 IPsudo ipset add blacklist 1.2.3.4sudo ipset add blacklist 5.6.7.8# 在 iptables 中引用iptables -I INPUT 1 -m set --match-set blacklist src -j DROP# 批量导入sudo ipset restore < blacklist.txt场景 6:负载均衡(轮询)
# 把 80 端口流量轮流转发到两台后端服务器# 使用 statistic 模块iptables -t nat -A PREROUTING -p tcp --dport 80 -m statistic --mode random --probability 0.5 -j DNAT --to-destination 10.0.0.2:80iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.3:80
# 允许转发iptables -A FORWARD -p tcp -d 10.0.0.2 --dport 80 -j ACCEPTiptables -A FORWARD -p tcp -d 10.0.0.3 --dport 80 -j ACCEPT
# MASQUERADEiptables -t nat -A POSTROUTING -p tcp -d 10.0.0.2 --dport 80 -j MASQUERADEiptables -t nat -A POSTROUTING -p tcp -d 10.0.0.3 --dport 80 -j MASQUERADE场景 7:VPN 服务器防火墙
# WireGuard / OpenVPN 服务器防火墙# 假设 VPN 接口:wg0,内网网段:10.10.0.0/24
# 允许 VPN 端口iptables -A INPUT -p udp --dport 51820 -j ACCEPT # WireGuard# iptables -A INPUT -p tcp --dport 1194 -j ACCEPT # OpenVPN
# 允许 VPN 接口流量iptables -A INPUT -i wg0 -j ACCEPTiptables -A OUTPUT -o wg0 -j ACCEPT
# 允许 VPN 客户端通过本机访问外网iptables -A FORWARD -i wg0 -o eth0 -j ACCEPTiptables -A FORWARD -i eth0 -o wg0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# MASQUERADEiptables -t nat -A POSTROUTING -s 10.10.0.0/24 -o eth0 -j MASQUERADE
# 开启 IP 转发sysctl -w net.ipv4.ip_forward=1场景 8:限制出站流量
# 只允许服务器访问指定外部端口iptables -P OUTPUT DROP
# 允许已建立连接iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 允许 DNSiptables -A OUTPUT -p udp --dport 53 -j ACCEPTiptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
# 允许 HTTP/HTTPSiptables -A OUTPUT -p tcp --dport 80 -j ACCEPTiptables -A OUTPUT -p tcp --dport 443 -j ACCEPT
# 允许 NTPiptables -A OUTPUT -p udp --dport 123 -j ACCEPT
# 允许 SSHiptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
# 允许 SMTPiptables -A OUTPUT -p tcp --dport 25 -j ACCEPTiptables -A OUTPUT -p tcp --dport 587 -j ACCEPT十二、监控与排错
12.1 查看规则匹配情况
# 查看每条规则匹配了多少包/字节sudo iptables -L -n -v# pkts bytes target prot opt in out source destination# 120 7200 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22# 0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306
# 如果某条规则 pkts=0,说明它从未被匹配过# 如果 DROP 规则有大量匹配,说明有流量被拦截
# 重置计数器sudo iptables -Zsudo iptables -Z INPUT 3 # 只重置第 3 条规则12.2 日志分析
# 查看防火墙日志sudo journalctl -k | grep "IPTABLES-DROP"# 或sudo grep "IPTABLES-DROP" /var/log/syslogsudo grep "IPTABLES-DROP" /var/log/messages
# 日志格式解读# IPTABLES-DROP: IN=eth0 OUT= SRC=1.2.3.4 DST=10.0.0.1 PROTO=TCP SPT=54321 DPT=22 WINDOW=29200 RES=0x00 SYN URGP=0# IN= 入口网卡# SRC= 源 IP# DST= 目标 IP# PROTO= 协议# SPT= 源端口# DPT= 目标端口# SYN TCP 标志(新连接)
# 实时监控被拒绝的连接sudo journalctl -k -f | grep "IPTABLES-DROP"
# 统计被拒绝的 IP(按次数排序)sudo journalctl -k | grep "IPTABLES-DROP" | grep -oP 'SRC=\K[0-9.]+' | sort | uniq -c | sort -rn | head -2012.3 连接跟踪表
# 查看当前连接跟踪数sudo cat /proc/sys/net/netfilter/nf_conntrack_count
# 查看最大连接跟踪数sudo cat /proc/sys/net/netfilter/nf_conntrack_max
# 查看连接跟踪表内容sudo cat /proc/net/nf_conntrack | head -20# ipv4 2 tcp 6 431999 ESTABLISHED src=1.2.3.4 dst=10.0.0.1 sport=54321 dport=22 ...
# 如果连接数接近最大值,需要调大echo 'net.netfilter.nf_conntrack_max = 262144' | sudo tee /etc/sysctl.d/99-conntrack.confsudo sysctl -p
# 查看连接跟踪超时参数sudo sysctl -a | grep nf_conntrack_timeout12.4 测试防火墙规则
# --- 从外部测试端口是否开放 ---
# 使用 nc(netcat)nc -zv 服务器IP 22 # 测试 SSHnc -zv 服务器IP 80 # 测试 HTTP
# 使用 telnettelnet 服务器IP 22
# 使用 nmap(全面扫描)nmap -p 22,80,443,8080 服务器IPnmap -sU -p 53,51820 服务器IP # UDP 扫描
# --- 从服务器内部测试 ---
# 检查服务是否在监听sudo ss -tlnp | grep -E '22|80|443'sudo ss -ulnp | grep -E '53|51820'
# 检查 iptables 是否拦截了某端口sudo iptables -L INPUT -n -v | grep 22
# 追踪数据包经过的规则# 用 LOG 规则在关键位置插入日志iptables -I INPUT 1 -p tcp --dport 22 -j LOG --log-prefix "SSH-CHECK: "# 然后查看日志sudo journalctl -k | grep "SSH-CHECK"# 测试完后删除iptables -D INPUT 112.5 常见问题排查
| 问题 | 可能原因 | 排查方法 |
|---|---|---|
| 外部无法访问端口 | 1. 服务未启动 2. 防火墙拦截 3. 监听 127.0.0.1 | ss -tlnp / iptables -L -n -v |
| SSH 连不上 | 1. 防火墙 DROP 22 2. 默认策略 DROP 未放行 22 | 控制台登录检查 |
| Docker 容器端口不通 | 1. iptables -F 清空了 Docker 规则 2. DOCKER-USER 链有 DROP | iptables -t nat -L DOCKER -n -v |
| 规则重启后丢失 | 未持久化保存 | iptables-save > /etc/iptables/rules.v4 |
| NAT 转发不生效 | 1. 未开 IP 转发 2. FORWARD 链拒绝 3. 回包路由问题 | sysctl net.ipv4.ip_forward / 检查 FORWARD |
| 规则不生效 | 规则顺序错误(前面的 DROP 先匹配) | iptables -L --line-numbers 检查顺序 |
| 性能下降 | conntrack 表满 / 规则太多 | cat /proc/sys/net/netfilter/nf_conntrack_count |
| UFW 和 firewalld 冲突 | 两者同时运行 | 只启用一个 |
| 丢包严重 | 日志规则未限速导致日志洪水 | LOG 规则加 -m limit --limit 5/min |
十三、性能优化
13.1 规则顺序优化
iptables 规则从上到下匹配,第一条匹配的规则生效。把高频匹配的规则放前面:
# 差的顺序(高频规则在后面,每个包都要遍历前面所有规则)iptables -A INPUT -p tcp --dport 9999 -j DROP # 很少匹配iptables -A INPUT -p tcp --dport 8888 -j DROP # 很少匹配iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # 高频!应该在最前面
# 好的顺序iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # 第一条,80% 的包在这里就结束了iptables -A INPUT -i lo -j ACCEPT # 第二条iptables -A INPUT -p tcp --dport 22 -j ACCEPT # 第三条iptables -A INPUT -j DROP # 最后兜底13.2 使用 ipset 优化大量 IP 规则
# 差的做法:每个 IP 一条规则(1000 个 IP = 1000 条规则)iptables -A INPUT -s 1.2.3.4 -j DROPiptables -A INPUT -s 5.6.7.8 -j DROP# ... 1000 条规则
# 好的做法:用 ipset(1 条规则匹配 1000 个 IP)sudo ipset create blacklist hash:ipsudo ipset add blacklist 1.2.3.4sudo ipset add blacklist 5.6.7.8# ... 批量添加iptables -A INPUT -m set --match-set blacklist src -j DROP
# ipset 支持 CIDRsudo ipset create blacklist_nets hash:netsudo ipset add blacklist_nets 10.0.0.0/813.3 调整 conntrack 参数
# 高并发服务器优化sudo tee /etc/sysctl.d/99-conntrack.conf << 'EOF'# 连接跟踪表大小(根据内存调整,每条约 300 字节)net.netfilter.nf_conntrack_max = 262144
# 超时优化# 已建立连接的超时(默认 432000 = 5 天,太长)net.netfilter.nf_conntrack_tcp_timeout_established = 86400
# TIME_WAIT 超时(默认 120)net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
# CLOSE_WAIT 超时(默认 60)net.netfilter.nf_conntrack_tcp_timeout_close_wait = 30
# FIN_WAIT 超时(默认 120)net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 30
# UDP 超时(默认 30)net.netfilter.nf_conntrack_udp_timeout = 15net.netfilter.nf_conntrack_udp_timeout_stream = 60EOF
sudo sysctl -p /etc/sysctl.d/99-conntrack.conf13.4 使用 raw 表绕过 conntrack
# 对于不需要连接跟踪的流量(如大量 UDP),用 raw 表标记 NOTRACK# 减少 conntrack 表的压力iptables -t raw -A PREROUTING -p udp --dport 123 -j NOTRACKiptables -t raw -A OUTPUT -p udp --dport 123 -j NOTRACK十四、IPv6 防火墙
# ip6tables 语法和 iptables 完全一样# 注意:IPv6 没有 NAT(NAT 是 IPv4 的补丁,IPv6 原生支持多地址)
# 基本配置sudo ip6tables -Fsudo ip6tables -P INPUT DROPsudo ip6tables -P FORWARD DROPsudo ip6tables -P OUTPUT ACCEPT
# 本地回环sudo ip6tables -A INPUT -i lo -j ACCEPT
# 已建立连接sudo ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# ICMPv6(IPv6 必须允许,否则邻居发现会失败)sudo ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
# SSHsudo ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
# HTTP/HTTPSsudo ip6tables -A INPUT -p tcp --dport 80 -j ACCEPTsudo ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
# 保存sudo ip6tables-save | sudo tee /etc/iptables/rules.v6关键:IPv6 防火墙必须允许 ICMPv6(
ipv6-icmp),否则 IPv6 邻居发现(NDP)会失败,导致 IPv6 完全不可用。
十五、速查表
命令速查
# 查看规则iptables -L -n -v --line-numbers # filter 表iptables -t nat -L -n -v --line-numbers # nat 表iptables -t mangle -L -n -v # mangle 表
# 添加规则iptables -A INPUT ... # 追加到末尾iptables -I INPUT 1 ... # 插入到第 1 行
# 删除规则iptables -D INPUT 3 # 按行号删除iptables -D INPUT -p tcp --dport 80 ... # 按规则删除
# 设置默认策略iptables -P INPUT DROPiptables -P OUTPUT ACCEPTiptables -P FORWARD DROP
# 保存 / 恢复iptables-save > /etc/iptables/rules.v4iptables-restore < /etc/iptables/rules.v4
# 清空iptables -F # 清空所有链iptables -F INPUT # 清空指定链iptables -X # 删除自定义链最常用规则模板
# 1. 允许已建立连接(必须)iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 2. 允许本地回环(必须)iptables -A INPUT -i lo -j ACCEPT
# 3. 允许 SSHiptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 4. 允许 HTTP/HTTPSiptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
# 5. SSH 速率限制iptables -A INPUT -p tcp --dport 22 -m limit --limit 5/min --limit-burst 10 -j ACCEPT
# 6. 限制单个 IP 并发连接iptables -A INPUT -p tcp --dport 22 -m connlimit --connlimit-above 3 -j DROP
# 7. 防暴力破解iptables -A INPUT -p tcp --dport 22 -m recent --name SSH --setiptables -A INPUT -p tcp --dport 22 -m recent --name SSH --update --seconds 60 --hitcount 5 -j DROP
# 8. 端口转发iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.2:8080iptables -A FORWARD -p tcp -d 10.0.0.2 --dport 8080 -j ACCEPT
# 9. 出站伪装iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# 10. 本地端口重定向iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080UFW 速查
ufw enable # 启用ufw disable # 禁用ufw status verbose # 查看状态ufw allow 80/tcp # 允许端口ufw deny 3306 # 拒绝端口ufw allow from 1.2.3.4 # 允许 IPufw limit 22/tcp # 速率限制ufw delete 3 # 删除第 3 条规则ufw reset # 重置firewalld 速查
firewall-cmd --state # 状态firewall-cmd --zone=public --add-service=http --permanent # 添加服务firewall-cmd --zone=public --add-port=8080/tcp --permanent # 添加端口firewall-cmd --zone=public --list-all # 查看区域firewall-cmd --reload # 重载firewall-cmd --set-default-zone=public # 设置默认区域nftables 速查
nft list ruleset # 查看所有规则nft add table inet filter # 创建表nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }' # 创建链nft add rule inet filter input tcp dport 22 accept # 添加规则nft delete rule inet filter input handle 5 # 删除规则nft flush ruleset # 清空所有nft list ruleset > /etc/nftables.conf # 保存十六、最佳实践清单
10 条核心原则
- 默认拒绝:
iptables -P INPUT DROP,然后只开放需要的端口 - 先放行已建立连接:
ct state ESTABLISHED,RELATED放在第一条(80% 的包在这里就结束了) - 不要忘了 lo:
-i lo -j ACCEPT,否则本地服务互相访问会出问题 - SSH 必须保护:限制来源 IP + 速率限制 + fail2ban
- 规则要持久化:
iptables-save,否则重启后全没了 - 规则顺序很重要:高频匹配规则放前面,DROP 兜底放最后
- 大量 IP 用 ipset:不要为每个 IP 写一条规则
- 不要和 Docker 抢 iptables:自定义规则放
DOCKER-USER链 - 日志要限速:
LOG规则必须加-m limit,否则日志会撑爆磁盘 - 备份规则:修改前先
iptables-save > backup.rules,出问题可以快速恢复
场景推荐
| 场景 | 推荐工具 | 理由 |
|---|---|---|
| 个人 VPS 基础防护 | UFW | 简单直观,5 条命令搞定 |
| 企业服务器管理 | firewalld | 区域管理 + 运行时/永久分离 |
| 精细控制 / NAT | iptables | 功能最全,文档最多 |
| 新系统 / 高性能 | nftables | 性能更好,语法更简洁 |
| Docker 环境 | iptables + DOCKER-USER | 兼容 Docker 自动管理 |
| 大量 IP 黑名单 | ipset + iptables | 性能最优 |
| 防暴力破解 | iptables recent + fail2ban | 双重保护 |
迁移路径
iptables(经典) → iptables-nft(兼容层) → nftables(未来) ↓ ↓ ↓ 老系统维护 过渡期 新系统首选- 老系统:继续用 iptables,没必要迁移
- 新系统:直接用 nftables,或使用 iptables-nft 兼容层
- 迁移:用
iptables-translate工具自动转换规则Terminal window # 将 iptables 规则翻译为 nftables 语法iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT# 输出:nft add rule inet filter input tcp dport 22 counter accept
推荐阅读
- SSH 完全指南:从远程连接到安全加固 — SSH 安全加固中 fail2ban 底层就是 iptables
- systemd 服务管理完全指南 — systemd 管理的服务端口需要防火墙放行
- Nginx 反向代理配置实战 — Nginx 七层限流 + iptables 三四层防护 = 完整安全方案
- Docker Compose v2 完全实战指南 — 理解 Docker 与 iptables 交互
- Linux 常用命令速查手册 — 服务器运维命令基础