11023 字
55 分钟

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在连接跟踪前处理,标记不走 conntrackPREROUTING, 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、已有规则维护★★★★★
nftablesiptables 继任者新系统、高性能需求★★★★
UFWUbuntu/Debian 前端简单端口管理★★
firewalldRHEL/CentOS 前端区域管理、运行时/永久配置★★★

一句话选择:简单开端口用 UFW/firewalld,精细控制用 iptables/nftables。


二、iptables 基础操作#

2.1 安装与验证#

Terminal window
# Ubuntu/Debian
sudo apt install iptables
# CentOS/RHEL
sudo 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 查看规则#

Terminal window
# 查看所有规则(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:3306

2.3 规则管理命令#

Terminal window
# 追加规则(添加到链的末尾)
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 -Z
sudo iptables -Z INPUT 3 # 只清空第 3 行的计数器
# 设置默认策略
sudo iptables -P INPUT DROP # 默认拒绝所有入站
sudo iptables -P FORWARD DROP # 默认拒绝所有转发
sudo iptables -P OUTPUT ACCEPT # 默认允许所有出站

2.4 命令速查表#

参数含义示例
-AAppend,追加到末尾iptables -A INPUT ...
-IInsert,插入到指定位置iptables -I INPUT 1 ...
-RReplace,替换指定行iptables -R INPUT 3 ...
-DDelete,删除iptables -D INPUT 3
-FFlush,清空iptables -F INPUT
-LList,列出iptables -L -n -v
-PPolicy,设置默认策略iptables -P INPUT DROP
-ZZero,清零计数器iptables -Z
-tTable,指定表iptables -t nat -L
-nNumeric,不解析iptables -L -n
-vVerbose,详细信息iptables -L -v

三、规则匹配条件#

3.1 基本匹配条件#

Terminal window
# 协议匹配
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 # 目标端口 80
iptables -A INPUT -p tcp --sport 1024 -j DROP # 源端口 1024
iptables -A INPUT -p tcp --dport 80:443 -j ACCEPT # 端口范围 80-443
iptables -A INPUT -p tcp -m multiport --dports 80,443,8080 -j ACCEPT # 多端口
# IP 地址匹配
iptables -A INPUT -s 192.168.1.100 -j DROP # 源 IP
iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT # 源 IP 段
iptables -A INPUT -d 10.0.0.1 -j DROP # 目标 IP
iptables -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 高级匹配条件#

Terminal window
# --- 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 ACCEPT
iptables -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 ACCEPT
iptables -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 --set
iptables -A INPUT -p tcp --dport 22 -m recent --name SSH --update --seconds 60 --hitcount 5 -j DROP
iptables -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)允许访问 SSH
iptables -A INPUT -p tcp --dport 22 -m time --timestart 09:00 --timestop 18:00 --weekdays Mon,Tue,Wed,Thu,Fri -j ACCEPT

3.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"
macMAC 地址--mac-source 00:11:22:33:44:55
time时间段--timestart 09:00 --timestop 18:00
ttlTTL 值--ttl-eq 64
owner用户/组--uid-owner 1000
tcp-flagsTCP 标志--tcp-flags SYN,RST,ACK SYN

3.4 目标动作(Target)#

Terminal window
# --- 基本动作 ---
-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 对比

特性DROPREJECT
响应无响应发送 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)#

Terminal window
# --- 场景:把外部 80 端口转发到内网 10.0.0.2:8080 ---
# 步骤 1:在 PREROUTING 链做 DNAT
sudo 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.conf
sudo sysctl -p
# 步骤 4(如果内网机器回包需要走本机):做 SNAT
sudo iptables -t nat -A POSTROUTING -p tcp -d 10.0.0.2 --dport 8080 -j MASQUERADE
Terminal window
# --- 场景:本地端口重定向 ---
# 把 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
Terminal window
# --- 场景:根据来源 IP 转发到不同后端 ---
# 192.168.1.0/24 → 10.0.0.2:8080
sudo 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:8080
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.3:8080

4.3 出站伪装(MASQUERADE / SNAT)#

Terminal window
# --- 场景:内网通过本机访问外网 ---
# 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-30000

MASQUERADE vs SNAT

