news 2026/10/3 17:56:10

Linux防火墙从原理到实战:netfilter、iptables与firewalld配置排查指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux防火墙从原理到实战:netfilter、iptables与firewalld配置排查指南

做运维这些年,被问得最多的一个问题,不是某个中间件怎么调优,而是“防火墙到底能不能关”。尤其是新人,遇到服务连不上,第一反应就是systemctl stop firewalld,甚至iptables -F把规则全冲掉。这种操作我太理解了,毕竟“关了就能通”是最大的诱惑,但代价是把机器裸奔在公网上。今天这篇就围绕 Linux 防火墙管理这个主题,把 netfilter 底层的原理、iptables/firewalld/nftables 的选型思路、常用配置命令、性能优化、故障排查完整串一遍。不管你是刚入行的运维,还是被线上事故吊打过的老兵,应该都能在这里找到点自己用得上的东西。更重要的是,我希望你看完之后,能形成一套自己的防火墙管理习惯,而不是继续靠“开关服务”来解决问题。

1. 防火墙的底牌:netfilter、表和链

1.1 netfilter 不是软件,是内核钩子

先纠正一个普遍误区:Linux 防火墙从来不是iptables这个命令,也不是firewalld这个服务,而是内核里的 netfilter 框架。iptables和firewalld只是用户态的管理工具,真正干活的是内核态。这就好比门禁系统里,刷卡机只是交互界面,真正检查你有没有权限开门的是后台服务器。

数据包从网卡进来之后,会沿着一条固定路径在内核里走一遍。这条路径上有五个关键钩子点,分别叫做 PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING。每个钩子点,内核都会发起一场“检查”,看用户态有没有下发规则来匹配这个包。匹配上了就按规则动作执行,ACCEPT 放行、DROP 丢包、REJECT 拒绝,或者跳转到其他规则继续检查。

我见过有人把 netfilter 理解成“一堵墙”,其实不太准确。它更像是一条流水线上的多个质检工位,每个工位负责不同工序:地址转换、流量整形、包过滤、连接跟踪。理解了钩子顺序,你才能明白为什么 DNAT 规则要写在 PREROUTING,而 SNAT 规则要写在 POSTROUTING,顺序错了,包就会在你意想不到的地方消失。

1.2 四表五链,其实只需要关注两个表

教材里说的“四表五链”经常把人劝退。我的理解方式是:链是路,表是事。一个数据包走哪条路,路上的每个节点有几件事要处理,这就是表和链的关系。

四个标准表是 raw、mangle、nat、filter,每个表在特定钩子点上有自己的链。比如 nat 表的工作集中在 PREROUTING、INPUT、OUTPUT、POSTROUTING 这四条链上,filter 表主要在 INPUT、FORWARD、OUTPUT 三条链上。实际运维中,90% 的需求只落在 filter 表和 nat 表上,raw 和 mangle 表更多用于特殊场景,比如给包打标记、绕过 conntrack,平时碰得不多。

为什么要强调这一点?因为规则放错表是特别经典的错误。举个例子:你想把公网 IP 的 8080 端口映射到内网一台机器,DNAT 规则应该写进 nat 表的 PREROUTING 链。有朋友图省事,直接把这条规则写在 filter 表的 INPUT 链上,结果外部访问一直不通,他自己还纳闷,明明规则存在啊。规则在,但放错了流水线工位,包根本不会经过那里。

1.3 iptables、firewalld、nftables 怎么选

这三个名字经常把人绕晕。其实它们的关系不是互相替代,而是站在同一个 netfilter 地基上的不同操作界面。

iptables是最传统的命令,语法直观,适合一次性排查问题或写脚本。但它的原生规则不动态,需要手动保存和加载。firewalld是 CentOS/RHEL 7 之后的默认防火墙服务,引入 zone 概念,支持动态加载,改完规则不用完全重启防火墙,而且可以--reload保留当前会话。nftables是新一代框架,语法比 iptables 更紧凑,性能也更好,目前 Debian 和较新的 RHEL 系列都在往它身上迁移。

很多人在 CentOS 7 上输入iptables -L能看到规则,就以为系统跑的是 iptables,实际上 firewalld 的后端可能是 nftables。这不算冲突,但你写规则时要意识到:firewalld 会管理自己的规则集,你直接用iptables命令临时添加的规则,重载 firewalld 后很容易被清掉。后文会专门讲这个坑。

选型建议:新服务器优先用好 firewalld,因为它跟系统机制契合,支持 zone、富规则、动态重载;老服务器继续用 iptables 也没问题,只要确认没有 firewalld 在背后干扰。没必要为了赶时髦把稳定的生产机器强行迁到 nftables,除非你有明确的性能或语法诉求。

