ARTICLE DETAIL

资讯详情

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

自建内网离线IP数据库,实现本地化IP归属地查询与安全审计

自建内网离线IP数据库,实现本地化IP归属地查询与安全审计 前阵子维护一套内网业务系统时客户提了一个很实际的需求日志系统每天产生海量访问记录里面全是IP地址但做安全审计时却查不出这些IP来自哪个地区、属于哪个运营商、是不是恶意扫描源。问题是这套系统运行在完全隔离的内网环境里没有任何公网出口常规的在线IP查询接口根本没法用。于是就有了这个项目在内网系统里自建一套离线IP数据库把IP归属地查询能力完全本地化。这套方案解决的核心问题就是让内网系统在没有外网的情况下依然能够完成IP归属地解析、来源区域统计、异常IP识别和资产溯源。整个项目从数据准备、解析代码、库表设计到与日志系统、网络设备的联动配置再到后续的离线更新维护是一套可以完整落地、可以直接抄作业的方案。适合需要做等保审计、日志分析、内网资产盘点以及网络运维的同学参考。1. 为什么内网系统需要一套离线IP数据库1.1 内网场景的约束决定了“在线查询”这条路走不通很多刚接触内网项目的同学会想IP归属地查询不是很简单吗调一下百度IP查询API、纯真IP在线接口或者用淘宝IP库几行代码就能拿到结果。但真实的内网生产环境完全不是这样。我遇到过的内网系统绝大多数部署在独立的VLAN或物理隔离的网段里堡垒机以外的机器根本没有访问公网的路由权限安全策略连DNS解析都限制得死死的。在这种环境里在线IP查询会面临几个致命问题。第一是可用性内网一旦与公网断开所有依赖外网接口的功能全部瘫痪日志分析任务跑一半就报错。第二是合规性很多单位对数据出网有严格要求业务日志里的IP、账号、访问时间这些信息直接通过HTTP请求发到第三方接口本身就有数据泄露风险等保评审的时候这一条就过不去。第三是性能几十万条日志逐条调用在线接口响应时间不可控接口限流更是家常便饭根本扛不住批量解析的吞吐需求。所以在内网系统里做IP归属地能力唯一的正解就是把IP数据库下载到本地在本地用代码解析完全离线运行。这就是这个项目的出发点。1.2 离线IP库解决了哪些实际问题我在实际项目中总结下来离线的IP归属地能力至少解决了四类问题一类是日志溯源。安全设备、Web服务器、应用系统每天记录大量访问IP有了离线IP库就能把“202.xxx.xxx.xxx”这种冷冰冰的地址翻译成“某省某市某运营商”安全事件复盘时定位来源范围效率提升非常明显。二类是攻击研判。内网服务器被扫描、被爆破时通过IP库能快速判断攻击来源是外部公网地址还是内网跨段地址是运营商动态拨号地址还是IDC机房地址这对后续封禁策略的制定很有帮助。三类是资产盘点。内网系统往往有数千台设备通过分析设备访问日志中的IP分布可以反推网段使用情况、业务系统分布配合交换机MAC表能比较完整地还原整个内网拓扑。四类是流量治理。带宽监控系统、上网行为管理系统里按IP归属地做流量统计能看出哪个地区的访问量异常为策略调优提供依据。1.3 方案选型自建库、纯真库还是GeoIP库确定离线路线之后下一个问题就是数据从哪来。这个环节我对比过三种主流方案。第一种是自行维护IP地址台账。把自己单位分配到的公网IP段、内网私有网段全部整理成一张表手工标注归属部门、用途、地理位置。这种方式数据完全可控但覆盖范围只限本单位对于日志里出现的外部IP无能为力适合作为辅助手段。第二种是使用纯真IP库QQWry.dat。这是一个在国内运维圈用了很多年的离线IP数据库文本格式发布数据文件几十MB包含IP段、起始地址、结束地址、归属地、运营商等信息解析速度快国内IP精度较高离线更新也方便。缺点是它的数据格式是自有的二进制格式需要专门的解析代码。第三种是GeoIP系列MaxMind GeoLite2。国际通用支持IPv4和IPv6数据格式为MMDB二进制有官方解析库查询性能很强但国内IP的精度不如纯真库细很多只到省份级别。我的建议是采用“纯真库为主、自建台账为辅”的组合方案。如果是全球业务系统可以考虑GeoIP做补充两个库并行解析优先返回精度高的一方。这个方案在多个项目里验证过能满足内网审计对国内IP精度的要求也保留了内部地址自定义标注的能力。2. 离线IP数据库的核心原理与数据格式解读2.1 IP地址结构与归属地解析的基本思路IP地址本质上是32位二进制数IPv4日常写法是点分十进制。IPv6则是128位二进制数。离线IP归属地查询的思路是把IP地址转换成一个整数然后在一张有序的“IP段-归属地”映射表里查找这个整数落在哪个区间内命中区间后取出对应的归属地字符串。这个过程类似于查字典目录按照拼音顺序排好你要找的字通过页码索引直接翻到对应页。IP库里的数据就是按IP数值排好序的区间表查询时用二分查找快速定位时间复杂度是O(logN)几百万条记录只需十几次比较就能命中。理解这个逻辑很重要因为后续所有解析代码、性能优化都是围绕“有序区间二分查找”展开的。很多同学直接用暴力遍历去匹配IP段数据量小的时候感觉还行一旦日志量大了性能差距瞬间就体现出来了。2.2 纯真QQWry.dat文件格式深度解析纯真数据库QQWry.dat是我用得最多的离线IP库它的文件结构分为三部分文件头、记录区、索引区。文件头是8字节前4字节是第一条索引的绝对偏移后4字节是最后一条索引的绝对偏移。索引区每条固定7字节前4字节是IP段起始地址网络字节序后3字节是记录区的绝对偏移。记录区每条记录的前4字节是IP段结束地址后面是归属地信息字符串。归属地字符串使用GBK编码这在内网Linux环境下是个常见的坑直接按UTF-8读取会乱码需要在读取时做GBK转UTF-8的处理。记录还可能包含“重定向”模式也就是归属地信息里某个位置存储的是偏移量而不是字符串解析时需要判断标志位我最初自己写解析器时就被这个细节坑过后面会详细说。2.3 GeoIPMMDB格式和文本CSV格式的差异如果选择GeoIP了解MMDB格式的逻辑也有必要。MMDB是一个二进制数据库内部结构类似只读的B树查询时根据IP的二进制位逐步下探节点最终在叶子节点取出数据记录。它天然支持IPv4和IPv6混合存储官方提供libmaxminddb库多语言绑定非常完善。还有一些开源项目会直接提供CSV格式的IP段数据字段一般是“起始IP,结束IP,起始IP数值,结束IP数值,国家,省份,城市,运营商”。这种格式最简单适合导入到MySQL、PostgreSQL或ClickHouse里做SQL查询缺点是没有现成的压缩索引数据量大了之后查询效率需要自己保证。三种格式我列了一个对比表方便大家选型。数据源文件格式国内精度解析难度性能典型更新方式纯真库QQWry.dat省份/城市较细中等需处理GBK和重定向高定期下载离线包GeoIPMMDB省份级低官方库直接读很高每周/每月更新文本CSVCSV取决于源低中需建索引导入数据库更新实际项目里我通常把三类数据都准备一份纯真库做日常快速解析MMDB做海量日志批处理参考自建台账做内网私有地址精确标注三者互备。2.4 离线IP库常见的两类“重定向”机制解析纯真库时最容易被忽略的就是重定向。QQWry.dat的记录区有两种特殊的标志位第一种是0x01表示“国家/地区”字段实际是一个重定向偏移真实字符串存放在另一个位置第二种是0x02表示“省份”字段存在重定向。这是纯真库为了节约空间搞出的设计类似文件系统里的符号链接。如果解析代码不做重定向处理直接按偏移读取字符串轻则读到乱码重则解析出的地址完全张冠李戴。不同版本的纯真库重定向行为稍有差异建议解析代码里把这两种情况都做兼容。我写解析器时的处理原则是读取记录时先取4字节的IP结束地址然后读取归属地字符串读到标志位为0x01时取后续3字节作为新的偏移地址跳到那个位置重新读取读到0x02时对“省份”字段做同样处理。这样多个版本的库文件都能正确解析。3. 内网离线IP数据库的完整搭建实操3.1 环境准备与基础依赖下面进入实操环节。先说一下环境我这次搭建使用的是内网的一台CentOS 7服务器4核8G内存系统盘剩余空间充足。Python版本3.6以上需要的第三方库只有两个struct标准库用于二进制解析codecs标准库用于GBK解码。全程不需要联网安装任何外部包这在内网环境里非常重要。如果你的内网机器连系统盘都没法访问外网我通常的做法是在有外网的机器上提前准备好离线安装包用U盘拷贝进去。因为Python标准库已经覆盖了核心解析能力我一般建议把解析逻辑封装成单个独立模块不依赖第三方库这样拷过去就能直接用省去依赖安装的麻烦。3.2 数据准备离线包制作与校验数据源我们使用纯真IP库的离线数据包。具体做法是在一台有外网的机器上下载最新版纯真IP数据包解压后得到qqwry.dat文件然后用md5sum计算校验值把这个文件和校验值一起拷贝到内网服务器。不要小看这一步。我踩过坑有一回U盘拷贝过程中文件损坏程序解析时频繁报错一开始还以为是代码问题排查了很久才发现是数据文件本身不完整。所以建议无论从哪个渠道获取数据导入内网之前一定要做两步校验第一步比对文件大小纯真库一般几十MB大小异常肯定有问题第二步比对MD5或SHA256确保文件与源站一致。内网导入路径建议统一放在/data/ipdb/目录下文件命名为qqwry_YYYYMMDD.dat保留日期信息方便后续版本回滚。这个命名习惯帮我解决过一个大麻烦某次新库更新后解析正确率反而下降靠着保留旧版本文件直接回滚几分钟就恢复了。3.3 核心解析代码实现二分查找与缓存有了数据文件接下来就是写解析模块。这里给出我封装的Python解析器核心代码可以在内网直接运行。import struct import socket import codecs import bisect class QQWry: def __init__(self, filepath): self._handle open(filepath, rb) self._total self._read_offset(0) # 第一条索引偏移 self._end self._read_offset(4) # 最后一条索引偏移 self._count (self._end - self._total) // 7 1 def _read_offset(self, pos): self._handle.seek(pos) data self._handle.read(4) # 纯真库中的偏移量是3字节有效第4字节恒为0 return struct.unpack(I, data)[0] 0x00FFFFFF def _read_string(self, offset): self._handle.seek(offset) raw b while True: ch self._handle.read(1) if not ch or ch b\x00: break raw ch return codecs.decode(raw, gbk, errorsignore) staticmethod def ip2long(ip): return struct.unpack(I, socket.inet_aton(ip))[0] def find(self, ip): ip_int self.ip2long(ip) low, high 0, self._count - 1 while low high: mid (low high) // 2 index_pos self._total mid * 7 start_ip struct.unpack(I, self._handle.read(4))[0] self._handle.seek(index_pos 4) record_offset self._read_offset(self._handle.tell()) if ip_int start_ip: high mid - 1 continue end_ip struct.unpack(I, self._read_offset(record_offset))[0] if ip_int end_ip: low mid 1 continue country self._parse_location(record_offset 4) return country return (未知, ) def _parse_location(self, offset): self._handle.seek(offset) flag self._handle.read(1)[0] if flag 0x01: new_off self._read_offset(self._handle.tell()) return self._parse_location(new_off) if flag 0x02: new_off self._read_offset(self._handle.tell()) area self._read_string(new_off) area_off offset 4 4 # 跳过重定向地址后取区域 area2 self._read_string(area_off) return (area area2).strip() # 普通模式直接读取国家、省、市 country self._read_string(offset) area self._read_string(offset len(country.encode(gbk)) 1) return (country area).strip() def close(self): self._handle.close()这个实现有几个关键点要说明。第一是bisect可以用来优化索引查询但上面的二分查找已经足够快几百万条索引最多比较20次左右。第二是读取索引时索引区每7个字节中只有前4字节是起始IP后面3字节是记录偏移所以不要直接从文件里读成8字节否则会错位。第三是socket.inet_aton会把IP转成4字节网络字节序再解包成无符号整数得到的就是IP的数值表示。3.4 查询接口封装与批量解析生产环境里日志系统经常需要一次性解析几万甚至几十万个IP。如果每解析一个IP都重新打开文件、定位、查找IO开销会非常夸张。我的做法是初始化时把索引区全部加载到内存查询时用内存二分查找再按需读取记录区。优化后的查询接口如下class QQWryFast(QQWry): def __init__(self, filepath): super().__init__(filepath) # 把所有索引读入内存 self._index [] self._handle.seek(self._total) for i in range(self._count): raw self._handle.read(7) start_ip struct.unpack(I, raw[:4])[0] rec_off (raw[4] | (raw[5] 8) | (raw[6] 16)) self._index.append((start_ip, rec_off)) def find_fast(self, ip): ip_int self.ip2long(ip) pos bisect.bisect_right(self._index, (ip_int,)) - 1 if pos 0: return 未知 start_ip, rec_off self._index[pos] if ip_int start_ip: return 未知 self._handle.seek(rec_off) end_ip struct.unpack(I, self._handle.read(4))[0] if ip_int end_ip: return 未知 return self._parse_location(rec_off 4)实测下来内存索引方案比逐条磁盘查找快了很多倍。在一台普通服务器上批量解析10万个IP原先需要十几秒优化后不到1秒。如果是超大规模日志分析还可以再加一层LRU缓存把最近查询的热门IP结果缓存下来命中率通常很高。3.5 将解析能力封装成内网API服务解析模块写好后不建议每个业务系统各自引入一份代码那样版本维护会很痛苦。我习惯把它封装成一个本地HTTP服务监听内网地址的某个端口返回JSON结果。这样日志系统、运维平台、安全设备都能通过HTTP调用统一维护一份数据。下面是一段基于Flask的示例代码只需要Flask这一个依赖。from flask import Flask, request, jsonify from qqwry import QQWryFast app Flask(__name__) db QQWryFast(/data/ipdb/qqwry_20250101.dat) app.route(/ip/lookup) def lookup(): ip request.args.get(ip, ) try: location db.find_fast(ip) return jsonify({ip: ip, location: location, status: ok}) except Exception as e: return jsonify({ip: ip, error: str(e), status: fail}) if __name__ __main__: app.run(host127.0.0.1, port8999, threadedTrue)生产环境建议用Gunicorn之类的WSGI服务器部署或者干脆不搞HTTP直接把解析模块以Python包的形式分发到各业务服务器两种方式各有优劣。HTTP方式省心但多一跳网络开销包方式性能最好但升级时需要同步更新所有节点。我一般的建议是节点少于10个用包同步多于10个用HTTP服务减少分发压力。4. 内网环境下的特殊处理与联动配置4.1 私有网段的归属地覆盖策略内网系统日志里最常见的其实不是公网IP而是RFC1918规定的私有地址段比如192.168.0.0/16、10.0.0.0/8、172.16.0.0/12。这些地址在纯真库里通常只标注为“保留地址”或“局域网”对我们的审计需求来说几乎没有价值。所以我在项目中专门维护了一个“内部台账”表把内网实际规划的子网段、用途、部门、物理位置全部登记进去。比如10.10.1.0/24对应“财务部服务器区”、10.10.3.0/24对应“办公终端区”。解析时先查私有网段台账命中则直接返回内部信息未命中再走离线IP库。这样日志分析里的内网流量也能做到精细化溯源。台账表用MySQL维护最方便我贴一下结构CREATE TABLE internal_net_segment ( id INT AUTO_INCREMENT PRIMARY KEY, net_segment VARCHAR(32) NOT NULL COMMENT 网段例如10.10.1.0/24, start_ip BIGINT NOT NULL COMMENT 起始IP数值, end_ip BIGINT NOT NULL COMMENT 结束IP数值, owner_department VARCHAR(128) NOT NULL COMMENT 归属部门, business_system VARCHAR(128) COMMENT 关联业务系统, location_desc VARCHAR(255) COMMENT 物理位置说明, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;数据录入完成后在查询逻辑里加一个前置分支即可代码很简单就不展开了。4.2 使用telnet和系统命令验证IP端口连通性内网里经常要排查某个IP的某个端口是否通。这个操作和IP库是两件事但在实际运维中往往同时发生日志里出现一个未知IP的访问记录我们通过IP库查到它来自外部某地下一步就要验证这个IP当前还能不能访问我们的业务端口。最常用、最不依赖额外工具的命令就是telnet。直接用telnet IP 端口如果端口开放会显示连接成功并进入空界面如果端口不通会长时间卡住或返回连接失败。但注意很多内网Linux机器默认没有安装telnet客户端在没有外网的情况下可以用/dev/tcp这个bash内置特性来探测timeout 5 bash -c echo /dev/tcp/10.0.0.8/3306 echo port open || echo port closed这条命令的意思是尝试向10.0.0.8的3306端口建立TCP连接5秒超时连接成功打印open失败打印closed。/dev/tcp是bash在编译时开启的特性几乎不需要额外依赖非常适合内网离线环境。同理也可以用nc工具但如果nc没装/dev/tcp就是最好的替代方案。对于已安装telnet的机器建议使用telnet IP 端口后结合Ctrl]进入命令模式再退出避免直接断开导致终端状态异常。实际操作中telnet测试HTTP端口时还会收到服务器返回的响应头信息对判断服务状态很有用。4.3 内网IP冲突的定位与排查日志或网络管理里经常出现IP冲突问题这个和IP库项目也有关系。IP冲突通常表现为某台设备突然断网、系统提示IP地址已被使用排查手法我有几个固定套路。最直接的是在核心交换机上查看ARP表找到冲突IP对应的MAC地址通常会有两个不同的MAC。命令是arp -a或者登录交换机执行display arp | include IP不同厂商命令略有差异。然后结合IP库和内部台账判断这两个MAC分别属于哪台设备、接入哪个交换机端口。Windows环境下可以在故障机上先ping IP再执行arp -a看解析到的MAC然后对比交换机上学习到的MAC。Linux下也可以用ip neigh命令查看邻居表。如果网络里有多个DHCP服务器冲突大多是因为不同网段的DHCP分配了重复地址这种要在DHCP服务器端排查地址池设置。整个排查过程中先确认冲突MAC再用IP台账反查设备位置是最高效的路径。4.4 与日志系统、防火墙的联动落地离线IP库的真正价值体现在联动上。我们的日志平台用ELK在Logstash里直接加了一个插件解析日志时自动调用本地的IP查询接口为每条访问日志附加“来源归属地”字段。这样在Kibana里做可视化时直接按归属地聚合哪里的扫描攻击最多、哪个地区的正常流量占比最高一眼就能看清楚。防火墙方面我们手工整理了一份“威胁IP段列表”来源是安全设备告警日志经过IP库归属地分析后筛出的高风险外部地址段再导入防火墙黑名单。这个过程每周跑一次有效缓解了大量重复扫描流量。当然封禁前要充分评估业务影响避免误封正常客户IP。联动架构上我画了一张逻辑脑图核心链路是日志采集器 → 本地解析API → 归属地字段入库 → 展示与告警。整条链路全部在内网完成不依赖任何外网接口这也是这个方案最大的优势所在。5. 离线IP库的日常维护与长期更新5.1 离线数据更新流程设计离线IP库的更新不能靠手工拷贝一旦服务器数量多了手工操作必然出错。我的做法是一月一次由一台有外网的跳板机下载最新纯真IP数据包生成校验文件推送到内网的一台主数据服务器再由主服务器分发到各节点。具体流程分四步走下载最新qqwry.dat文件名带上日期在跳板机执行md5sum qqwry_YYYYMMDD.dat生成校验值通过内网文件传输通道推送到主服务器/data/ipdb/目录主服务器执行一个分发脚本把文件和校验值推到各业务节点各节点验证MD5一致后更新本地解析器的数据文件句柄热加载新版数据。更新时注意解析器如果正在被频繁调用千万不要直接覆盖正在读取的旧文件。Linux下虽然可以边读边替换但极端情况下会读到文件头部不一致的脏数据。我的做法是新文件用新文件名落地解析器提供reload(filepath)接口内部重新打开文件描述符查询线程短暂阻塞后切到新文件然后旧文件延迟删除。这个机制在内网环境里跑了两年非常稳定。5.2 数据质量监控与比对离线IP库的更新有一定风险新版库可能出现数据回退、字段缺失、甚至部分IP段归属错误。所以我维护了一套质量监控脚本每次更新后自动抽取固定样本进行比对。样本包括三类本省重点地市IP、知名网站IP、历史告警IP段。脚本解析新库和旧库逐条比对归属地结果如果差异率超过阈值比如5%说明新库可能有数据异常自动报警人工介入。这个监控脚本本身只依赖Python标准库可以用crontab定时执行非常轻量。另外建议定期统计库中记录的IP段总数对比历史版本。如果总条数出现大幅度缩水要警惕数据文件被截断或下载不完整。这个统计字段在纯真库的索引数量里可以直接拿到解析器初始化时就能输出。5.3 台账数据与库版本的生命周期管理内部台账表同样需要维护。内网业务系统调整频繁新网段、新部门不断出现台账如果不同步日志分析里的内网溯源就会失真。我要求台账管理员每月核对一次把新增网段和废弃网段及时更新。数据库里加一个status字段标记为active和deprecated查询逻辑只关联active数据旧网段自动失效。离线IP库版本管理上建议保留至少最近3个月的旧文件不要更新完就删旧文件。一旦新库数据质量出问题回滚操作只需要改一下配置文件里的数据文件路径重启解析服务即可。数据文件放在独立目录并做定期冷备这点在内网安全审计时也会被检查到。5.4 冷备与容灾考虑虽然IP库解析不是什么高可用系统但它是日志分析、安全告警链路上的依赖点一旦服务挂掉新写入的日志会丢失归属地信息。所以我在两个节点分别部署了解析服务前端用一个内网负载均衡地址分发请求节点挂了自动切换保证查询不中断。冷备方面主数据服务器的/data/ipdb/目录每天凌晨4点打包备份到备份服务器备份保留最近12份每月归档一次。这个备份策略不复杂但能在数据文件被误删、误覆盖时快速恢复。亲身经历告诉我内网环境里越简单的备份越可靠花里胡哨的方案反而容易在关键时刻掉链子。6. 常见问题与排查技巧实录下面把我在实施过程中遇到的高频问题整理成速查表每个问题都给出直接可用的解决方案。现象原因解决方案解析出乱码GBK/UTF-8编码未转换用codecs.decode(raw,gbk)解码大量IP查不到数据文件损坏或版本过旧重新获取最新离线包并校验MD5内网IP显示保留地址未配置内部台账增加私有网段台账表批量解析耗时过长每次查询重复读文件索引加载内存使用二分查找更新后结果变差新版库数据问题回滚旧版本文件监控比对telnet命令不存在未安装telnet客户端用/dev/tcp bash内置特性替代6.1 乱码问题GBK解码这个坎纯真库的字符串是GBK编码这是个历史包袱毕竟这个库最初就是中文Windows时代设计的数据结构。在Python3里字符串默认是Unicode如果用默认方式读二进制字节再直接输出必然出现乱码。正确做法是用codecs.decode(raw, gbk)并且可以加上errorsignore参数这样遇到个别无法解码的字节不会导致整个查询崩溃。我在一次版本更新后曾经遇到部分IP段返回空字符串后来定位到是某个数据包的编码混入了异常字节加上errorsignore后再把空字符串兜底成“未知”问题就解决了。6.2 二分查找边界与匹配不准的问题自己实现二分查找时最容易错的是索引区间边界。纯真库索引区第一条和最后一条偏移存在文件头里有些资料里把个数计算成(end - start) / 7但实际上是除以7再加1否则会漏掉最后一条索引。我排查过一次某个特定IP段总是查不到比对文件后发现就是索引条数少算了一条导致最后一个IP段永远无法命中。另一个坑是匹配区间时不仅要比对起始IP还要同时验证IP是否小于等于记录区里的结束IP。有的解析器只比对起始IP就开始返回结果一旦IP落在两条索引之间的缝隙里就会返回错误的归属地。代码里面if ip_int end_ip这个判断不能省。6.3 内网telnet探测端口时的超时处理用telnet命令探测端口最大的坑是目标主机不可达时默认会卡很久。比如日志里有个可疑IP你想确认它的22端口是否开放直接telnet 1.2.3.4 22如果这个IP不存在可能会卡几十秒甚至更久。我的经验是要么给命令加超时工具比如timeout 3 telnet 1.2.3.4 22要么直接使用前面提到的/dev/tcp方案。另外telnet成功的判断依据是看到类似SSH-2.0-OpenSSH_7.4的banner信息如果在连接后立即出现这些内容说明端口开放且服务在运行。如果只出现连接信息但没有banner也可能是防火墙对端口做了代理需要进一步结合协议分析判断。6.4 日志中大量异常IP的处置思路日志系统接入离线IP库后经常暴露出大量未知IP或者异常归属地IP。我的处理思路分三步先按IP归属地聚合统计看是否有某个地区IP疯狂扫描然后利用内部台账核对这些IP是否来自已知网段最后把确认恶意的IP段整理成封禁列表提交到防火墙和主机防护策略。处置时要注意别图省事直接把一个大地市IP段整个封掉这样容易误伤正常业务。我一般先封禁连续攻击的单个IP如果攻击来源分散但都落在某个IDC机房段再考虑封禁对应子网。纯真库对IDC机房地址有相对明确的标注这个信息很有参考价值。6.5 解析服务挂掉后的降级方案内网解析服务一旦不可用业务不能跟着挂。我的降级方案是日志系统在调用IP解析API失败时自动把结果标记为“解析失败”同时把IP原文保留下来等解析服务恢复后再做补解析。这个逻辑不复杂但能保证日志采集流程不中断。补解析的任务用定时脚本实现每天凌晨扫描最近24小时解析失败的数据重新提交到解析服务成功后更新结果字段。经过补解析整体解析率可以稳定保持在99%以上。这个方案对ELK这种文档型存储特别友好更新一条日志字段用脚本批量处理即可。我个人在几个项目里连续用这套离线IP库方案最大的体会是IP归属地解析看起来是个小功能但真正做好、做稳、做成长效运行的公共服务需要考虑数据源、解析性能、更新机制、异常降级、台账联动一整条链路。内网系统没有公网可用恰恰逼着我们把这个小功能做成一个独立、可靠、可持续维护的模块。如果你们的内网系统也有类似的日志审计、IP溯源需求照着这套方案落地遇到的问题基本都能在本文里找到对应的解法。最后再提醒一句离线库的更新和台账的日常维护一定要有固定的人和时间点这比代码本身更重要。
返回列表