特性MASQUERADESNAT
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 -F
iptables -t nat -F
iptables -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 DROP

4.5 查看 NAT 规则#

Terminal window
# 查看 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.conf
sudo sysctl -p

五、UFW:Ubuntu/Debian 的简化防火墙#

5.1 安装与启用#

Terminal window
# 安装
sudo apt install ufw
# 启用(会自动设置默认策略)
sudo ufw enable
# 默认策略:入站拒绝,出站允许
# 查看状态
sudo ufw status
sudo ufw status verbose # 详细信息
sudo ufw status numbered # 带行号
# 禁用
sudo ufw disable
# 重置(清除所有规则,恢复默认)
sudo ufw reset

5.2 基本规则#

Terminal window
# --- 端口管理 ---
# 允许 SSH
sudo ufw allow 22/tcp
sudo ufw allow ssh # 按服务名(/etc/services 中定义)
# 允许 HTTP/HTTPS
sudo ufw allow 80/tcp
sudo 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
# 拒绝指定 IP
sudo ufw deny from 1.2.3.4
# 拒绝指定 IP 到指定端口
sudo ufw deny from 1.2.3.4 to any port 22
# --- 删除规则 ---
# 按行号删除
sudo ufw status numbered
sudo ufw delete 3
# 按规则删除
sudo ufw delete allow 80/tcp

5.3 默认策略#

Terminal window
# 设置默认策略
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 日志#

Terminal window
# 开启日志
sudo ufw logging on
sudo 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=22

5.5 UFW 完整配置示例#

#!/bin/bash
# VPS 初始化防火墙配置
# 重置
sudo ufw --force reset
# 默认策略
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 允许 SSH(限制来源 IP)
sudo ufw allow from YOUR_IP to any port 22 proto tcp
# 允许 HTTP/HTTPS
sudo ufw allow 80/tcp
sudo 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 安装与基本操作#

Terminal window
# 安装
sudo dnf install firewalld
# 启动并设为开机自启
sudo systemctl start firewalld
sudo systemctl enable firewalld
# 查看状态
sudo firewall-cmd --state
sudo 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-all

6.3 端口与服务管理#

Terminal window
# --- 服务管理 ---
# firewalld 预定义了常见服务,用服务名操作更方便
# 查看所有预定义服务
sudo firewall-cmd --get-services
# 添加服务(运行时,重启后失效)
sudo firewall-cmd --zone=public --add-service=http
sudo firewall-cmd --zone=public --add-service=https
sudo firewall-cmd --zone=public --add-service=ssh
# 永久添加服务(需要 reload 才生效)
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --reload
# 移除服务
sudo firewall-cmd --zone=public --remove-service=http --permanent
sudo firewall-cmd --reload
# --- 端口管理 ---
# 添加端口
sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent
sudo firewall-cmd --zone=public --add-port=3000-3100/tcp --permanent
sudo firewall-cmd --reload
# 移除端口
sudo firewall-cmd --zone=public --remove-port=8080/tcp --permanent
sudo firewall-cmd --reload
# 查看已开放的端口和服务
sudo firewall-cmd --zone=public --list-ports
sudo firewall-cmd --zone=public --list-services

6.4 区域切换与网卡绑定#

Terminal window
# 更改默认区域
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 --permanent
sudo firewall-cmd --reload

6.5 富规则(Rich Rules)#

firewalld 的富规则提供更细粒度的控制:

Terminal window
# 基本语法
rule [family="ipv4|ipv6"] [source] [destination] service|port|protocol|icmp-block|masquerade|forward-port [log] [audit] [accept|reject|drop]
# 只允许 192.168.1.0/24 访问 SSH
sudo 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' --permanent
sudo firewall-cmd --reload

6.6 firewalld 完整配置示例#

#!/bin/bash
# CentOS/RHEL VPS 初始化防火墙配置
# 设置默认区域
sudo firewall-cmd --set-default-zone=public
# 添加基本服务
sudo firewall-cmd --zone=public --add-service=ssh --permanent
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --zone=public --add-service=https --permanent
# 添加自定义端口
sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent
# 只允许特定 IP 访问 MySQL
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.100" service name="mysql" accept' --permanent
sudo 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 安装#