2. 从 firewalld 到 iptables:常用配置实操

2.1 firewalld 的 zone 是一套规则集合

firewalld 最核心的概念是 zone,我习惯把它理解成“多个隔间”,每个网络接口可以划分到不同隔间,不同隔间有一套单独的放行规则。默认 zone 是 public,意思是这个网络区域是不信任的,只能放行极少量的基本服务。

实际部署时,我通常会把内网接口加到 trusted 区域,公网接口留在 public 区域。这样内网访问完全放开,公网访问按白名单走。常用操作命令如下:

firewall-cmd --get-default-zone firewall-cmd --zone=public --list-all firewall-cmd --permanent --zone=public --add-port=8080/tcp firewall-cmd --permanent --zone=public --add-rich-rule='rule family=ipv4 source address=192.168.10.0/24 port port="3306" protocol=tcp accept' firewall-cmd --reload

注意--permanent和--reload的关系。--permanent只把规则写进配置文件,不会立即生效;必须执行firewall-cmd --reload才会重新加载配置。我见过有人只加了--permanent忘了 reload,测试端口不通,就以为规则没生效,其实规则已经在文件里等着了。反过来,如果你在生产环境用临时规则排查问题,不要加--permanent,改完直接测试,测完--reload就可以还原到之前的永久配置。

2.2 iptables 命令写法与规则保存

虽然新系统都在用 firewalld,但iptables命令依然值得熟记,因为很多容器环境、NetworkManager 脚本、SDN 网络方案还会直接操作 iptables,而且排查问题时用iptables -L看明细非常快。

iptables 命令的套路是:指定表、指定链、匹配条件、动作。比如最经典的三连:

iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/8 -j ACCEPT iptables -P INPUT DROP

第一行是放行回环接口 lo,否则本机内部通信都会乱掉,SSH 也会受牵连。第二行是放行已建立和相关的连接,这是保证 TCP 连接能正常回包的关键。第三行是允许办公网段访问 SSH。最后一行设置默认策略为 DROP,意思是其他所有 INPUT 请求一律丢弃。这里有一个小细节:-I是插入到前面,-A是追加到后面,iptables 按顺序匹配,所以常用规则要放在前面,匹配到你想要的规则时后面就不会继续执行了。

保存规则是最容易忽略的环节。裸敲iptables命令只对当前运行的内核生效,重启后全部消失。你可以这样保存:

iptables-save > /etc/iptables/rules.v4 iptables-restore < /etc/iptables/rules.v4

或者在安装了 iptables-services 的发行版上直接:

systemctl enable iptables service iptables save

第一次用 iptables 的时候我就吃过亏,半夜加了一条规则,以为万事大吉,结果主机一重启服务全暴露了。从那以后我再也不敢只敲命令不保存。

2.3 配置一台 Web 服务器的标准流程

光讲命令不结合场景容易飘,我拿一台典型的 Web 服务器举例:开放 80/443 给所有人访问,SSH 只允许公司办公网段连接,其他所有入站请求拒绝。

用 firewalld 一步步做,最后的效果就是白名单模式。先把默认 zone 设成 public,然后添加服务:

firewall-cmd --set-default-zone=public firewall-cmd --permanent --zone=public --add-service=http firewall-cmd --permanent --zone=public --add-service=https firewall-cmd --permanent --zone=public --add-rich-rule='rule family=ipv4 source address=192.168.1.0/24 port port="22" protocol=tcp accept' firewall-cmd --reload

注意,我没有直接--add-service=ssh,因为 ssh 应该只对公司网段开放,用富规则更精细。验证规则时,先在本机看firewall-cmd --zone=public --list-all,确认 http、https 和 SSH 富规则都在;再从外部用nc -vz 服务器IP 80测端口,用nc -vz 服务器IP 22测 SSH。外部访问 22 应该超时或拒绝,而访问 80 应该成功。这个流程虽然简单,但能避免“规则看起来在,实际没生效”的错觉。

3. 规则优化、日志与攻防细节

3.1 规则顺序和 conntrack 对性能的影响

防火墙不是无限性能的,规则越多、匹配越多,损耗越大。尤其在并发量高的场景下,规则顺序能直接影响 CPU 使用率。

一个核心原则是:匹配频率高的规则放前面。比如“放行已建立的连接”这条规则,几乎每个回包都会命中,所以一定要放在 INPUT 链的最前面。下面这段就很有代表性:

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -m conntrack --ctstate INVALID -j DROP iptables -A INPUT -s 10.0.0.0/8 -j ACCEPT iptables -A INPUT -j DROP

