菜单

Administrator
发布于 2026-10-12 / 7 阅读
0
0

Linux 进阶 L24:iptables 防火墙进阶——亲手给服务器写规则

Linux 进阶 L24:iptables 防火墙进阶——亲手给服务器写规则

上一课我们用了 ufw 这个"遥控器"给服务器装上防盗门。今天把遥控器放下,直接去配线——iptables 才是真正干活的那扇门。学会它,你的服务器上任何奇奇怪怪的网络需求都能自己写规则解决。

先认清战场:表、链、规则

iptables 的结构是三层:表(table)→ 链(chain)→ 规则(rule)。数据包进来后,先选表,再按链的顺序一条一条过规则。

记住一张图就够了:

Linux 进阶 L24:iptables 防火墙进阶——亲手给服务器写规则
上一课我们用了 ufw 这个"遥控器"给服务器装上防盗门。今天把遥控器放下,直接去配线——iptables 才是真正干活的那扇门。学会它,你的服务器上任何奇奇怪怪的网络需求都能自己写规则解决。

先认清战场:表、链、规则
iptables 的结构是三层:表(table)→ 链(chain)→ 规则(rule)。数据包进来后,先选表,再按链的顺序一条一条过规则。

记住一张图就够了:

数据包进来
  ↓
PREROUTING 链(nat 表)—— 进来第一件事,先路由判定
  ↓        ┌→ 本地进程?→ INPUT 链 → 应用程序(比如你的博客)
  路由判定 ┘
           └→ 转发?  → FORWARD 链 → 后面的机器(Docker 容器就是走这里)
数据包出去
  ↓
OUTPUT 链 → POSTROUTING 链(nat 表)—— 出门前最后一站
表:filter(过滤,默认)、nat(地址转换)、mangle(改包)、raw(原始)。今天 90% 时间在 filter 和 nat。
链:INPUT(进本机)、OUTPUT(本机出去)、FORWARD(路过)、PREROUTING/POSTROUTING(nat 专用)。
规则:一条条"如果…就…",按顺序匹配,命中一条就执行动作,不再往下走。
基本动作只有四个,背下来:

动作	含义
ACCEPT	放行
DROP	悄悄丢掉,对方以为超时
REJECT	明确拒绝,对方立刻收到"此路不通"
LOG	只记录日志,继续往下匹配
读懂现有的规则
先看看你服务器上现在的规则长啥样:

sudo iptables -L -n --line-numbers -v
Chain INPUT (policy DROP 12 packets, 34 bytes)
num   pkts bytes target     prot opt in     out     source        destination
1        0     0 ACCEPT     all  --  lo     *       0.0.0.0/0     0.0.0.0/0
2      512  43892 ACCEPT     tcp  --  *      *       0.0.0.0/0     0.0.0.0/0    tcp dpt:22
3      123  10548 ACCEPT     tcp  --  *      *       0.0.0.0/0     0.0.0.0/0    tcp dpt:80
4       89   7216 ACCEPT     tcp  --  *      *       0.0.0.0/0     0.0.0.0/0    tcp dpt:443
读法:pkts 是这条规则放行/挡掉了多少个包,target 是动作,dpt:22 是目标端口 22。policy DROP 是默认策略:前面所有规则都没命中的包,直接丢掉——这就是上一课说的"白名单"思路。

状态跟踪:一条规则顶十条
iptables 最聪明的设计是连接状态跟踪(conntrack)。一个 TCP 连接有四种状态:

sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
这条规则的意思是:凡是已经建立的连接、或者和已有连接相关的包,直接放行。为什么重要?举个例子:你的服务器 curl 一个外部网站,返回的数据包走 INPUT 链——你总不能为每个临时端口写放行规则吧?有了这条,已建立连接的回包自动放行。

完整的 INPUT 链白名单模板(记住这个顺序,很有讲究):