Terminal window
# Ubuntu/Debian(通常已预装)
sudo apt install nftables
# CentOS/RHEL
sudo dnf install nftables
# 启动并设为开机自启
sudo systemctl enable --now nftables
# 检查版本
nft --version
# nftables 1.0.6 (Lester Gooch)
# 查看当前规则集
sudo nft list ruleset

7.3 基本语法#

Terminal window
# --- 表管理 ---
# 创建表
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" accept
sudo nft add rule inet filter input ct state established,related accept
sudo nft add rule inet filter input tcp dport 22 accept
sudo 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 显示 handle
sudo nft delete rule inet filter input handle 5
# 清空链
sudo nft flush chain inet filter input

7.4 集合与字典#

Terminal window
# --- 集合(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_policy

7.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 ruleset

7.6 iptables vs nftables 语法对比#

操作iptablesnftables
创建表(内置)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 ACCEPTnft add rule inet filter input tcp dport 80 accept
允许 IPiptables -A INPUT -s 1.2.3.4 -j ACCEPTnft add rule inet filter input ip saddr 1.2.3.4 accept
多端口-m multiport --dports 80,443tcp dport { 80, 443 }
状态匹配-m state --state ESTABLISHEDct state established
速率限制-m limit --limit 5/minlimit rate 5/minute
NATiptables -t nat -A PREROUTING ... -j DNAT --to 10.0.0.2nft add rule inet nat prerouting ... dnat to 10.0.0.2
保存规则iptables-save > /etc/iptables/rules.v4nft list ruleset > /etc/nftables.conf

八、Docker 与 iptables 的交互#

8.1 Docker 如何使用 iptables#

Docker 自动管理 iptables 规则来控制容器网络。启动 Docker 时,它会创建以下自定义链:

# Docker 创建的链(filter 表)
DOCKER-USER # 用户自定义规则(Docker 不会覆盖)
DOCKER # 容器端口映射规则
DOCKER-ISOLATION-STAGE-1 # 网络隔离阶段 1
DOCKER-ISOLATION-STAGE-2 # 网络隔离阶段 2
# Docker 创建的链(nat 表)
DOCKER # 容器端口映射 DNAT 规则
Terminal window
# 查看 Docker 创建的 iptables 规则
sudo iptables -L DOCKER -n -v
sudo iptables -t nat -L DOCKER -n -v
sudo iptables -L DOCKER-USER -n -v

8.2 常见冲突与解决方案#

问题 1:iptables -F 清空规则后 Docker 容器无法访问

Terminal window
# 错误做法:直接清空所有规则
sudo iptables -F # 这会清空 Docker 的规则,容器网络会断开
# 正确做法:重启 Docker 让它重新创建规则
sudo systemctl restart docker
# 或者只清空 Docker 之外的自定义链
# 先备份规则
sudo iptables-save > /tmp/iptables-backup.rules
# 然后只修改需要的链,不动 DOCKER 相关链

问题 2:自定义规则导致容器无法访问

Terminal window
# 在 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 容器端口映射后外部无法访问

Terminal window
# 检查 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 ACCEPT

8.3 禁用 Docker 的 iptables 管理(不推荐)#

Terminal window
# 如果确实需要完全控制 iptables(不推荐,会破坏容器网络)
# 编辑 /etc/docker/daemon.json
{
"iptables": false
}
# 重启 Docker
sudo systemctl restart docker
# 然后需要手动配置容器网络的 iptables 规则(非常麻烦)

最佳实践:不要禁用 Docker 的 iptables 管理。如果你需要自定义规则,放在 DOCKER-USER 链中。


九、安全加固实战#

9.1 生产级防火墙脚本#

#!/bin/bash
# ============================================================
# 生产级 iptables 防火墙脚本
# 适用:VPS / 云服务器
# 策略:默认拒绝入站,允许出站,最小化开放端口
# ============================================================
# --- 变量定义 ---
SSH_PORT=22
WEB_PORTS="80,443"
CUSTOM_PORT=8080
TRUSTED_IP="YOUR_TRUSTED_IP" # 替换为你的管理 IP
# --- 清空所有规则 ---
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X # 删除自定义链
iptables -t nat -X
iptables -t mangle -X
# --- 设置默认策略 ---
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# --- 1. 本地回环 ---
iptables -A INPUT -i lo -j ACCEPT
iptables -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 DROP
iptables -A INPUT -p tcp --syn -m limit --limit 10/s --limit-burst 20 -j ACCEPT
iptables -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 ACCEPT
iptables -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 ACCEPT
else
# 速率限制(每分钟 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 DROP
fi
# --- 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
fi
fi
echo "防火墙规则已配置并保存"
echo "已开放端口:SSH($SSH_PORT) Web($WEB_PORTS) Custom($CUSTOM_PORT)"
iptables -L -n --line-numbers

