十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux 防火墙白名单配置实战:ufw、firewalld、iptables

Linux 防火墙白名单配置实战:ufw、firewalld、iptables 1. 先把白名单这件事想明白1.1 白名单到底在解决什么问题Linux 防火墙的白名单说白了就一句话默认拒绝所有流量只把你明确认可的来源放进来。它的反面是黑名单——先放开、再封禁已知的坏 IP。很多人一开始图省事用黑名单理由通常是怕把自己的业务挡了。但只要你在生产环境待过一段时间就会发现黑名单根本维护不动攻击源每天都在换你封了 1000 个 IP第 1001 个还是能进来。白名单的逻辑正好反过来。你只需要回答一个问题谁需要连我。运维跳板机的网段、监控服务器的地址、办公网出口、负载均衡的内网地址、数据库主从之间互相连接的那几台机器把这些列清楚剩下的全部丢掉。攻击面从整个互联网直接收缩到你写的这几行规则这是最划算的一次安全投入。我见过太多机器是这样的状态systemctl status firewalld显示 running看着挺安心实际firewall-cmd --list-all一看services: ssh dhcpv6-client cockpit http https全开着等于门锁挂在门上但没扣。这种情况在 Ubuntu 上同样常见ufw status显示 active可ufw allow 22谁都能连。防火墙开着不等于有白名单这是最容易产生的错觉。这篇内容面向三类人刚接手 Linux 服务器的开发同学、需要给一批机器做安全收敛的运维、以及准备考试或者面试需要把这块讲清楚的人。Ubuntu、CentOS、RedHat 三个发行版我都会覆盖因为它们虽然用的是同一套内核机制但上层工具完全不同网上的教程又经常混着抄导致你在 Ubuntu 上照抄 CentOS 的命令回车就报 command not found。1.2 三套工具、三个发行版家族怎么对应底层其实只有一套东西NetfilterLinux 内核里的包过滤框架。用户态工具是后来加上的不同年代、不同发行版选了不同的封装。发行版默认前端工具底层配置文件Ubuntu 18.04 / Debian 系ufwiptables/nftables/etc/ufw/ 目录CentOS 7 / 8、RedHat 7firewalldiptables/nftables/etc/firewalld/CentOS 6 / RedHat 6 及更早iptables serviceiptables/etc/sysconfig/iptables通用所有发行版直接写 nft / iptablesnftables / iptables视方案而定这里要解释一个高频困惑为什么 CentOS 7 上service iptables status会说没这个服务因为 RHEL 7 开始红帽把 firewalld 设成默认iptables 的服务单元被拆到了iptables-services这个可选包里不装就没有。反过来Ubuntu 上你也能装 firewalld但那是自找麻烦——默认装了 ufw 就用 ufw别在同一台机器上同时开两套前端工具规则互相覆盖排查起来能让人崩溃。选型上我的建议很直接跟着发行版默认走。Ubuntu 用 ufwCentOS 7 用 firewalld老系统用 iptables。理由不是默认的就是最好的而是默认工具和系统的其他组件比如 NetworkManager、cloud-init、各种安装脚本有约定你换一套工具很可能某次 yum 更新之后规则就被别的东西改回去了。提示ufw 和 firewalld 都不要和 iptables 命令混用。你用iptables -L看到的那一堆链很可能是前端工具生成的直接手改会在下一次 reload 时全部丢失。2. 动手之前先把退路铺好2.1 四元组与最小放行原则热搜词里有一条白名单需要四元组这个说法很准确。一条完整的放行规则本质是在匹配五个要素源 IP、源端口、目的 IP、目的端口、协议。前面的四元组加上协议构成了一个流的身份。实际操作中源端口几乎永远是随机的你没法预测客户端用哪个端口发起连接所以规则里一般只写源 IP 目的端口 协议。目的 IP 在单机防火墙上通常省略就是本机只有在做转发、NAT 或者容器网络时才需要写明。真正的功夫在于把源 IP 收窄到什么粒度。我的经验是按这个顺序收敛能用具体主机地址/32就别用网段能用 /28 就别用 /24。办公网出口如果是动态的先找网管要固定出口 IP实在没有就上跳板机。数据库、缓存、内部管理端口永远只对应用服务器放行不对办公网放行。SSH 端口对办公网开放时加上频率限制或者配合密钥登录双保险。有个细节经常被忽略源 IP 是可以伪造的但 TCP 三次握手伪造不了除非能做到中间人。所以对于 TCP 服务源 IP 白名单是可靠的对于 UDP 服务比如 DNS、SNMP伪造源 IP 就能打进来白名单只能挡住响应被发到哪不能完全阻止探测。这也是为什么 DNS 服务器要额外做响应速率限制。2.2 改防火墙前必须做的三件事我踩过最惨的一次坑是在一台只有 SSH 这一个入口的云主机上手写 iptables 时先执行了iptables -P INPUT DROP然后才想起来还没加 SSH 放行规则。那一瞬间终端还连着但因为你已经把当前会话也算进了 INPUT 链回车之后直接卡死。最后靠 VNC 控制台进去救回来的前后折腾了四十分钟。从那以后我固定做三件事第一先加放行后改默认策略。顺序永远是写 ACCEPT 规则 → 确认规则在链里 → 最后才-P INPUT DROP。反过来做等于先关门再找钥匙。第二所有 SSH 会话里做防火墙变更都要开一个兜底定时任务。# 5 分钟后把 INPUT 策略恢复为 ACCEPT给自己留一扇窗 echo iptables -P INPUT ACCEPT | at now 5 minutes如果机器上没有at用这个更土但一定生效的写法nohup bash -c sleep 300; iptables -P INPUT ACCEPT; iptables -F /dev/null 21 规则调好了kill掉这个后台进程或者atrm掉定时任务就行。这招看着粗暴但它救过我不下五次。第三变更前先备份当前规则。# iptables 系 iptables-save /root/iptables.bak.$(date %F) # firewalld 系 tar czf /root/firewalld.bak.$(date %F).tar.gz /etc/firewalld # ufw 系 tar czf /root/ufw.bak.$(date %F).tar.gz /etc/ufw备份这件事顺风的时候觉得多余逆风的时候就是命。2.3 关闭防火墙不是解决方案热搜里有个问题叫防火墙关闭有影响吗。我的回答很明确在开发机上无所谓在生产机上关掉等于把自己的端口清单挂在网上。有人会说我后面有云安全组顶着安全组确实是第一道关但它管的是南北向流量进出云环境的同一 VPC 内部机器之间的东西向流量安全组通常放得很宽这时候主机防火墙就是唯一的阻挡层。而且关掉防火墙还有两个隐性代价一是很多安全扫描和基线检查会直接判你不合格后面要写一堆说明二是你会失去连接日志。iptables的 LOG 目标、firewalld 的 log-denied、ufw 的 logging能在被扫的时候给你留下证据。关掉之后攻击者在你机器上敲门你连声音都听不到。正确的做法是防火墙开着然后花一下午把白名单配完。下面三节我按发行版一个一个来。3. Ubuntu / Debian 系ufw 白名单实操3.1 ufw 默认策略与开启顺序ufw 是 uncomplicated firewall 的缩写它把 iptables 的一堆链包装成了人类能读的规则。默认情况下 ufw 是关闭的ufw status会显示Status: inactive。但不要直接ufw enable因为它的默认策略是allow incoming你一开等于什么都没做。正确的开启步骤是这样# 1) 先设默认策略进来的拒绝出去和转发放行 sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw default allow routed # 2) 先把自己要用的端口放行此刻 ufw 还没生效但规则已经写好了 sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp comment office-ssh # 3) 最后才启用 sudo ufw enable启用时会提示 Command may disrupt existing ssh connections输入y回车。我这里把deny incoming放在最前面是因为 ufw 的规则是按文件里出现的先后顺序执行的默认策略永远排在所有规则的最后。先写默认拒绝再写具体放行语义上跟 iptables 的-P INPUT DROP加-A INPUT -j ACCEPT是一回事。有一点必须解释清楚ufw default allow routed这一行在普通服务器上可以省掉但如果你这台机器要跑 Docker、要开 IP 转发、或者做内网网关就一定要显式放行转发否则容器里的服务会突然全部断网。3.2 按源地址放行的三种写法ufw 的规则语法其实很简洁核心就三种形态记住这三条基本够用一辈子。第一种只限端口不限来源不推荐但调试时用sudo ufw allow 443/tcp第二种限定来源 IP 或网段白名单的主力写法# 单个 IP 访问 22 sudo ufw allow from 203.0.113.17 to any port 22 proto tcp # 一个 /24 网段访问 3306 sudo ufw allow from 10.20.30.0/24 to any port 3306 proto tcp # 指定来源网段访问任意端口 sudo ufw allow from 10.20.30.0/24第三种限定来源加出接口多网卡机器用sudo ufw allow in on eth1 from 10.20.30.0/24 to any port 8080 proto tcp这里有个很实用的细节ufw allow from X to any port Y的to any部分在双栈机器上默认会同时生成 IPv4 和 IPv6 规则。如果你机器上 IPv6 根本没启用可以不管如果启用了但你不打算对 IPv6 做白名单记得检查/etc/default/ufw里的IPV6yes那一行改成no更省心否则会出现我明明只放行了 IPv4 网段怎么 IPv6 还能连的诡异情况。另外ufw 的规则顺序问题需要特别注意。它不像 iptables 那样可以随意-I插到指定位置虽然提供了ufw insert 1 allow ...把规则插到编号 1但编号会随着增删变化维护成本很高。所以一旦你的规则超过 15 条或者出现了 allow 和 deny 混合的复杂逻辑我建议直接放弃 ufw改用 /etc/ufw/before.rules 写原生的 iptables 规则——ufw 在 enable 时会把这些规则一起加载进去两边不冲突。3.3 规则顺序、日志与限速ufw 有个非常好用的功能叫限速专门对付 SSH 爆破sudo ufw limit from 203.0.113.0/24 to any port 22 proto tcp它的原理是同一个来源 IP 在 30 秒内如果发起超过 6 次连接就会被自动拉黑一段时间。对正常的交互式 SSH 完全没影响但爆破脚本一跑就会被掐断。只要你的 SSH 是暴露在公网或者大内网的我强烈建议用limit代替allow这是性价比最高的一条规则。日志方面默认 ufw 的日志级别是low只记录被策略拒绝的包。想看得更细sudo ufw logging medium sudo tail -f /var/log/ufw.log日志长这样[UFW BLOCK] INeth0 OUT MAC... SRC198.51.100.77 DST10.0.0.5 LEN60 TOS0x00 PREC0x00 TTL52 ID12345 DF PROTOTCP SPT41234 DPT6379 WINDOW29200 RES0x00 SYN URGP0看懂这行的关键就三个字段SRC是谁在敲、DPT敲的是哪个端口、PROTO用什么协议。如果DPT6379说明有人在扫你的 Redis。这类日志积累一段时间你就能摸清自己机器被扫的规律。注意ufw logging medium在高流量机器上会产生大量日志日志盘写满会导致服务异常。生产环境建议配合 logrotate或者用high级别只在排查问题的短时间窗口内打开。3.4 ufw 常用命令清单日常运维真正会用到的命令其实就这些建议直接存成笔记操作命令查看规则带编号sudo ufw status numbered查看详细规则sudo ufw status verbose删除第 3 条规则sudo ufw delete 3按内容删除sudo ufw delete allow from 203.0.113.17 to any port 22 proto tcp临时关闭sudo ufw disable重载配置sudo ufw reload重置所有规则sudo ufw reset查看原始规则文件cat /etc/ufw/user.rulesufw reset要慎用它会把所有规则清空并禁用 ufw通常只在彻底重做时才用。而cat /etc/ufw/user.rules这招是排查我明明删了规则为什么还生效的终极武器——有时候规则文件里残留了旧条目status看不到但实际在跑。4. CentOS 7 / RedHat 系firewalld 白名单实操4.1 zone 与 targetfirewalld 的挡板逻辑firewalld 最让人迷糊的就是zone区域这个概念。你可以把它理解成几套不同的挡板每套对应一批网卡或者一批来源地址。一台机器可以同时启用多个 zone流量进来之后firewalld 会先根据来源 IP 匹配 zone匹配不到就用网卡绑定的 zone最后用默认 zone。查当前状态firewall-cmd --get-active-zones firewall-cmd --get-default-zone firewall-cmd --list-all默认 zone 一般是public它的 target 是default含义是没被明确允许的都拒绝。这就意味着firewalld 天生就是白名单模式你只要把内置放行的服务删干净就自动变成白名单了。这也是它比 ufw 更省心的地方。但很多人踩的坑是publiczone 默认带了ssh、dhcpv6-client、cockpit这几个服务安装完就是放行的。你得先把它们摘掉# 看看现在放了什么 firewall-cmd --list-services # 把不需要的摘掉注意 ssh 别急着删先加好你自己的白名单规则 sudo firewall-cmd --permanent --remove-servicecockpit sudo firewall-cmd --permanent --remove-servicedhcpv6-client删ssh之前一定要先把白名单规则加上否则 reload 的一瞬间你就掉线了。4.2 用 rich rule 做源地址白名单firewalld 有两条路可以做源地址白名单我实际用下来更推荐第一种富规则rich rule。# 只允许 203.0.113.0/24 访问 22 端口 sudo firewall-cmd --permanent --add-rich-rulerule familyipv4 source address203.0.113.0/24 port protocoltcp port22 accept # 只允许 10.20.30.0/24 访问 MySQL sudo firewall-cmd --permanent --add-rich-rulerule familyipv4 source address10.20.30.0/24 port protocoltcp port3306 accept # 允许某台监控机访问所有端口慎用 sudo firewall-cmd --permanent --add-rich-rulerule familyipv4 source address10.20.30.99 acceptrich rule 的语法结构其实很规整rule family source 动作条件 accept/reject/drop。drop和reject的区别值得说一下——drop是直接丢包对方会一直等到超时reject会回一个 ICMP 不可达对方立刻知道被拒了。对外网扫描源用drop更省事对内网误配用reject更容易排查。第二种路是source zone适合来源地址很多、需要分组的场景# 建一个专用 zone sudo firewall-cmd --permanent --new-zoneadmin-ssh # 把办公网段塞进去 sudo firewall-cmd --permanent --zoneadmin-ssh --add-source203.0.113.0/24 sudo firewall-cmd --permanent --zoneadmin-ssh --add-source198.51.100.0/24 # 这个 zone 里放行 22 sudo firewall-cmd --permanent --zoneadmin-ssh --add-port22/tcp sudo firewall-cmd --reload这种写法的好处是管理侧很清晰以后想加一个新的办公网出口只要往 zone 里加 source不用重复写端口规则。缺点是多一层抽象新人看--list-all-zones的输出会晕。4.3 runtime 与 permanent 的坑这是 firewalld 新手翻车率最高的一处。firewalld 有两套规则集runtime内存里当前生效和permanent写在/etc/firewalld/里重启后生效。你用firewall-cmd加规则时如果不带--permanent只改 runtime带了--permanent只改文件不会立刻生效。所以标准动作永远是两步sudo firewall-cmd --permanent --add-rich-rule... sudo firewall-cmd --reload--reload会丢掉所有 runtime 里没写进 permanent 的规则所以别养成先临时加一条试试的习惯试完忘了 reload重启就没了。还有个更隐蔽的问题--reload会短暂切断连接吗答案是已经建立的连接通常不受影响但新连接会经历一个极短的窗口。--reload本身是平滑的真正会断的是systemctl restart firewalld。所以在线上机器上我宁愿多敲一次--reload也不去 restart 服务。另外--complete-reload和--reload也不一样前者会清空所有连接跟踪表等于把现有会话全踢掉只在规则乱到需要推倒重来时才用。4.4 firewalld 常用命令清单# 查当前 zone 的全部规则 firewall-cmd --list-all # 查所有 zone firewall-cmd --list-all-zones # 查看所有富规则 firewall-cmd --list-rich-rules # 删除某条富规则把 add 换成 remove内容一模一样 sudo firewall-cmd --permanent --remove-rich-rulerule familyipv4 source address203.0.113.0/24 port protocoltcp port22 accept # 查看某个 zone 的 target firewall-cmd --permanent --zonepublic --get-target这里提醒一个细节firewalld 的富规则顺序是按添加顺序或者说按内部规则列表顺序来的但内部实现会把它拼成 iptables 规则链做复杂逻辑时依然可能出现先匹配到某条就返回的情况。所以当你的富规则超过二三十条排查会变得很难这时候就该考虑下一节的 ipset 了。5. CentOS 6 / RedHat 6 等老平台iptables 白名单实操5.1 规则链、匹配顺序与默认策略虽然 RHEL 6 已经很老了但不少内网老系统、工业设备、专用一体机还在跑而且很多高级玩法比如 ipset、conntrack 调优在 iptables 层反而讲得更清楚。所以这一节我建议所有人都看一遍不只是为了照顾老系统。iptables 的核心机制是链chain和顺序匹配。INPUT 链处理发往本机的包FORWARD 处理经过本机转发的包OUTPUT 处理本机发出的包。规则从上往下匹配匹配到第一条就执行动作并停止-j ACCEPT、-j DROP都算终止动作。所以规则的顺序至关重要。正确的顺序是放行回环接口lo不然本机的一堆服务会互相连不上。放行已建立连接的后续包状态匹配否则每个包的响应都被当成新连接。放行 ICMP 的必要类型保证 ping 和路径 MTU 发现正常。白名单来源的放行规则。记录日志。默认策略兜底。第 2 条是新手最容易漏的。如果你写了-A INPUT -s 10.0.0.5 -p tcp --dport 22 -j ACCEPT但没写状态匹配那么客户端 SYN 能进来服务器回 SYN-ACK 的时候这个包属于 OUTPUT 链会被放行但客户端再发 ACK 过来这个包在 INPUT 链里既不匹配源 IP 规则其实匹配因为源 IP 还是 10.0.0.5……嗯SSH 这个例子里其实能通。真正会出问题的是主动去连别人的场景本机发起一个到外部的 HTTP 请求对方的响应包源 IP 是那个 HTTP 服务器不在你的白名单里就会被 DROP 掉表现为我 ping 得通它但我请求不通。这就是状态匹配存在的意义-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT这句话的意思是只要是已经建立连接或者与已建立连接相关的包一律放行。有了它你的白名单就只需要管谁可以发起新连接不用管后续的数据流规则量能减少一大半。5.2 一份可以直接抄的白名单脚本下面这份脚本我在 CentOS 6 和 CentOS 7最小化安装、没装 firewalld上都用过改几个变量就能上#!/bin/bash # iptables-whitelist.sh # 用途为服务器配置基于源地址的白名单防火墙 # 适用CentOS 6 / RedHat 6 / 精简版 CentOS 7 set -e # 白名单网段空格分隔 TRUSTED_NETS127.0.0.1 10.20.30.0/24 203.0.113.0/28 # 对外开放的端口格式 端口/协议 OPEN_PORTS22/tcp 443/tcp # 清空旧规则 iptables -F iptables -X iptables -Z # 默认策略进来拒绝出去和转发放行 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # 1) 回环接口 iptables -A INPUT -i lo -j ACCEPT # 2) 已建立连接与相关连接 iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # 3) 必要的 ICMPping、目标不可达、TTL 超时 iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT # 4) 白名单放行 for net in $TRUSTED_NETS; do for pp in $OPEN_PORTS; do port${pp%/*} proto${pp#*/} iptables -A INPUT -s $net -p $proto --dport $port -j ACCEPT done done # 5) 记录被拒绝的包限速避免日志被打爆 iptables -A INPUT -m limit --limit 10/min -j LOG --log-prefix IPT-DROP: --log-level 4 # 6) 兜底其实默认策略已经 DROP这条是为了语义清晰 iptables -A INPUT -j DROP echo 规则写入完成当前规则数$(iptables -L INPUT -n | wc -l)几个地方解释一下。set -e让脚本在任一命令失败时立刻退出避免中途出错留下半套规则。-m limit --limit 10/min是日志限速如果不加这个被扫描的时候/var/log/messages一分钟能涨几百兆。IPT-DROP:这个前缀是给后续 grep 用的方便你写脚本统计攻击源。-P FORWARD DROP这一条如果你的机器要做路由转发或者跑 Docker需要改成 ACCEPT 并单独放行转发规则。Docker 会自动往 FORWARD 链里插自己的规则所以在 Docker 宿主机上iptables 手写规则和 Docker 的规则会打架这是另一个大话题这里先不展开。5.3 持久化与开机加载iptables 命令写进去的规则重启就没了。持久化有两个办法。老系统CentOS 6 / RHEL 6service iptables save # 规则会存到 /etc/sysconfig/iptables service iptables restart新系统CentOS 7 装了 iptables-servicesyum install -y iptables-services systemctl stop firewalld systemctl disable firewalld iptables-save /etc/sysconfig/iptables systemctl enable iptables systemctl start iptables注意顺序一定要先停 firewalld 再启用 iptables 服务两套东西同时跑规则会互相干扰而且很可能重启之后你发现生效的是 firewalld 那套。还有一种更原始但更可控的方式把上面的脚本放到/etc/rc.local里CentOS 6或者写成一个 systemd unitCentOS 7。我个人的偏好是用 iptables-save / iptables-restore 配合 systemd unit因为规则文件是纯粹的数据改起来直观也不会因为脚本里的某个变量写错导致整个链断掉。6. 规则多了怎么办ipset 与管理侧收敛6.1 ipset 基本用法与 firewalld 集成当白名单地址超过三五十个或者你有一个经常变动的地址集合比如一堆云函数出口 IP逐条写规则就变成噩梦了。这时候用ipset把所有地址装进一个集合防火墙规则只匹配这个集合。底层操作是这样# 安装CentOS: yum install ipsetUbuntu: apt install ipset # 建一个集合类型选择很关键 ipset create trusted_hosts hash:ip ipset create trusted_nets hash:net # 加入地址 ipset add trusted_hosts 203.0.113.17 ipset add trusted_nets 10.20.30.0/24 # 看一下 ipset list # 引用到 iptables iptables -A INPUT -m set --match-set trusted_nets src -p tcp --dport 22 -j ACCEPThash:ip和hash:net的区别一定要搞清hash:ip里只能放单个 IP你塞10.0.0.0/24进去会报错hash:net里既能放单个 IP 也能放网段但内存占用稍高。绝大多数白名单场景直接用hash:net就对了。firewalld 从 0.4 版本开始直接支持 ipset# 建 ipset sudo firewall-cmd --permanent --new-ipsettrusted_nets --typehash:net # 加地址 sudo firewall-cmd --permanent --ipsettrusted_nets --add-entry10.20.30.0/24 sudo firewall-cmd --permanent --ipsettrusted_nets --add-entry203.0.113.0/28 # 引用到 rich rule sudo firewall-cmd --permanent --zonepublic --add-rich-rulerule source ipsettrusted_nets port port22 protocoltcp accept sudo firewall-cmd --reload好处非常明显以后加一个新网段只要--ipset --add-entry然后 reload不用动任何规则。查firewall-cmd --ipsettrusted_nets --get-entries就能看到全部内容。6.2 动态 IP 与云环境的处理思路现实里最头疼的场景是来源地址会变。比如办公网是动态出口、远程同事在家办公、云上的 API 网关出口 IP 不固定。我的处理思路分三档第一档找固定点。大多数企业都有固定的出口 IP 或者跳板机。哪怕是通过跳板机再多一跳也比把整个 0.0.0.0/0 放开强得多。跳板机本身就是白名单资产的入口把防护集中在一台机器上管理成本反而更低。第二档用脚本定期同步。如果来源地址是某个服务商提供的、能通过接口查到比如对象存储、CDN 回源段就写一个定时任务每 10 分钟拉一次列表和 ipset 做差集更新。核心逻辑是先加后删避免中间出现空窗期#!/bin/bash # sync-whitelist.sh NEW_LIST$(curl -s https://example.internal/api/egress-ips) ipset create trusted_nets hash:net -exist # 先全部加进去 for ip in $NEW_LIST; do ipset add trusted_nets $ip -exist done # 再删掉已经不在列表里的需要自己维护一份旧列表做对比 # 简化做法直接 swap重建一个临时集合 ipset create trusted_tmp hash:net -exist ipset flush trusted_tmp for ip in $NEW_LIST; do ipset add trusted_tmp $ip -exist done ipset swap trusted_tmp trusted_nets ipset destroy trusted_tmpipset swap这个操作是原子的切换过程中不会有规则失效的瞬间比 flush 再 add 安全得多。这个思路值得记住所有需要热更新集合的场景都能用。第三档对无法固定的来源改用应用层认证。与其在防火墙上开一个大洞不如把这个端口本身改成必须带证书/Token 才能访问。防火墙白名单守的是网络层应用层认证守的是身份层两者是互补的不是二选一。7. 常见问题排查与速查表7.1 把自己锁在外面了怎么救这是最高频的事故。分几种情况说。能连 SSH 但新连接被拒说明当前会话还在规则里漏了 ESTABLISHED 状态匹配或者默认策略改错了。赶紧在一个还没断的会话里执行# iptables iptables -P INPUT ACCEPT iptables -F # firewalld firewall-cmd --set-default-zonetrusted # 或者临时全放 firewall-cmd --zonetrusted --add-source0.0.0.0/0完全连不上走云控制台的 VNC / 串口登录或者联系机房上 KVM。登进去之后按上面的命令恢复。所以前面说的at兜底方案真的很重要它能让你在 300 秒之内自动恢复而不用等控制台。Ubuntu 上ufw reset之后还是连不上ufw 重置了但机器上可能同时跑着 Docker 或者别的工具生成的 iptables 规则。执行iptables -L -n --line-numbers看看到底是谁的规则在挡路。7.2 端口放行了却连不上这个问题的排查要有顺序不能瞎试。我固定按这个链条走排查点命令常见结论服务本身在监听吗ss -lntp | grep 端口只监听 127.0.0.1 而不是 0.0.0.0防火墙规则匹配到了吗iptables -L INPUT -n -v计数器不增长说明规则没匹配上包到底走到哪了tcpdump -i any port 端口 -nn只见 SYN 不见 SYN-ACK是被丢了是不是安全组挡的云控制台看安全组主机放行了云上没放行是不是中间设备traceroute -T -p 端口 目标链路上有设备拦截第一条最常见。很多服务默认只绑 127.0.0.1比如 Redis 的默认配置、MySQL 在某些发行版上的默认配置、Node.js 应用如果写的是app.listen(3000, localhost)。防火墙放行了包能到网卡但服务根本不在那个地址上监听你看到的现象就是连接被拒绝而不是超时。这两个现象要分清超时通常是防火墙 DROP拒绝通常是服务没监听或者被 REJECT。7.3 重启后规则失效九成是持久化没做剩下的一成是被别的组件覆盖了。firewalld 的规则如果没带--permanent重启必丢这个前面讲过。iptables 的规则没执行iptables-save或service iptables save重启也必丢。Ubuntu 上 ufw 的规则默认是持久的写在/etc/ufw/user.rules但如果ufw服务被 disable 了规则文件再全也不会加载用systemctl is-enabled ufw确认一下。还有一类隐蔽情况机器上装了 Docker、kube-proxy、LibreNMS 这类会自动改写 iptables 的组件。它们启动时会往链里插规则如果你的规则和它们的顺序冲突表现可能是防火墙看着是开的但某些端口莫名其妙通了。处理办法是让这些组件的规则优先或者干脆不让它们管防火墙。7.4 排查速查表把上面几节的要点汇总成一张表遇到问题时从上往下过一遍基本能覆盖八成场景现象最可能的原因处理动作SSH 卡住不动默认策略 DROP 且没放行 22控制台登录iptables -P INPUT ACCEPT端口 telnet 超时规则未放行或被 DROP检查规则的源地址是否匹配看-v计数器端口 connection refused服务未监听该地址ss -lntp确认绑定地址重启后规则消失未持久化service iptables save/ 加--permanent规则加了不生效firewalld 未 reloadfirewall-cmd --reload日志暴涨未做日志限速加-m limit --limit 10/min本机服务互相连不上漏了 lo 接口放行iptables -A INPUT -i lo -j ACCEPT主动出站请求无响应漏了 conntrack 状态匹配加 ESTABLISHED,RELATED 放行最后分享一个我自己的习惯每次改完防火墙一定从另一台机器实测一次。别信status的输出也别信自己的记忆。用nc -zv 目标IP 端口或者telnet从内网、从外网、从白名单外的机器各测一遍四个方向都通了才算收工。防火墙这种东西配置的时候多花二十分钟验证比事后半夜被叫起来排查要划算得多。还有一个后续可以做的事把白名单规则和资产清单对齐。每台机器开放了哪些端口、对哪些来源开放、由谁负责整理成一张表定期 review。我见过太多当年临时开放、后来没人记得的规则它们就是白名单最终失效的原因。规则和人一样不维护就会腐烂。
返回列表