# 1. 本地回环先放(lo 接口是系统自己跟自己说话,千万别挡)
sudo iptables -A INPUT -i lo -j ACCEPT

# 2. 已建立的连接放行(性能和功能都靠它)
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# 3. 放行业务端口
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT    # SSH
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT    # HTTP
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT   # HTTPS

# 4. 默认策略设为 DROP
sudo iptables -P INPUT DROP
⚠️ 顺序是生死线:规则按顺序匹配,先命中先执行。把 DROP 写在 ACCEPT 前面,等于门都没开就上锁——包括自己。动手前记住保命命令:

# 万一手滑把自己锁外面了(规则还没保存的话),重启即清空
sudo iptables -F        # 清空所有规则(慎用,等于拆门)
sudo iptables -P INPUT ACCEPT  # 先把默认策略改回放行,再慢慢改
新手强烈建议:改规则前开两个 SSH 会话,一个改、一个备用,改坏了直接用备用的救回来。

实战 1:限速防爆破(比 Fail2Ban 更底层)
上一课的 Fail2Ban 是"看门狗",但 iptables 自己就能干限速的活——limit 模块:

# SSH:每分钟最多 3 个新连接,超出的直接丢掉
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m limit --limit 3/min --limit-burst 5 -j ACCEPT
解读:--limit 3/min 是长期平均速率,--limit-burst 5 是允许短时间突发 5 个。正常人登录 SSH 一分钟敲 3 次绰绰有余,爆破脚本一秒几十次,直接撞墙。

更狠的玩法——recent 模块做"三次失败就封 10 分钟"(Fail2Ban 干的也是这事):

# 记录每个 IP 的新连接
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --set --name SSH
# 一分钟内超过 4 次新连接的,封 600 秒
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
真实效果:

# 攻击者视角(用 hydra 爆破时看到的)
hydra -l root -P pass.txt ssh://你的服务器
# [ERROR] all children were disabled due too many tried
服务器端看一眼被挡了多少:

sudo iptables -L INPUT -n -v | grep -i drop
7   1842  112K DROP   tcp  --  *  *  0.0.0.0/0  0.0.0.0/0  tcp dpt:22
1842 个包被这条规则吃掉了——全是爆破流量,连 Fail2Ban 都不用惊动。

实战 2:端口转发(nat 表的拿手好戏)
家里有台 NAS、或者想把 8080 转到某个 Docker 容器?nat 表的 DNAT 就是干这个的:

# 把本机 8080 端口的流量,转到内网 192.168.1.100 的 80 端口
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 \
  -j DNAT --to-destination 192.168.1.100:80

# 回包要能回去,还得开转发和做 SNAT
sudo iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 80 -j ACCEPT
sudo iptables -t nat -A POSTROUTING -p tcp -d 192.168.1.100 --dport 80 \
  -j MASQUERADE
再打开内核转发开关(不然包过不去):

echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
# 永久生效写进 /etc/sysctl.conf:net.ipv4.ip_forward=1
测试:

curl -I http://你的服务器IP:8080
HTTP/1.1 200 OK
Server: nginx/1.24.0
搞定——外部访问 8080,实际打到内网机器上。这招在 VPS 上搭跳板、中转流量时特别常用。

实战 3:挡掉整段恶意 IP
日志里发现某个 C 段一直在扫你?一行命令整段封:

# 挡掉 203.0.113.0/24 整个网段
sudo iptables -A INPUT -s 203.0.113.0/24 -j DROP

# 只看这条规则挡了多少
sudo iptables -L INPUT -n -v --line-numbers | grep 203.0.113
5   9372  562K DROP   all  --  *  *  203.0.113.0/24  0.0.0.0/0
9372 个包,56 万字节的垃圾流量,一条规则全部吃掉。比在应用层一个个封 IP 爽多了——包在内核层就被丢了,连 Nginx 的日志都不会被污染。

规则持久化:重启不丢
iptables 的规则默认只活在内存里,重启就没了。Ubuntu/Debian 用 iptables-persistent:

sudo apt install -y iptables-persistent
# 安装时会问你要不要保存当前规则,选 Yes

# 以后改完规则,手动保存一次:
sudo netfilter-persistent save
规则文件存在 /etc/iptables/rules.v4(IPv4)和 rules.v6(IPv6),开机自动加载。改之前先备份,这是好习惯:

sudo cp /etc/iptables/rules.v4 /etc/iptables/rules.v4.bak
认识 nftables:iptables 的接班人
Debian 10+、Ubuntu 20.04+ 的底层其实已经是 nftables 了——iptables 命令只是个兼容马甲,规则最终都翻译成 nftables 执行。可以验证:

sudo nft list ruleset | head -20
table ip filter {
    chain INPUT {
        type filter hook input priority 0; policy drop;
        iif "lo" accept
        ct state established,related accept
        tcp dport 22 accept
        ...
    }
}
看,是不是比 iptables 的输出清爽多了?nftables 的语法更像写配置,一条命令能表达 iptables 好几条:

# nftables 原生写法:一次性放行 22/80/443
sudo nft add rule ip filter INPUT tcp dport { 22, 80, 443 } accept

# 限速:一分钟 25 个新连接
sudo nft add rule ip filter INPUT tcp dport 22 ct state new \
  limit rate 25/minute accept
nftables 的规则集是原子提交的——要么全生效,要么全不生效,不会出现 iptables 逐条添加时"改到一半断连"的尴尬。救命命令:

sudo nft flush ruleset   # 清空全部(等于拆门,慎用)
新项目建议直接学 nftables,但 iptables 的生态(Fail2Ban、Docker 的规则)还得认——两个都认识,不亏。

小结
知识点	一句话
表→链→规则	数据包按表选链、按链逐条匹配规则
四个基本动作	ACCEPT 放行 / DROP 丢弃 / REJECT 拒绝 / LOG 记录
状态跟踪	ESTABLISHED,RELATED 一条顶十条,回包自动放行
规则顺序	先命中先执行,DROP 写错位置等于把自己锁门外
limit 模块	限速防爆破,内核层直接挡掉
DNAT 端口转发	nat 表 PREROUTING 改目标地址,记得开 ip_forward
持久化	netfilter-persistent save,规则存进 rules.v4
nftables	新一代防火墙,原子提交,语法更清爽
ufw 是遥控器,iptables 是亲手接线。今天之后,你的服务器防盗门不再是"别人装好的",而是你自己一根线一根线接出来的——哪里进水、哪里漏电,你都门儿清。下一课,我们讲 LVM 之后另一个磁盘话题?不,LVM 已经讲过了——下一课继续深挖:Docker 网络与 iptables 的爱恨情仇,敬请期待。
  • 表:filter(过滤,默认)、nat(地址转换)、mangle(改包)、raw(原始)。今天 90% 时间在 filter 和 nat。

  • 链:INPUT(进本机)、OUTPUT(本机出去)、FORWARD(路过)、PREROUTING/POSTROUTING(nat 专用)。

  • 规则:一条条"如果…就…",按顺序匹配,命中一条就执行动作,不再往下走。

基本动作只有四个,背下来:

动作

含义

ACCEPT

放行

DROP

悄悄丢掉,对方以为超时

REJECT

明确拒绝,对方立刻收到"此路不通"

LOG

只记录日志,继续往下匹配

读懂现有的规则

先看看你服务器上现在的规则长啥样:

sudo iptables -L -n --line-numbers -v
Chain INPUT (policy DROP 12 packets, 34 bytes)
num   pkts bytes target     prot opt in     out     source        destination
1        0     0 ACCEPT     all  --  lo     *       0.0.0.0/0     0.0.0.0/0
2      512  43892 ACCEPT     tcp  --  *      *       0.0.0.0/0     0.0.0.0/0    tcp dpt:22
3      123  10548 ACCEPT     tcp  --  *      *       0.0.0.0/0     0.0.0.0/0    tcp dpt:80
4       89   7216 ACCEPT     tcp  --  *      *       0.0.0.0/0     0.0.0.0/0    tcp dpt:443