9.2 防暴力破解方案#

Terminal window
# --- 方案 1:iptables recent 模块 ---
# 60 秒内 SSH 连接超过 5 次的 IP 加入黑名单,保持 600 秒
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --name SSH_BRUTE --set
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --name SSH_BRUTE --update --seconds 60 --hitcount 5 -j DROP
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
# --- 方案 2:fail2ban + iptables ---
# 安装 fail2ban
sudo apt install fail2ban # Ubuntu
sudo dnf install fail2ban # CentOS
# 配置 /etc/fail2ban/jail.local
cat << 'EOF' | sudo tee /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
findtime = 600
bantime = 3600
banaction = iptables-multiport
EOF
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
# --- 方案 3:connlimit 限制并发 ---
# 每个 IP 最多 3 个 SSH 并发连接
iptables -A INPUT -p tcp --dport 22 -m connlimit --connlimit-above 3 -j DROP

9.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#

Terminal window
# 安装持久化工具
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.v4
sudo 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-persistent

10.2 CentOS/RHEL#

Terminal window
# 保存规则
sudo iptables-save | sudo tee /etc/sysconfig/iptables
sudo ip6tables-save | sudo tee /etc/sysconfig/ip6tables
# 恢复规则
sudo iptables-restore < /etc/sysconfig/iptables
# 如果使用 firewalld,不需要手动保存
# firewalld 的 --permanent 选项会自动持久化

10.3 nftables 持久化#

Terminal window
# 保存规则到配置文件
sudo nft list ruleset | sudo tee /etc/nftables.conf
# 开机自启
sudo systemctl enable nftables
# 手动加载规则
sudo nft -f /etc/nftables.conf
# 重载规则
sudo systemctl reload nftables

10.4 使用脚本管理(推荐)#

# 创建防火墙脚本
sudo tee /usr/local/bin/firewall-setup.sh << 'EOF'
#!/bin/bash
# 防火墙初始化脚本
iptables -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
iptables -A INPUT -p tcp --dport 22 -m limit --limit 5/min --limit-burst 10 -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
iptables -A INPUT -j DROP
# 持久化保存
iptables-save > /etc/iptables/rules.v4 2>/dev/null || iptables-save > /etc/sysconfig/iptables 2>/dev/null
EOF
sudo chmod +x /usr/local/bin/firewall-setup.sh
# 用 systemd 服务在开机时运行
sudo tee /etc/systemd/system/firewall-setup.service << 'EOF'
[Unit]
Description=Firewall Setup Script
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/firewall-setup.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable firewall-setup.service

十一、常见场景实战配置#

场景 1:VPS 基础防护#

Terminal window
# 最小化开放:SSH + HTTP + HTTPS
iptables -F
iptables -P INPUT DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate 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
iptables -A INPUT -j DROP
# 保存
iptables-save > /etc/iptables/rules.v4

场景 2:只允许特定 IP 访问 SSH#

Terminal window
# 只允许 1.2.3.4 访问 SSH
iptables -A INPUT -p tcp --dport 22 -s 1.2.3.4 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
# 允许 IP 段
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP

场景 3:端口转发到 Docker 容器#

Terminal window
# 把外部 8080 端口转发到 Docker 容器 172.17.0.2:80
# 注意:Docker -p 8080:80 已经自动做了,这里演示手动方式
# DNAT
iptables -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:透明代理(重定向到代理端口)#

Terminal window
# 把所有 HTTP 流量重定向到本地代理端口 7890
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 7890
# 把所有 HTTPS 流量重定向到本地代理端口 7891
iptables -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 黑白名单#