第二行把 INVALID 状态直接丢包,能避免一些畸形包骚扰应用。这里有一个容易被忽视的性能隐患:conntrack 表。系统默认会跟踪所有经过防火墙的连接,如果连接数超过表上限,新连接就会被丢弃,表现是网络时通时不通。可以用下面命令查看和调整:

sysctl net.netfilter.nf_conntrack_max sysctl -w net.netfilter.nf_conntrack_max=262144

调大这个值要注意内存消耗,每个 conntrack 条目差不多要占用几百字节,机器内存不足时别盲目调高。我遇到过一台 2G 内存的机器,默认值太小导致高并发下丢包,调成 131072 后问题缓解,但还是建议同时优化应用本身的连接复用,减少新建连接数。

3.2 用防火墙拦截暴力破解与恶意流量

公网服务器的 SSH 基本每天都会被人扫。除了常规的改端口、禁止 root 登录、改用密钥认证,防火墙还可以做第一层粗过滤。

iptables 可以用 recent 模块限制单位时间内的新建连接数。下面这套规则是限制每个源 IP 每分钟最多建立 5 个新连接:

iptables -I INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name ssh --rsource iptables -I INPUT -p tcp --dport 22 -m recent --limit 5/minute --update --name ssh --rttl -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j DROP

思路是先把新的 SSH 连接标记到 recent 列表,再判断这个源 IP 是否超过限制,超过的立即丢包。配合 fail2ban 做动态封禁效果更好:防火墙负责粗粒度限速,fail2ban 负责在多次密码失败后把 IP 拉进黑名单。如果用 firewalld,富规则也能实现类似限流:

firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=0.0.0.0/0 port port="22" protocol=tcp accept limit value="5/m"'

要注意,任何限流规则都不建议只限制 IP,得配合端口。因为同一 IP 背后可能有多个人用一个出口上网,限制太死会把正常用户也误伤。我在办公网经常遇到这种问题,后来规则就统一改成限制单 IP 的并发连接数,而不是总连接次数。

3.3 防火墙日志怎么开,怎么看

默认情况下,防火墙丢弃的包不会记录日志,这给问题排查带来麻烦。你需要手动加入 LOG 规则,并且放在 DROP 规则之前,否则包在匹配到 DROP 时就直接丢了,根本不会走到日志规则。

iptables -A INPUT -p tcp --dport 3306 -j LOG --log-prefix "MYSQL-DROP " --log-level 4 iptables -A INPUT -p tcp --dport 3306 -j DROP

查看日志可以这样:

tail -f /var/log/kern.log journalctl -k | grep MYSQL-DROP

日志量大的时候一定要加过滤,否则/var分区会被刷爆。我的做法是给 LOG 规则限定源 IP 或目的端口,只记录可疑来源,同时配合 rsyslog 把包含特定前缀的日志单独写到独立文件里,这样排查起来清爽得多。有人喜欢在 LOG 规则里记录所有 DROP,结果五分钟就把磁盘写满了,这个坑希望大家别踩。

4. 故障排查:规则不生效、端口不通、重启丢失

4.1 重启后规则丢失的常见原因

规则丢失基本就两个原因:一是用了临时规则,没保存;二是系统里同时存在 firewalld 和 iptables 两套管理方式,相互覆盖。

先说第一种。iptables -A加规则是针对内存里的规则集,重启后内核重新初始化,规则自然没了。所以持久化的动作必须做,用iptables-save写文件或者service iptables save。firewalld 则要求永久规则带--permanent,临时规则不带。如果你只加了临时规则,且一直没执行--reload,那当前会话有效,但重载后临时规则也没了。

第二种情况坑更大。有些云镜像默认安装了 firewalld,但你操作时可能下意识用了iptables命令,两者管理的底层规则其实并不完全互通。你通过 iptables 加的一条规则,可能在 firewalld 重载时被清理掉,也可能因为 firewalld 的 FORWARD 策略默认 DROP 而直接失效。我在一台双网卡服务器上就遇到过:iptables 加了 NAT 转发,但 firewalld 一 reload,转发规则全乱了。后来我统一了管理方式,只用 firewalld 的 rich rule 或 direct rule,才彻底解决。

4.2 Docker 端口映射与防火墙规则冲突

Docker 和防火墙的关系是运维里的经典疑难杂症。Docker 启动时会直接操作 iptables,在 nat 表和 filter 表插入自己的规则。如果你再用 firewalld 管理端口,很容易出现“容器端口明明映射了,但外面就是访问不了”的情况。

