ARTICLE DETAIL

资讯详情

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

Linux IP白名单配置实战:从iptables到应用层防护

Linux IP白名单配置实战:从iptables到应用层防护 这篇事情要从一次不算愉快的排障说起。有台线上服务器被安全团队扫出异常登录日志里一大半是来自各个地区IP的SSH爆破尝试虽然密码够复杂没被攻破但看着那几百条 Failed password 记录心里属实不安稳。后来处理方案很简单给这台机器加上IP白名单只允许公司办公网出口IP和跳板机IP连进去其他来源一律在防火墙层面就拦掉。就是这个过程让我想好好把Linux添加IP白名单这件事从头到尾梳理一遍。日常工作中你会发现IP白名单需求比想象中常见得多管理后台只允许内部网络访问、数据库端口只对应用服务器开放、SSH只接受固定出口IP连接、第三方回调接口只信任对方服务器地址……说法五花八门落到技术上都是同一套能力在Linux系统里按来源IP精确控制谁能访问哪个服务。这篇文章就围绕这个场景展开覆盖iptables和firewalld两种防火墙方案再到Nginx、Servlet容器、SSH这些常用服务的白名单配置最后是我自己踩过的一些坑和排查套路。适合Linux运维、后端开发以及自建服务器、对安全性有要求的个人用户参考。你不需要把每条命令背下来但看完至少能搞清楚在哪个环节加白名单最合适、配完怎么验证、出了问题怎么查。1. 先搞清楚Linux下IP白名单到底有哪几层1.1 网络层白名单负责“能不能连上”网络层白名单对应的就是Linux防火墙常见的是iptables和firewalld。它们工作在TCP/IP协议栈的内核层面在数据包真正到达应用程序之前做判断允许的放行不允许的直接丢弃或者拒绝。你可以把它理解成小区大门的保安先看你的脸来源IP再看你要去几栋几单元目标端口不在名单里的压根进不了小区。为什么要在这一层做白名单因为效率高、覆盖范围广。一个端口一旦在iptables层面DROP掉后面所有依赖这个端口的服务都不需要再关心请求来源哪怕是Nginx、MySQL这类应用本身有Bug网络层已经把危险挡在了门外。而且这一层的粒度可以非常细源IP、源端口、目标IP、目标端口、协议类型都能组合匹配也就是所谓的四元组匹配。可以说网络层白名单是整个访问控制体系的基石也是绝大多数场景下的首选方案。1.2 应用层白名单负责“进来之后能干什么”网络层放行之后请求会进入应用程序。应用层白名单就是在软件内部再设置一道关卡典型代表是Nginx的allow/deny指令、Web应用的拦截器、Servlet容器的访问控制配置。它相当于楼栋单元的门禁进了小区并不代表你能进每一栋楼application层还要再验一次身份。应用层白名单最大的优势是条件判断更灵活。你可以基于URL路径、请求方法、请求头、Cookie甚至业务用户信息来做判断而网络层只能看IP和端口。比如管理后台的/admin路径只允许内网IP访问但前端静态资源允许所有人访问这种场景用iptables做不了Nginx里配置却很方便。另外应用层白名单对应用自身的状态是感知的可以做更丰富的日志记录出问题时排查起来更直观。但应用层白名单有个致命弱点它依赖程序本身的正确性代码逻辑有漏洞、配置项没生效白名单就形同虚设。所以安全设计上讲究纵深防御网络层和应用层各设一道门而不是只靠其中一层。1.3 服务层白名单单独给SSH这类关键服务开小灶单独把SSH拿出来说是因为它太特殊了。SSH是每一台Linux服务器的命脉一旦配置错你可能连机器都登不进去。SSH相关的白名单传统做法是通过/etc/hosts.allow和/etc/hosts.deny这两个文件利用TCP Wrapper机制做访问控制指定哪些来源IP可以使用ssh服务。不过需要提醒的是现在不少Linux发行版默认不再编译libwrap支持这种方式在部分系统上是不生效的用之前得确认。更通用、更推荐的方式是直接在sshd_config里用Match Address做匹配。它可以在sshd进程内部做判断效果上类似白名单而且还不会影响防火墙规则。实际项目中我通常建议SSH白名单同时配置在防火墙和服务层两层网络层负责拦掉绝大多数扫描流量服务层负责账户策略的兜底例如非白名单来源强制使用密钥登录、禁止密码登录。这对抵御暴力破解非常有效。2. 最常用的方案用iptables给SSH和业务端口做白名单2.1 动手前先确认两件事现状和安全通道配iptables白名单最怕的就是配到一半断连。不管你多熟练操作前一定要先确认两件事第一当前机器的防火墙状态和已有规则是什么样的别上来就追加结果和旧规则冲突第二你有没有一条救命通道——比如云平台的VNC控制台、带外管理卡再不济也得有个能登录物理机的途径。没有这条退路IP白名单配置失误时你连后悔的机会都没有。查看当前状态和规则# 查看防火墙是否运行 systemctl status iptables 2/dev/null || systemctl status firewalld # 查看INPUT链现有规则带行号和流量统计 iptables -L INPUT -n --line-numbers -v输出里你会看到每条规则的匹配条件、目标动作以及已经匹配了多少个数据包。如果没有输出或者提示链不存在说明当前INPUT链可能没有显式规则全部依赖默认策略。看到默认策略是ACCEPT还是DROP很重要这直接决定你接下来的配置顺序。2.2 白名单规则的核心顺序比命令本身还重要iptables的规则是按顺序匹配的上一条命中之后下一条就不再执行。这就会引出一个经典误区有人把白名单ACCEPT规则加在末尾但前面已经有一条拒绝所有来源的DROP规则白名单自然永远不会生效。正确思路是把白名单规则放在前面拒绝规则放在后面。以只允许某个IP访问SSH为例# 1. 允许白名单IP访问22端口 iptables -A INPUT -s 203.0.113.5/32 -p tcp --dport 22 -j ACCEPT # 2. 拒绝其他所有来源访问22端口 iptables -A INPUT -p tcp --dport 22 -j DROP这里-s指定来源IP/32是IPv4的单IP掩码写法-p tcp限定协议--dport 22限定目标端口-j ACCEPT表示放行-j DROP表示静默丢弃。用-A追加时两条规则的相对顺序就是上面的样子白名单在前拒绝在后。如果反过来先DROP再ACCEPT那后面那条ACCEPT永远不会被匹配到等于所有来源都被封了。我习惯把白名单规则用-I INPUT 1插到链首确保它肯定在拒绝规则之前# 在INPUT链第1条位置插入白名单规则 iptables -I INPUT 1 -s 203.0.113.5/32 -p tcp --dport 22 -j ACCEPT-I是插入1是插入到第一条这样做的好处是无论之前链上有什么规则这条白名单都最先判断。但要注意插入到最前只对允许的IP有效如果旧链上还有别的拒绝规则依然会挡掉后面追加的允许规则所以稳妥起见还是先查看现状再动手。2.3 多IP多网段怎么办循环、文件和ipset生产环境往往不止一个IP需要放行。办公网一堆出口IP、几个合作方的服务器IP、跳板机IP加起来可能几十个。如果逐条iptables -A写上去人累不说规则维护也是一团乱麻。这种情况下我通常用两种方式第一种把IP写进文件用循环批量添加# 每行一个IP井号开头是注释 cat /etc/ip-whitelist.txt EOF 203.0.113.5 203.0.113.10 198.51.100.0/24 EOF # 批量添加白名单规则 while read ip; do [[ $ip ~ ^#.*$ || -z $ip ]] continue iptables -A INPUT -s $ip -p tcp --dport 22 -j ACCEPT done /etc/ip-whitelist.txt第二种IP数量特别多上百个的时候建议用ipset。ipset的好处是匹配性能更高而且规则只写一条后续增删IP不用改iptables规则只需操作ipset集合。基础用法# 创建一个名为whitelist的IP集合 ipset create whitelist hash:ip # 往集合里添加IP ipset add whitelist 203.0.113.5 ipset add whitelist 198.51.100.0/24 # 让iptables直接匹配这个集合 iptables -I INPUT 1 -m set --match-set whitelist src -p tcp --dport 22 -j ACCEPT后续要临时放行某个IP执行ipset add whitelist 新IP即可生效不需要再动iptables规则。这个思路在维护大量白名单时非常实用尤其是业务方频繁这个IP也要加一下的场景。2.4 保存规则别再让重启把白名单“冲掉”iptables命令直接操作的是内核当前生效的规则内存态的重启后默认全部丢失。不少新人配完白名单测得好好的隔天机器一重启规则全没了服务重新暴露出来。所以规则配置完一定要保存。不同发行版保存方式不一样RHEL/CentOS 6 及以前service iptables save规则写入/etc/sysconfig/iptablesRHEL/CentOS 7/8、Rocky Linux、AlmaLinux 等如果用了firewalld不要混用iptables命令做持久化如果确实在管理iptables需要安装iptables-services包yum install -y iptables-services systemctl enable iptables systemctl start iptables iptables-save /etc/sysconfig/iptablesUbuntu/Debian安装iptables-persistentapt install -y iptables-persistent netfilter-persistent save需要注意现在很多发行版底层已经用nftables替代了iptablesiptables命令只是一个兼容层。我见过一些系统上iptables-save保存完重启后规则又没了的Case多半是persistent服务没启动或者系统同时存在firewalld在管理nftables规则集。配置完成后最好主动重启一次机器验证规则还在不在别等业务告警了才想起检查。3. 现代发行版的主流选择firewalld白名单实战3.1 zone概念与rich rule配置如果你用的是RHEL/CentOS 7以上、Rocky Linux、Fedora这类发行版默认防火墙管理工具是firewalld很多人还停留在firewalld是iptables的壳这种认知上。这个说法没大错firewalld底层确实还是nftables/iptables但它引入了一套更直观的管理模型zone区域。每个网络接口可以归属一个zone不同zone有不同的信任级别和放行策略。比如默认的public zone对外部流量采取保守策略trusted zone则表示完全信任这个来源。给特定IP放行某个端口最灵活的是使用rich rule富规则。例如只允许办公网IP段访问SSHfirewall-cmd --permanent --add-rich-rulerule familyipv4 source address203.0.113.0/24 port protocoltcp port22 accept这条命令的语义很直白对来自203.0.113.0/24网段的IPv4流量如果目标是TCP端口22就接受。加--permanent表示写入持久化配置不加的话只对当前运行期有效。配置完记得重新加载firewall-cmd --reload如果要移除这条白名单firewall-cmd --permanent --remove-rich-rulerule familyipv4 source address203.0.113.0/24 port protocoltcp port22 accept firewall-cmd --reload很多朋友喜欢用firewall-cmd --add-port22/tcp这种方式放行端口但它放行的是所有来源相当于没有白名单。如果明确要做IP白名单rich rule才是正确的姿势别把这两个概念混了。3.2 验证与回滚先不持久化确认再保存firewalld的配置有两个状态运行期和持久化。运行期配置即时生效但不保证重启保留持久化配置写入磁盘在重启后加载。我强烈建议调试阶段只改运行期配置验证没问题后再加--permanent保存。这样即使规则写错重新加载或者重启机器系统还能恢复到原来状态不至于因为一条错误的rich rule连不上服务器。一个例子我想放行某个IP访问Web端口先执行firewall-cmd --add-rich-rulerule familyipv4 source address203.0.113.5/32 port protocoltcp port80 accept然后用另一台机器验证访问是否正常确认无误后再补上--permanent重新执行并reload。验证白名单是否生效有个实用小技巧你用非白名单IP访问一下相关端口如果被拒绝说明规则工作正常如果还能访问说明规则有问题或者有其他放行规则存在。查看当前firewalld所有有效规则用这条命令firewall-cmd --list-all它会显示当前zone里放行的所有服务、端口和rich rules一目了然。3.3 iptables还是firewalld选型经验我在实际工作中经常被问到底用iptables还是firewalld好答案取决于环境和你的使用习惯。如果是新装的标准发行版firewalld是默认方案zone和rich rule的语义清晰配置管理比纯iptables命令可读性好太多而且支持动态修改、运行时和持久化分离踩坑概率更低。如果你维护的是一批存量机器系统里已经有一套成熟的iptables脚本或者你更习惯把规则写进启动脚本里统一管理那继续用iptables也沒问题。技术没有好坏只有适不适合。不过要提醒一点firewalld和iptables-services不要同时启用否则两个工具都在管理内核规则互相覆盖排起障来酸爽加倍。选一个作为主力另一个停用并禁用开机自启。4. 再做一层保险Nginx、Servlet容器和SSH的白名单4.1 Nginx的allow/deny给管理后台加门禁网络层防火墙搞定之后其实已经能挡住绝大多数不安全访问了。但有些场景必须在应用层再配一层白名单比如同一个Nginx上跑着多个站点只希望管理后台路径对特定来源开放其他路径保持不变。这时候Nginx的allow和deny指令就派上用场了。配置在server块、location块或者http块里均可以location为例# 管理后台只允许办公网IP访问 location /admin/ { allow 192.168.1.0/24; allow 203.0.113.5; deny all; # 下面继续配置你的代理、静态文件等逻辑 proxy_pass http://backend_admin; }这段配置的意思是来源IP是192.168.1.0/24网段或者203.0.113.5时允许访问/admin/路径其他来源一律拒绝返回403。同样要注意匹配顺序Nginx的allow和deny是从上往下匹配的找到了匹配项就停止。所以必须把具体的允许规则写在deny all前面否则deny all会先拦住一切。在Nginx里做IP白名单很多人容易忽略一个问题如果前面挂了CDN、SLB或者多层反向代理Nginx拿到的remote_addr是代理服务器的IP不是真实客户端IP。直接按remote_addr做白名单要么把代理IP放行等于放开所有经过代理的来源要么把真实IP全部误拦。正确做法是先通过real_ip模块告诉Nginx哪些来源是可信代理然后从X-Forwarded-For里提取真实IP# 信任内网代理服务器 set_real_ip_from 10.0.0.0/8; real_ip_header X-Forwarded-For; real_ip_recursive on;这段配置放在http块里配合allow/deny使用$remote_addr就会变成真实客户端IP白名单判断就准确了。这个点非常关键我在后面排查章节还会再展开。4.2 服务端口白名单以Druid StatViewServlet为例Java后端用Druid连接池的很多Druid自带的监控页StatViewServlet功能强大能看到SQL执行、连接池占用、慢查询等一堆敏感信息。这个页面暴露在公网上基本等于把数据库健康状况脱光了给人看。Druid本身是有白名单设计的但很多人没配置或者根本不知道有这个参数。在Spring Boot中配置Druid监控页的白名单spring: datasource: druid: stat-view-servlet: # 开启监控页 enabled: true url-pattern: /druid/* # 白名单多个IP用逗号分隔留空意味着允许所有IP访问 allow: 127.0.0.1,203.0.113.5 # 黑名单优先于白名单 deny: 10.0.0.0/8 # 登录监控页的账号密码必须设强密码 login-username: admin login-password: 你自己的复杂密码这里有一个关键点也是官方文档里容易被忽略的说明allow参数如果不配置默认表示允许所有来源访问。这个默认开放的设计初衷可能是方便开发调试但是如果直接部署到生产环境运维又没注意到这个参数监控页就裸奔在公网上了。我在不少客户现场见过这种情况alert日志里一堆扫描器在探测/druid/index.html因为路径是公开的一探测一个准。Druid的allow参数支持IP和IP段格式是逗号分隔的字符串。常见的误区是以为deny优先实际也是deny优先即被deny的IP无论是否在allow里都会被拒绝。这是一个很实用的细节。4.3 SSH白名单从hosts.allow到Match Address在sysV时代管理SSH白名单最常见的做法是改/etc/hosts.allow和/etc/hosts.deny# /etc/hosts.allow sshd: 203.0.113.5 sshd: 192.168.1.0/255.255.255.0 # /etc/hosts.deny sshd: ALL原理是TCP Wrapper在连接到达sshd之前做一层封装式的判断。但现在很多系统默认编译时不再启用libwrap改完半天不生效是常态。所以我更推荐直接在sshd_config里使用Match Address指令它由sshd自己解析不依赖外部库跨发行版通用性更好。# /etc/ssh/sshd_config # 白名单来源走密码登录 Match Address 203.0.113.5,192.168.1.0/24 PasswordAuthentication yes # 其余来源禁用密码登录仅允许密钥登录 Match all PasswordAuthentication no注意这个逻辑先在白名单范围里允许密码认证然后在Match all里关闭密码认证。效果就是白名单之外的用户即使端口没被防火墙DROP也无法用密码登录只能使用密钥。如果你连密钥都不想放行可以在防火墙层把非白名单IP对22端口的流量直接DROP。如果要更彻底只让白名单来源能创建SSH连接可以在iptables或firewalld里设置。顺便提醒一句你的办公网出口IP如果是动态分配的配置SSH白名单要慎重否则哪天运营商重新拨号你的IP变了人就进不去了。5. 白名单配置常见的坑与排查方法5.1 规则顺序和默认策略为什么“配了但没生效”白名单配了但没生效是我见过最多的排障问题而九成以上都出在规则顺序或默认策略上。拿iptables举例子INPUT链的匹配是按顺序从上往下执行的。如果你先执行了iptables -A INPUT -j DROP这类拒绝所有数据的规则再执行iptables -A INPUT -s 白名单IP -j ACCEPT那么根据匹配顺序新到访的数据包先撞上DROP规则直接被丢弃根本轮不到后面的ACCEPT规则。所以白名单规则在前、拒绝规则在后是不变的原则。另一个问题是INPUT链的默认策略。用iptables -P INPUT DROP把默认策略改成丢弃后整个INPUT链上没有任何匹配的数据包都会被拦住。此时必须保证白名单规则已经添加成功且顺序正确。有个排查小技巧添加规则后用iptables -L INPUT -n --line-numbers查看规则行号再对照一下执行顺序基本能立刻定位问题。firewalld也是一样的道理rich rule之间也有优先级。如果你设置了多个rich rule一个允许一个拒绝结果取决于匹配顺序。所以规则配置完成后最好做正反两个方向的测试白名单IP能连通非白名单IP被拒绝才算真正完成。5.2 动态IP与安全组冲突把自己锁在门外的教训白名单配好后最大的风险不是配置本身而是来源IP变了。企业办公网的出口IP大多是动态分配的也许今天还是203.0.113.5明天运营商重新拨号就变成了203.0.113.188。如果这些IP被写死在白名单里你第二天上班就会发现SSH怎么也连不上那种感觉我太熟悉了。应对办法有几条路径一是遇到固定IP需求和网络部门确认运营商是否能提供静态IP二是通过DDNS动态域名配合运维脚本定期更新白名单里的IP三是在云上使用堡垒机让所有运维人员先登录堡垒机再把堡垒机IP加入白名单。第三种方案在正规企业里用得最多既解决了IP变化问题又方便审计操作记录算是一举两得。还有一个经常被忽视的点云服务器的安全组和系统内部防火墙是两层不同机制。安全组是虚拟机外部的网络策略系统防火墙是机器内部的策略。有些朋友在系统里加好了白名单却发现外部还是能访问一查才发现云控制台上的安全组放行了所有来源的端口等于系统防火墙外面已经失守了。反过来安全组限制太严格系统防火墙再怎么配请求也进不来。排查白名单问题时一定要把这两层都检查一遍。5.3 多层代理下的真实IP白名单能不能被绕过用户是用多层代理ip都能捕获到吗——这几乎是每次聊白名单都要被问到的问题。直接回答从TCP/IP协议本身的逻辑讲无论用户套了多少层代理服务器在建立TCP连接时一定知道直接和它握手的那一跳的IP因为这是网络层的硬事实不是应用层能隐藏的。但问题在于应用层程序拿到的一般是HTTP头部里的信息如果只依赖某些头部做判断就可能被伪造。最典型的例子是只信任X-Forwarded-For头。这个头是HTTP协议里的一个约定用于传递原始客户端IP设计初衷是好的CDN、反向代理在转发请求时把来源IP追加到头里。但它有一个天生缺陷如果Nginx没有正确配置real_ip_module去覆盖remote_addr应用直接读取X-Forwarded-For来做权限判断攻击者完全可以手动构造一个请求头把XFF写成白名单里的IP轻而易举绕过白名单。所以在我们前面提到的Nginx配置里set_real_ip_from加real_ip_header这套组合本质就是在告诉Nginx只有这些可信代理传过来的XFF才值得信任。可信代理之外任何客户端自带的XFF都不应该作为判断依据。这一点非常重要不然白名单只是筛了个寂寞。5.4 排查工具速查从ss到tcpdump最后整理一份排查白名单问题的实用命令表都是我日常高频使用的用途工具示例命令查看端口监听状态ssss -lntp | grep 22查看iptables规则iptablesiptables -L INPUT -n --line-numbers -v查看firewalld规则firewall-cmdfirewall-cmd --list-all测试端口是否能连通nc / telnetnc -zv 127.0.0.1 22测试HTTP接口状态curlcurl -I http://127.0.0.1/admin抓包分析真实来源tcpdumptcpdump -i eth0 port 22 -nn排查流程我一般是这么走的先确认服务在正常监听ss -lntp看一下端口有没有LISTEN再看系统防火墙规则iptables或firewalld当前允许哪些来源然后从外部机器用nc或者curl测连通性判断是否真的被拦截如果还查不出来抓包看数据包到没到网卡、是被丢弃还是被拒绝。按这个顺序大部分问题十分钟内就能定位出来。最后分享一个我自己的习惯也算是踩过几次坑之后养成的肌肉记忆每次改白名单规则前先把当前规则全文备份一份到文件里同时确认云平台控制台或者带外管理通道是通的再动手改。改完之后一定要用白名单内和白名单外两个视角各测一遍确认放行和拦截都符合预期最后再保存规则。这套流程看起来繁琐但真正遇到把自己锁在门外的那一刻你会感激当初多花的那两分钟。
返回列表