Terminal window
# --- 白名单方式(推荐) ---
# 只允许指定 IP 访问,拒绝其他所有
iptables -N WHITELIST # 创建自定义链
iptables -A WHITELIST -s 1.2.3.4 -j ACCEPT
iptables -A WHITELIST -s 192.168.1.0/24 -j ACCEPT
iptables -A WHITELIST -j DROP
# 应用到 INPUT 链
iptables -A INPUT -j WHITELIST
# --- 黑名单方式 ---
# 拒绝指定 IP,允许其他所有
iptables -I INPUT 1 -s 1.2.3.4 -j DROP
iptables -I INPUT 2 -s 5.6.7.8 -j DROP
# --- 使用 ipset(大量 IP 时更高效) ---
sudo apt install ipset
# 创建集合
sudo ipset create blacklist hash:ip
# 添加 IP
sudo ipset add blacklist 1.2.3.4
sudo 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:负载均衡(轮询)#

Terminal window
# 把 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:80
iptables -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 ACCEPT
iptables -A FORWARD -p tcp -d 10.0.0.3 --dport 80 -j ACCEPT
# MASQUERADE
iptables -t nat -A POSTROUTING -p tcp -d 10.0.0.2 --dport 80 -j MASQUERADE
iptables -t nat -A POSTROUTING -p tcp -d 10.0.0.3 --dport 80 -j MASQUERADE

场景 7:VPN 服务器防火墙#

Terminal window
# 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 ACCEPT
iptables -A OUTPUT -o wg0 -j ACCEPT
# 允许 VPN 客户端通过本机访问外网
iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o wg0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# MASQUERADE
iptables -t nat -A POSTROUTING -s 10.10.0.0/24 -o eth0 -j MASQUERADE
# 开启 IP 转发
sysctl -w net.ipv4.ip_forward=1

场景 8:限制出站流量#

Terminal window
# 只允许服务器访问指定外部端口
iptables -P OUTPUT DROP
# 允许已建立连接
iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 允许 DNS
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
# 允许 HTTP/HTTPS
iptables -A OUTPUT -p tcp --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT
# 允许 NTP
iptables -A OUTPUT -p udp --dport 123 -j ACCEPT
# 允许 SSH
iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
# 允许 SMTP
iptables -A OUTPUT -p tcp --dport 25 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 587 -j ACCEPT

十二、监控与排错#

12.1 查看规则匹配情况#

Terminal window
# 查看每条规则匹配了多少包/字节
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 -Z
sudo iptables -Z INPUT 3 # 只重置第 3 条规则

12.2 日志分析#

Terminal window
# 查看防火墙日志
sudo journalctl -k | grep "IPTABLES-DROP"
# 或
sudo grep "IPTABLES-DROP" /var/log/syslog
sudo 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 -20

12.3 连接跟踪表#

Terminal window
# 查看当前连接跟踪数
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.conf
sudo sysctl -p
# 查看连接跟踪超时参数
sudo sysctl -a | grep nf_conntrack_timeout

12.4 测试防火墙规则#

Terminal window
# --- 从外部测试端口是否开放 ---
# 使用 nc(netcat)
nc -zv 服务器IP 22 # 测试 SSH
nc -zv 服务器IP 80 # 测试 HTTP
# 使用 telnet
telnet 服务器IP 22
# 使用 nmap(全面扫描)
nmap -p 22,80,443,8080 服务器IP
nmap -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 1

12.5 常见问题排查#

问题可能原因排查方法
外部无法访问端口1. 服务未启动 2. 防火墙拦截 3. 监听 127.0.0.1ss -tlnp / iptables -L -n -v
SSH 连不上1. 防火墙 DROP 22 2. 默认策略 DROP 未放行 22控制台登录检查
Docker 容器端口不通1. iptables -F 清空了 Docker 规则 2. DOCKER-USER 链有 DROPiptables -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 规则从上到下匹配,第一条匹配的规则生效。把高频匹配的规则放前面:

Terminal window
# 差的顺序(高频规则在后面,每个包都要遍历前面所有规则)
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 规则#