典型表现是:docker ps显示 8080 映射到容器 80,外部curl却超时。排查时先执行iptables -L FORWARD -n,看 FORWARD 链的默认策略是不是 DROP。如果是,容器流量就没有被放行。快速解决方法是把 docker0 接口加入 trusted 区域:

firewall-cmd --permanent --zone=trusted --add-interface=docker0 firewall-cmd --reload

这样做能解决连接问题,但要注意,把整个 docker0 网桥都信任了,所有容器的入站流量都不会被防火墙过滤,容器内部有漏洞时风险不小。更精细的做法是单独放行容器所在的子网或端口。如果实在排查不清,也可以临时将 Docker 的 iptables 管理关掉,在/etc/docker/daemon.json里配置:

{ "iptables": false }

但不建议新手这么做,因为 Docker 自身的网络隔离依赖 iptables,关掉后端口映射和跨容器通信都可能出问题。我通常只在防火墙规则与 Docker 规则冲突严重,且可以接受手动管理网络规则的环境里才用这个方案。

4.3 端口不通时,按这个顺序查

网络不通的排查本质上是个剥洋葱的过程。从外到内,一层层排除。我的习惯是:

先ping目标 IP,看网络层通不通。如果 ping 不通,先查路由和防火墙,注意 ping 包也可能被 ICMP 规则过滤。 ping 通后检查目标端口监听状态,ss -lntp看服务有没有起来。接着查防火墙规则,重点看目标端口对应链的默认策略和放行规则。千万不要只看 INPUT,如果机器是 NAT 网关或者 Docker 主机,转发流量走的是 FORWARD 链,那里也得看。

有一个高频错误案例:新装了一台 Linux,用nc监听了一个端口,本机能访问,外网访问不了。最后发现公有云安全组把端口禁了,跟操作系统防火墙一点关系没有。所以在排查时,虚拟化平台或云厂商的安全组一定要先确认,别只盯着服务器内部的 iptables 规则看半天。反过来也有,安全组全放行但系统内防火墙没配置导致端口不通,这类案例我见过不止一次。

5. 建立防火墙规则资产的管理习惯

5.1 把防火墙配置纳入版本管理

防火墙配置跟代码一样,需要版本管理。很多团队没有这个习惯,服务器上的规则都是“一次性积累”,改一次加几条,没人说得清每条规则是谁在什么背景下加的。几年下来,规则臃肿到没人敢轻易动。

我的做法是把规则备份到一个统一目录,例如/etc/firewall-backup/,每次变更前先导出快照,变更后也导出一次,文件名带日期和变更说明。备份命令很简单:

firewall-cmd --permanent --zone=public --list-all > /etc/firewall-backup/public-$(date +%F).txt iptables-save > /etc/firewall-backup/iptables-$(date +%F).rules

用 git 管理这些备份文件,配合 cron 定时导出,出问题时可以直接回滚到某一天的规则状态。这个习惯能帮你省掉很多“昨晚还好好的,今天突然不对了”的定位时间。

5.2 规则定期清理和审计

防火墙规则不是越多越好。过期的端口放行、临时放行没撤销、某次调试加的白名单忘了删,这些都是隐患。我通常每季度做一次规则审计,把list-all的结果过一遍,同时跟业务侧核对端口是否需要继续开放。

清理规则时要小心,先在测试环境验证,而不是直接在生产机器上删。删除 firewalld 规则用--remove-port或--remove-rich-rule,删除 iptables 规则要先iptables -L -n --line-numbers找到规则编号,再用iptables -D 链名 编号删除。这个操作比添加更容易出错,因为删错一条可能直接把管理通道断掉。所以我在删规则前,一定会确保自己有其他管理通道,比如远程管理卡或者带外控制台,否则一旦把 SSH 放行规则误删,就只能眼睁睁看着服务器失联。

5.3 变更前的自我检查清单

到这里,聊点我自己习惯用的检查清单。每次变更防火墙前,我会确认三件事:第一,当前规则是否有备份;第二,是否有保留的远程访问通道;第三,变更后怎么验证。验证不能只看 “规则已加载”,要真的从外部访问测试端口。多敲一条nc -vz比事后救火强太多。

做了多年防火墙管理,我最深的体会是:防火墙不是一个配置完就静态不变的东西,它是业务边界的一部分,要持续维护。与其花时间研究“怎么关掉防火墙”,不如花时间研究“怎么精确放行”。每次看到有人把防火墙关掉然后说“安全靠应用层”,我都觉得这是把侥幸当经验。规则透明、变更可审计、回滚有备份,这才是运维该有的状态。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 17:25:02

Cesium进阶实战:把ShaderToy特效搬进数字孪生场景

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华