2026/8/26 23:18:35

Linux防火墙firewalld配置IP白名单:从基础原理到SSH端口安全实践

Linux防火墙firewalld配置IP白名单:从基础原理到SSH端口安全实践 1. 项目背景与核心需求最近在部署一个内部服务时遇到了一个典型的运维安全需求一台运行着 CentOS/Rocky Linux 的服务器上某个关键服务比如数据库、管理后台需要对外提供访问但绝不能对全网开放。最直接的想法就是只允许公司办公室的固定IP或者几个运维人员的家庭IP来连接其他任何来源的访问请求都应该被防火墙无情地拒之门外。这个需求听起来简单但在实际配置时如果对 Linux 防火墙机制不熟悉很容易踩坑比如规则不生效、重启后失效或者更糟糕的——一不小心把自己锁在服务器外面。在 RHEL/CentOS 7/8 及 Fedora、Rocky Linux、AlmaLinux 等主流发行版上firewalld已经取代了传统的iptables成为默认的防火墙管理工具。它通过“区域Zone”和“服务Service”的概念让防火墙规则的管理变得更加直观和动态。对于“只允许指定IP访问特定端口”这个需求firewalld提供了非常清晰的实现路径但其中涉及到“富规则Rich Rule”的精确使用以及源地址source与端口port规则的优先级理解是配置成功的关键。很多人初次接触firewalld时会尝试用--add-port再结合--add-source结果发现规则冲突或不生效。本文将彻底拆解这个需求从firewalld的基础逻辑讲起手把手带你配置一个高可靠性的IP白名单规则并深入探讨规则生效的底层顺序、如何验证配置以及那些容易忽略的“坑”。无论你是要保护 MySQL、Redis、SSH还是任何一个自定义的TCP/UDP服务这套方法都是通用的。2. 理解 firewalld 的核心逻辑区域、服务与规则在动手敲命令之前必须花几分钟理解firewalld的设计哲学。它与直接写iptables规则的最大不同在于引入了“区域Zone”这个抽象层。你可以把 Zone 理解为一套预设的防火墙策略模板每个网络接口如 eth0, ens192都会被绑定到某一个 Zone 上从而继承该 Zone 的所有规则。常见的 Zone 有public默认 在公共区域使用不相信网络内的其他计算机只允许选定的传入连接。这是新安装系统后网卡默认绑定的区域。trusted 信任所有连接。这是一个“全通”区域通常用于完全信任的内部网络。home 用于家庭网络信任网络中的其他计算机。internal 用于内部网络信任网络中的其他计算机。我们的配置通常围绕public区域进行因为服务器对公网提供服务时网卡多半就在这个区域。firewalld管理两种主要规则直接规则 最接近原生iptables的命令通过--direct选项添加。它强大但笨重绕过firewalld的高级抽象一般不推荐普通管理使用因为其规则难以被firewalld自身持久化和管理。富规则Rich Rule 这是实现我们复杂需求如基于源IP的允许/拒绝的利器。富规则使用一种更易读的语法可以表达非常细致的条件比如rule familyipv4 source address192.168.1.100 port port22 protocoltcp accept。firewalld会将这些富规则翻译成底层的iptables规则。一个至关重要的概念规则执行的顺序。在同一个 Zone 内firewalld处理规则的顺序是拒绝规则Reject/Drop 允许规则Accept并且更具体的规则优先于更通用的规则。这意味着如果你先添加了一条“允许所有IP访问22端口”的规则再添加一条“拒绝某个IP访问22端口”的规则那么拒绝规则可能会因为顺序或优先级问题不生效。正确的做法是先设置默认的拒绝策略再添加特例的允许规则。对于“只允许指定IP访问”这种白名单模式最佳实践是首先拒绝所有对该端口的访问然后单独允许白名单中的IP。但在firewalld的富规则中我们可以通过一条规则同时实现“允许特定源IP访问特定端口”这本质上就是一条特例允许规则。只要我们没有添加其他通用的允许规则比如放行整个端口那么默认的拒绝策略就会生效。3. 实战配置为SSH服务构建IP白名单假设我们的服务器IP是192.168.1.10我们需要只允许IP地址为203.0.113.5和198.51.100.20的客户端通过SSH端口22连接到服务器其他任何IP的SSH连接尝试都应被拒绝。3.1 环境检查与初始状态确认在开始任何修改之前先查看当前的防火墙状态和配置做到心中有数。# 1. 检查 firewalld 服务状态确保其正在运行 sudo systemctl status firewalld如果状态是inactive (dead)需要启动并设置开机自启sudo systemctl start firewalld sudo systemctl enable firewalld# 2. 查看默认区域和活动区域 sudo firewall-cmd --get-default-zone sudo firewall-cmd --get-active-zones输出通常显示public是默认和活动区域并且列出了绑定的接口如ens192。# 3. 查看当前 public 区域的所有规则非常重要 sudo firewall-cmd --zonepublic --list-all这个命令会输出类似以下内容public (active) target: default icmp-block-inversion: no interfaces: ens192 sources: services: ssh dhcpv6-client ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:请重点关注services:和rich rules:部分。初始状态下services:里很可能已经有ssh。这意味着当前配置是允许所有IP地址访问SSH服务的。这是我们接下来要改变的状况。3.2 核心步骤移除通用规则添加富规则我们的目标是让SSH只对白名单IP开放。因此需要两个步骤步骤一从 public 区域移除通用的 SSH 服务允许规则。这条命令移除了那条“允许所有IP连接SSH”的规则。sudo firewall-cmd --zonepublic --remove-servicessh --permanent注意--permanent参数这个参数表示将变更写入永久配置。如果没有--permanent变更只会在运行时生效防火墙重启或重载后就会丢失。在生产环境中建议总是先测试运行时规则不加--permanent确认无误后再添加--permanent并重载。本文为了流程清晰直接使用--permanent但你自己操作时请务必小心。步骤二为白名单IP添加允许访问22端口的富规则。现在我们为两个白名单IP分别创建允许规则。# 允许 IP 203.0.113.5 访问 TCP 22 端口 sudo firewall-cmd --zonepublic --add-rich-rulerule familyipv4 source address203.0.113.5 port port22 protocoltcp accept --permanent # 允许 IP 198.51.100.20 访问 TCP 22 端口 sudo firewall-cmd --zonepublic --add-rich-rulerule familyipv4 source address198.51.100.20 port port22 protocoltcp accept --permanent命令拆解--zonepublic: 指定规则生效的区域。--add-rich-rule: 添加一条富规则。rule familyipv4: 指定规则应用于IPv4地址族。如果是IPv6地址则需改为ipv6。source addressx.x.x.x: 规则的源地址条件即来自这个IP的流量。port port22 protocoltcp: 规则的目标条件即访问22端口的TCP流量。accept: 对匹配上述条件的流量执行“接受”动作。--permanent: 永久生效。步骤三重新加载防火墙配置使永久规则生效。sudo firewall-cmd --reload--reload会重新加载永久配置到运行时状态而不会中断现有的已建立连接对于SSH这类长连接很友好。3.3 验证配置结果配置完成后必须进行验证。# 再次查看 public 区域的完整配置 sudo firewall-cmd --zonepublic --list-all现在输出应该类似于public (active) target: default icmp-block-inversion: no interfaces: ens192 sources: services: dhcpv6-client # ssh 服务已经不见了 ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules: rule familyipv4 source address203.0.113.5 port port22 protocoltcp accept rule familyipv4 source address198.51.100.20 port port22 protocoltcp accept确认services:里没有ssh而rich rules:里出现了我们添加的两条特定规则。这表示配置已按预期生效通用SSH放行已关闭取而代之的是两条针对特定IP的放行规则。终极验证从不同源IP进行SSH连接测试。这是最可靠的验证方法。从白名单IP203.0.113.5的机器上尝试 SSH 到服务器192.168.1.10应该能成功连接。从一个不在白名单中的IP比如192.168.1.55尝试 SSH 连接应该会收到Connection timed out或者长时间等待后失败。这证明防火墙的拒绝规则默认拒绝生效了。4. 进阶场景与深度配置解析基本的白名单配置已经完成但在实际生产环境中情况往往更复杂。下面针对几个常见进阶场景进行拆解。4.1 允许一个IP段而非单个IP如果你的办公室使用一个固定的IP地址段例如203.0.113.0/24你可以使用CIDR表示法在富规则中允许整个网段。sudo firewall-cmd --zonepublic --add-rich-rulerule familyipv4 source address203.0.113.0/24 port port22 protocoltcp accept --permanent sudo firewall-cmd --reload这条规则允许来自203.0.113.1到203.0.113.254的所有IP访问SSH。4.2 同时限制多个端口如Web服务假设你有一个Web应用需要同时开放80HTTP和443HTTPS端口给同一个IP白名单。你可以为每个端口添加一条规则或者使用port指令的多端口语法。方法一分别添加两条规则清晰推荐sudo firewall-cmd --zonepublic --add-rich-rulerule familyipv4 source address203.0.113.5 port port80 protocoltcp accept --permanent sudo firewall-cmd --zonepublic --add-rich-rulerule familyipv4 source address203.0.113.5 port port443 protocoltcp accept --permanent方法二使用端口范围适用于连续端口如果端口是连续的可以用port start80 end443但这会允许80-443之间的所有端口如8182...通常不适用于80和443这种情况。更佳实践定义自定义服务对于复杂的多端口应用最佳实践是在firewalld中定义一个自定义的“服务”。这提高了可读性和可维护性。# 1. 在 /etc/firewalld/services/ 目录下创建自定义服务的XML文件例如 my-web-app.xml sudo vim /etc/firewalld/services/my-web-app.xml文件内容如下?xml version1.0 encodingutf-8? service shortMy Web Application/short descriptionThis service allows access to my custom web app on ports 80 and 443./description port protocoltcp port80/ port protocoltcp port443/ !-- 还可以指定需要的模块等 -- /service# 2. 重新加载 firewalld 以识别新服务 sudo firewall-cmd --reload # 3. 现在可以使用这个自定义服务来添加富规则了 sudo firewall-cmd --zonepublic --add-rich-rulerule familyipv4 source address203.0.113.5 service namemy-web-app accept --permanent sudo firewall-cmd --reload这样一条规则就同时放行了80和443端口逻辑上更聚合。4.3 配置默认拒绝策略与规则优先级陷阱在firewalld中每个 Zone 都有一个target属性。默认情况下public区域的target是default。default的行为是只要流量不匹配任何一条accept规则包括通过service、port或rich rule添加的并且没有被任何drop或reject规则明确拒绝那么该流量将被允许。这听起来有点反直觉。对于白名单场景我们期望的是“默认拒绝显式允许”。firewalld提供了更严格的target选项target: ACCEPT: 接受所有流量类似trusted区域。target: %%REJECT%%: 拒绝所有流量并发送拒绝回复如 TCP RST。target: DROP: 丢弃所有流量静默丢弃不回复。target: default: 上述的默认行为。将区域目标设置为 DROP 以实现严格白名单# 将 public 区域的目标设置为 DROP (运行时) sudo firewall-cmd --zonepublic --set-targetDROP # 永久生效 sudo firewall-cmd --zonepublic --set-targetDROP --permanent sudo firewall-cmd --reload警告在执行此操作前你必须确保已经通过富规则允许了你的管理IP访问SSH22端口否则你会立刻失去所有连接包括你当前的SSH会话设置DROP后所有未明确允许的流量都将被静默丢弃。设置targetDROP后我们的两条允许203.0.113.5和198.51.100.20访问22端口的富规则就成了这个“黑暗森林”中唯一的灯塔。这是实现最严格白名单的方式。关于优先级的再次强调即使targetDROP富规则中的accept规则优先级依然最高。firewalld的规则处理流程可以简化为先处理所有显式规则富规则、端口规则等如果匹配到accept就放行匹配到reject/drop就拒绝如果都不匹配则执行区域的target动作。4.4 规则的查看、删除与故障排查查看所有富规则sudo firewall-cmd --zonepublic --list-rich-rules删除一条特定的富规则删除规则时必须提供与添加时完全一致的规则字符串。sudo firewall-cmd --zonepublic --remove-rich-rulerule familyipv4 source address203.0.113.5 port port22 protocoltcp accept --permanent sudo firewall-cmd --reload一个常见故障规则不生效。排查思路确认 firewalld 正在运行systemctl status firewalld。确认规则已加载使用--list-all和--list-rich-rules仔细检查确认规则语法正确且已包含--permanent并执行了--reload。检查区域绑定确认你的网卡ens192等确实绑定在public区域。使用firewall-cmd --get-active-zones查看。检查规则冲突是否有其他更宽泛的规则覆盖了你的富规则例如如果services:里还有ssh那么所有IP都能访问你的富规则虽然存在但通用规则优先级并不一定低于富规则实际上在默认target下通用允许规则会使白名单失效。这就是为什么第一步要移除通用ssh服务。检查底层 iptablesfirewalld最终生成iptables规则。可以查看其生成的链来深入排查。sudo iptables -nL --line-numbers | grep -A 5 -B 5 你的IP或端口 sudo firewall-cmd --direct --get-all-rules # 查看直接规则如果有使用 tcpdump 抓包在服务器上抓取目标端口的包看来自非白名单IP的请求是否真的到达了服务器还是被防火墙丢弃了。sudo tcpdump -i ens192 -nn tcp port 22 and host not 203.0.113.5 and not host 198.51.100.20如果能看到来自其他IP的SYN包但连接不成功说明防火墙规则可能有问题。如果根本看不到包可能是网络层面如云服务商安全组的拦截。5. 在云服务器AWS EC2, 阿里云 ECS等上的特殊考量在公有云上操作防火墙时有一个极其重要的层级不能忘记云平台的安全组Security Group或网络ACL。云服务器的网络流量路径是互联网 -云安全组- 宿主主机虚拟化层 -操作系统防火墙firewalld/iptables- 你的应用。安全组的规则优先级高于操作系统内的防火墙这意味着即使你在firewalld里配置了完美的白名单如果云安全组的入站规则是0.0.0.0/0允许22端口那么全网的SSH尝试仍然会到达你的服务器尽管可能被firewalld拒绝。反之如果云安全组只允许了你的白名单IP那么非白名单IP的流量在云网络边界就被拦截了根本到不了你的firewalld。最佳实践纵深防御在云安全组层面设置第一道白名单。只允许必要的IP地址段访问管理端口如SSH的22RDP的3389和应用端口。这是最有效、最外层的防护。在操作系统内部用firewalld设置第二道白名单。这可以作为安全组规则意外被更改或服务器被横向移动时的额外屏障。配置时永远通过一个已被允许的IP进行操作并在修改任何拒绝性规则前确保有备用的访问方式如云控制台的VNC连接以防配置失误把自己锁在外面。6. 脚本化与自动化管理当需要管理的白名单IP较多或经常变动时手动敲命令容易出错。可以编写简单的Shell脚本来管理。#!/bin/bash # 文件名manage_ssh_whitelist.sh # 用途批量添加或删除SSH白名单IP ZONEpublic PORT22 PROTOCOLtcp ACTION$1 # add 或 remove IP_LIST_FILE$2 # 存储IP地址的文件每行一个IP if [[ ! -f $IP_LIST_FILE ]]; then echo 错误IP列表文件 $IP_LIST_FILE 不存在。 exit 1 fi if [[ $ACTION ! add $ACTION ! remove ]]; then echo 用法$0 [add|remove] [ip_list_file] exit 1 fi while IFS read -r IP_ADDR || [[ -n $IP_ADDR ]]; do # 跳过空行和注释行 [[ -z $IP_ADDR || $IP_ADDR ~ ^[[:space:]]*# ]] continue # 去除行首尾空格 IP_ADDR$(echo $IP_ADDR | xargs) # 简单的IP格式校验IPv4 if [[ ! $IP_ADDR ~ ^[0-9]\.[0-9]\.[0-9]\.[0-9](/[0-9])?$ ]]; then echo 警告跳过无效的IP地址格式 $IP_ADDR continue fi RULErule family\ipv4\ source address\$IP_ADDR\ port port\$PORT\ protocol\$PROTOCOL\ accept echo 正在执行$ACTION 规则 for $IP_ADDR ... sudo firewall-cmd --zone$ZONE --$ACTION-rich-rule$RULE --permanent done $IP_LIST_FILE echo 所有规则变更已提交--permanent。正在重新加载防火墙... sudo firewall-cmd --reload echo 操作完成。使用示例# 创建一个IP列表文件 whitelist_ips.txt # 203.0.113.5 # 198.51.100.20 # 192.168.10.0/24 # 添加这些IP到白名单 chmod x manage_ssh_whitelist.sh sudo ./manage_ssh_whitelist.sh add whitelist_ips.txt # 从白名单中移除这些IP sudo ./manage_ssh_whitelist.sh remove whitelist_ips.txt这个脚本提供了基本的批量操作能力并且因为使用了--permanent所有变更都是持久的。在实际使用前强烈建议在测试环境验证。