Terminal window
# 差的做法:每个 IP 一条规则(1000 个 IP = 1000 条规则)
iptables -A INPUT -s 1.2.3.4 -j DROP
iptables -A INPUT -s 5.6.7.8 -j DROP
# ... 1000 条规则
# 好的做法:用 ipset(1 条规则匹配 1000 个 IP)
sudo ipset create blacklist hash:ip
sudo ipset add blacklist 1.2.3.4
sudo ipset add blacklist 5.6.7.8
# ... 批量添加
iptables -A INPUT -m set --match-set blacklist src -j DROP
# ipset 支持 CIDR
sudo ipset create blacklist_nets hash:net
sudo ipset add blacklist_nets 10.0.0.0/8

13.3 调整 conntrack 参数#

Terminal window
# 高并发服务器优化
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 = 15
net.netfilter.nf_conntrack_udp_timeout_stream = 60
EOF
sudo sysctl -p /etc/sysctl.d/99-conntrack.conf

13.4 使用 raw 表绕过 conntrack#

Terminal window
# 对于不需要连接跟踪的流量(如大量 UDP),用 raw 表标记 NOTRACK
# 减少 conntrack 表的压力
iptables -t raw -A PREROUTING -p udp --dport 123 -j NOTRACK
iptables -t raw -A OUTPUT -p udp --dport 123 -j NOTRACK

十四、IPv6 防火墙#

Terminal window
# ip6tables 语法和 iptables 完全一样
# 注意:IPv6 没有 NAT(NAT 是 IPv4 的补丁,IPv6 原生支持多地址)
# 基本配置
sudo ip6tables -F
sudo ip6tables -P INPUT DROP
sudo ip6tables -P FORWARD DROP
sudo 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
# SSH
sudo ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
# HTTP/HTTPS
sudo ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo 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 完全不可用。


十五、速查表#

命令速查#

Terminal window
# 查看规则
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 DROP
iptables -P OUTPUT ACCEPT
iptables -P FORWARD DROP
# 保存 / 恢复
iptables-save > /etc/iptables/rules.v4
iptables-restore < /etc/iptables/rules.v4
# 清空
iptables -F # 清空所有链
iptables -F INPUT # 清空指定链
iptables -X # 删除自定义链

最常用规则模板#

Terminal window
# 1. 允许已建立连接(必须)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 2. 允许本地回环(必须)
iptables -A INPUT -i lo -j ACCEPT
# 3. 允许 SSH
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 4. 允许 HTTP/HTTPS
iptables -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 --set
iptables -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:8080
iptables -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 8080

UFW 速查#

Terminal window
ufw enable # 启用
ufw disable # 禁用
ufw status verbose # 查看状态
ufw allow 80/tcp # 允许端口
ufw deny 3306 # 拒绝端口
ufw allow from 1.2.3.4 # 允许 IP
ufw limit 22/tcp # 速率限制
ufw delete 3 # 删除第 3 条规则
ufw reset # 重置

firewalld 速查#

Terminal window
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 速查#

Terminal window
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 条核心原则#

  1. 默认拒绝iptables -P INPUT DROP,然后只开放需要的端口
  2. 先放行已建立连接ct state ESTABLISHED,RELATED 放在第一条(80% 的包在这里就结束了)
  3. 不要忘了 lo-i lo -j ACCEPT,否则本地服务互相访问会出问题
  4. SSH 必须保护:限制来源 IP + 速率限制 + fail2ban
  5. 规则要持久化iptables-save,否则重启后全没了
  6. 规则顺序很重要:高频匹配规则放前面,DROP 兜底放最后
  7. 大量 IP 用 ipset:不要为每个 IP 写一条规则
  8. 不要和 Docker 抢 iptables:自定义规则放 DOCKER-USER
  9. 日志要限速LOG 规则必须加 -m limit,否则日志会撑爆磁盘
  10. 备份规则:修改前先 iptables-save > backup.rules,出问题可以快速恢复

场景推荐#

场景推荐工具理由
个人 VPS 基础防护UFW简单直观,5 条命令搞定
企业服务器管理firewalld区域管理 + 运行时/永久分离
精细控制 / NATiptables功能最全,文档最多
新系统 / 高性能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

推荐阅读#

Linux 防火墙完全指南:iptables + nftables + UFW + firewalld | 2026 最新实践
https://971918.xyz/posts/docs/linux-firewall-complete-guide/
作者
九所长
发布于
2026-07-09
许可协议
CC BY-NC-SA 4.0