读法:pkts 是这条规则放行/挡掉了多少个包,target 是动作,dpt:22 是目标端口 22。policy DROP 是默认策略:前面所有规则都没命中的包,直接丢掉——这就是上一课说的"白名单"思路。

状态跟踪:一条规则顶十条

iptables 最聪明的设计是连接状态跟踪(conntrack)。一个 TCP 连接有四种状态:

sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

这条规则的意思是:凡是已经建立的连接、或者和已有连接相关的包,直接放行。为什么重要?举个例子:你的服务器 curl 一个外部网站,返回的数据包走 INPUT 链——你总不能为每个临时端口写放行规则吧?有了这条,已建立连接的回包自动放行。

完整的 INPUT 链白名单模板(记住这个顺序,很有讲究):

# 1. 本地回环先放(lo 接口是系统自己跟自己说话,千万别挡)
sudo iptables -A INPUT -i lo -j ACCEPT

# 2. 已建立的连接放行(性能和功能都靠它)
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# 3. 放行业务端口
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT    # SSH
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT    # HTTP
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT   # HTTPS

# 4. 默认策略设为 DROP
sudo iptables -P INPUT DROP

⚠️ 顺序是生死线:规则按顺序匹配,先命中先执行。把 DROP 写在 ACCEPT 前面,等于门都没开就上锁——包括自己。动手前记住保命命令:

# 万一手滑把自己锁外面了(规则还没保存的话),重启即清空
sudo iptables -F        # 清空所有规则(慎用,等于拆门)
sudo iptables -P INPUT ACCEPT  # 先把默认策略改回放行,再慢慢改

新手强烈建议:改规则前开两个 SSH 会话,一个改、一个备用,改坏了直接用备用的救回来。

实战 1:限速防爆破(比 Fail2Ban 更底层)

上一课的 Fail2Ban 是"看门狗",但 iptables 自己就能干限速的活——limit 模块:

# SSH:每分钟最多 3 个新连接,超出的直接丢掉
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m limit --limit 3/min --limit-burst 5 -j ACCEPT

解读:--limit 3/min 是长期平均速率,--limit-burst 5 是允许短时间突发 5 个。正常人登录 SSH 一分钟敲 3 次绰绰有余,爆破脚本一秒几十次,直接撞墙。

更狠的玩法——recent 模块做"三次失败就封 10 分钟"(Fail2Ban 干的也是这事):

# 记录每个 IP 的新连接
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --set --name SSH
# 一分钟内超过 4 次新连接的,封 600 秒
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP

真实效果:

# 攻击者视角(用 hydra 爆破时看到的)
hydra -l root -P pass.txt ssh://你的服务器
# [ERROR] all children were disabled due too many tried

服务器端看一眼被挡了多少:

sudo iptables -L INPUT -n -v | grep -i drop
7   1842  112K DROP   tcp  --  *  *  0.0.0.0/0  0.0.0.0/0  tcp dpt:22

1842 个包被这条规则吃掉了——全是爆破流量,连 Fail2Ban 都不用惊动。

实战 2:端口转发(nat 表的拿手好戏)

家里有台 NAS、或者想把 8080 转到某个 Docker 容器?nat 表的 DNAT 就是干这个的:

# 把本机 8080 端口的流量,转到内网 192.168.1.100 的 80 端口
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 \
  -j DNAT --to-destination 192.168.1.100:80

# 回包要能回去,还得开转发和做 SNAT
sudo iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 80 -j ACCEPT
sudo iptables -t nat -A POSTROUTING -p tcp -d 192.168.1.100 --dport 80 \
  -j MASQUERADE

再打开内核转发开关(不然包过不去):

echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
# 永久生效写进 /etc/sysctl.conf:net.ipv4.ip_forward=1

测试:

curl -I http://你的服务器IP:8080
HTTP/1.1 200 OK
Server: nginx/1.24.0

搞定——外部访问 8080,实际打到内网机器上。这招在 VPS 上搭跳板、中转流量时特别常用。

实战 3:挡掉整段恶意 IP

日志里发现某个 C 段一直在扫你?一行命令整段封:

# 挡掉 203.0.113.0/24 整个网段
sudo iptables -A INPUT -s 203.0.113.0/24 -j DROP

# 只看这条规则挡了多少
sudo iptables -L INPUT -n -v --line-numbers | grep 203.0.113
5   9372  562K DROP   all  --  *  *  203.0.113.0/24  0.0.0.0/0

9372 个包,56 万字节的垃圾流量,一条规则全部吃掉。比在应用层一个个封 IP 爽多了——包在内核层就被丢了,连 Nginx 的日志都不会被污染。

规则持久化:重启不丢

iptables 的规则默认只活在内存里,重启就没了。Ubuntu/Debian 用 iptables-persistent:

sudo apt install -y iptables-persistent
# 安装时会问你要不要保存当前规则,选 Yes

# 以后改完规则,手动保存一次:
sudo netfilter-persistent save

规则文件存在 /etc/iptables/rules.v4(IPv4)和 rules.v6(IPv6),开机自动加载。改之前先备份,这是好习惯:

sudo cp /etc/iptables/rules.v4 /etc/iptables/rules.v4.bak

认识 nftables:iptables 的接班人

Debian 10+、Ubuntu 20.04+ 的底层其实已经是 nftables 了——iptables 命令只是个兼容马甲,规则最终都翻译成 nftables 执行。可以验证:

sudo nft list ruleset | head -20
table ip filter {
    chain INPUT {
        type filter hook input priority 0; policy drop;
        iif "lo" accept
        ct state established,related accept
        tcp dport 22 accept# 限速:一分钟 25 个新连接
        ...
    }
}

看,是不是比 iptables 的输出清爽多了?nftables 的语法更像写配置,一条命令能表达 iptables 好几条:

# nftables 原生写法:一次性放行 22/80/443
sudo nft add rule ip filter INPUT tcp dport { 22, 80, 443 } accept

# 限速:一分钟 25 个新连接
sudo nft add rule ip filter INPUT tcp dport 22 ct state new \
  limit rate 25/minute accept

nftables 的规则集是原子提交的——要么全生效,要么全不生效,不会出现 iptables 逐条添加时"改到一半断连"的尴尬。救命命令:

sudo nft flush ruleset   # 清空全部(等于拆门,慎用)

新项目建议直接学 nftables,但 iptables 的生态(Fail2Ban、Docker 的规则)还得认——两个都认识,不亏。

小结

知识点

一句话

表→链→规则

数据包按表选链、按链逐条匹配规则

四个基本动作

ACCEPT 放行 / DROP 丢弃 / REJECT 拒绝 / LOG 记录

状态跟踪

ESTABLISHED,RELATED 一条顶十条,回包自动放行

规则顺序

先命中先执行,DROP 写错位置等于把自己锁门外

limit 模块

限速防爆破,内核层直接挡掉

DNAT 端口转发

nat 表 PREROUTING 改目标地址,记得开 ip_forward

持久化

netfilter-persistent save,规则存进 rules.v4

nftables

新一代防火墙,原子提交,语法更清爽

ufw 是遥控器,iptables 是亲手接线。今天之后,你的服务器防盗门不再是"别人装好的",而是你自己一根线一根线接出来的——哪里进水、哪里漏电,你都门儿清。下一课,我们讲 LVM 之后另一个磁盘话题?不,LVM 已经讲过了——下一课继续深挖:Docker 网络与 iptables 的爱恨情仇,敬请期待。


评论