Ubuntu服务器防火墙白名单精细化配置的核心不是简单地“允许某几个IP”,而是要把默认拒绝策略、服务端口绑定、来源IP验证、连接状态跟踪和日志审计这五件事做成一个闭环。很多人配置白名单后仍然被扫描或误伤业务,根本原因在于只写了允许规则,却没有同步收紧默认策略,或者忽略了已建立连接的回程流量。下面直接给出可落地的配置思路和命令,基于Ubuntu 22.04/24.04自带的ufw和底层iptables/nftables规则展开。
先确认防火墙后端与当前规则状态Ubuntu默认的ufw是iptables或nftables的前端封装。执行sudo ufw status verbose可以查看当前默认策略和已有规则。如果输出显示“Status: active”但默认策略是“allow (incoming)”,那么白名单形同虚设,因为所有未匹配流量仍然放行。正确做法是先执行sudo ufw default deny incoming和sudo ufw default allow outgoing。前者保证入站流量默认拒绝,后者允许服务器主动外发请求及回程流量。不要随意把outgoing也设为deny,否则apt更新、DNS查询、NTP同步都会失败,排查成本极高。
白名单的第一层:按服务端口放行并绑定来源ufw的allow命令支持from参数,这是精细化白名单的基础语法。例如只允许办公网出口IP 203.0.113.15访问SSH,应执行sudo ufw allow from 203.0.113.15 to any port 22 proto tcp。注意不要使用sudo ufw allow 22这种无来源限制的写法,它等价于向全网开放22端口。对于Web服务,如果仅对CDN回源节点开放443,需要把CDN提供商的回源IP段逐条加入。可以写成sudo ufw allow from 192.0.2.0/24 to any port 443 proto tcp。如果来源IP数量较多,建议用脚本批量读取IP列表并循环执行ufw命令,避免手工输入出错。
利用ipset或nftables集合管理大规模白名单当白名单IP超过几十条甚至上百条时,ufw的规则列表会变得难以维护,而且每条规则都会线性匹配,影响性能。更优方案是使用ipset配合iptables,或直接使用nftables的set结构。以ipset为例,先创建集合sudo ipset create allowlist hash:net,然后批量添加网段sudo ipset add allowlist 198.51.100.0/24。接着在iptables中插入一条规则:sudo iptables -I INPUT -m set --match-set allowlist src -p tcp --dport 22 -j ACCEPT。这样后续维护只需增删ipset集合成员,不需要反复修改防火墙规则链。Ubuntu 22.04以上版本底层已逐步切换至nftables,也可以用nft add set ip filter allowlist { type ipv4_addr; flags interval; }来创建集合,再用nft add rule ip filter INPUT ip saddr @allowlist tcp dport 22 accept实现同样效果。两种方式都比纯ufw规则更适合大规模动态白名单。
不要忽略已建立连接和回程流量很多人在配置白名单后发现SSH连上后很快断开,或者Web服务能建立TCP握手但无法传输数据。原因是只放行了入站SYN包,没有放行ESTABLISHED和RELATED状态的流量。ufw默认会处理连接状态跟踪,但如果手动编写iptables规则,必须显式加入sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT。这条规则要放在白名单规则之前或之后均可,但必须存在。对于nftables,对应规则是nft add rule ip filter INPUT ct state established,related accept。没有这条规则,任何已建立的连接在后续数据包到达时都会被默认拒绝策略拦截。
针对管理端口做二次验证与限速白名单解决了“谁能连”的问题,但无法防止白名单内某个IP被攻陷后发起暴力破解。建议对SSH等管理端口叠加fail2ban或ufw的limit规则。例如sudo ufw limit from 203.0.113.15 to any port 22 proto tcp会在来源IP触发连接频率限制后临时拒绝。更精细的做法是在应用层启用SSH密钥认证并禁用密码登录,同时修改默认端口。修改端口不是安全措施,但能减少日志噪音。真正有效的是在/etc/ssh/sshd_config中设置PasswordAuthentication no和PermitRootLogin prohibit-password。白名单加密钥认证加连接限速,三层叠加后管理端口的安全性会显著提升。
用日志验证白名单是否真正生效配置完成后不要只看ufw status的输出,要主动验证。先从一个不在白名单内的IP尝试连接SSH或Web端口,确认连接被拒绝且/var/log/ufw.log中出现“UFW BLOCK”记录。再从白名单IP连接,确认可以正常建立会话。如果日志中没有BLOCK记录,说明默认拒绝策略没有生效,或者流量走了其他链。执行sudo ufw logging on可以开启日志,日志级别建议设为low或medium,过高的级别会产生大量噪音。对于生产环境,建议将防火墙日志集中收集,定期分析被拒绝的来源IP和端口,反向验证白名单是否覆盖了所有合法业务流量。
白名单配置的常见错误与排查路径第一种错误是规则顺序错误。ufw内部按规则添加顺序匹配,先添加的allow规则会优先命中。如果先执行了sudo ufw allow 22,再执行sudo ufw allow from 203.0.113.15 to any port 22,那么22端口仍然对所有IP开放,因为第一条规则已经放行了。正确做法是先删除无来源限制的规则,再添加带来源限制的规则。第二种错误是IPv6规则遗漏。如果服务器启用了IPv6,ufw默认同时管理IPv4和IPv6规则,但手动编写iptables规则时只写了IPv4,导致IPv6流量绕过白名单。执行sudo ip6tables -L -n检查IPv6规则链是否也应用了默认拒绝和白名单逻辑。第三种错误是Docker或Kubernetes的流量绕过ufw。Docker会直接操作iptables的NAT和FORWARD链,ufw的INPUT链规则无法拦截容器映射端口的流量。此时需要在DOCKER-USER链中插入白名单规则,例如sudo iptables -I DOCKER-USER -p tcp --dport 8080 -s 203.0.113.15 -j ACCEPT,并在该链末尾设置DROP。
精细化白名单的进阶实践:按服务拆分与最小权限不要把所有服务共用一个白名单。数据库端口3306只应允许应用服务器IP访问,管理端口22只允许运维跳板机IP访问,Web端口443允许CDN回源节点访问。按服务拆分白名单后,单条规则的来源范围缩小,被突破后影响面也减小。例如MySQL配置为只监听内网网卡,在/etc/mysql/mysql.conf.d/mysqld.cnf中设置bind-address = 10.0.0.5,然后在防火墙中只允许10.0.1.0/24访问3306端口。即使Web应用被入侵,攻击者也无法从公网直接连接数据库。同样的思路适用于Redis、Memcached等默认无认证或弱认证的服务,这些服务必须绑定内网地址并配合防火墙白名单,绝不能暴露在公网。
动态白名单与自动化更新对于使用弹性公网IP的团队,办公网出口IP可能定期变化。手动更新白名单不可持续,建议用脚本定期获取当前出口IP并与防火墙规则比对更新。脚本逻辑可以是:获取当前出口IP,检查ufw规则中是否已存在该IP对22端口的允许规则,如果不存在则先删除旧的办公网IP规则再添加新规则。将脚本加入crontab每小时执行一次。对于云环境,可以利用云厂商的安全组作为第一层白名单,服务器内ufw作为第二层。两层白名单叠加后,即使云安全组被误改,服务器内防火墙仍然生效。但要注意两层规则的一致性,避免出现安全组放行而ufw拒绝导致业务中断。
总结白名单精细化配置的检查清单配置完成后逐项核对:默认入站策略是否为deny;所有allow规则是否都带from来源限制;是否放行了ESTABLISHED和RELATED状态流量;IPv6规则是否与IPv4一致;Docker等容器流量是否在DOCKER-USER链中被拦截;管理端口是否叠加了限速和密钥认证;日志是否开启并能记录BLOCK事件;白名单IP变更是否有自动化流程。只有全部满足,白名单配置才算真正精细化。防火墙白名单不是一次性配置,而是需要随业务和网络环境持续维护的基础安全设施。
