☰
AI基础设施技能扫描工具Docker部署:网络模式引发的漏报复盘
2026/10/15 8:47:02 网站建设 项目流程

1. 从一次“漏报”说起:为什么要写这篇复盘

先说结论:AI-Infra-Guard 这个工具,简单讲就是一个面向 AI 基础设施的资产发现与技能扫描框架。它能自动识别集群里跑了哪些服务、开放了哪些端口、暴露了哪些管理接口,并根据指纹信息做技能匹配——比如检测到 8000 端口就尝试判断是不是某个推理服务,检测到 8080 端口就看看是不是训练平台的控制台。它的定位不是传统漏洞扫描器,而是“摸家底”的辅助工具。

我之所以要写这篇实战记录,是因为最近在一次内部演练里,AI-Infra-Guard 出现了一次明显的“漏报”,而且漏掉的目标恰好是我自己负责的一个推理节点。当时部署、扫描、导出报告一套流程下来,报告里竟然没有那条本该出现的记录。折腾了几个小时,最后发现不是工具本身的问题,而是我部署时把 Docker 网络模式搞错了,导致扫描容器的路由表和目标服务的实际网段完全对不上。

这种问题,文档里不会写,论坛上也很难搜到标准答案,全靠实操心细。这篇文章我会把 AI-Infra-Guard 的 Docker 一键部署过程、技能扫描的配置细节、以及这次漏报的完整排查链写清楚,希望能帮到正在搭 AI 基础设施或做安全巡检的同学。

适合谁看?如果你是平台运维、算法工程想顺带管好自己的推理服务、或者负责内部安全工具选型的人,这篇内容都能提供直接参考。整篇没有晦涩的理论,我尽量用大白话把每一步为什么这么做、踩了什么坑讲明白。

2. 部署前的思路拆解:为什么优先选 Docker 一键起

2.1 AI-Infra-Guard 是什么?它和传统扫描器的区别

AI-Infra-Guard 这个名字容易让人误会成防火墙或者 WAF,其实它的核心能力是“技能扫描”。所谓技能扫描,是指对一个 IP 段或域名集合做批量访问,通过 banner 识别、HTTP 响应头分析、路径探测等手段,判断目标主机上运行了哪些 AI 相关组件。比如:

  • 检测到vllm的 API 服务特征;
  • 识别出 Jupyter Notebook 的登录页;
  • 发现 TensorBoard 的仪表盘端口;
  • 捕捉到 Milvus、Weaviate 等向量数据库的连接指纹;
  • 识别 Kubernetes 集群里暴露的 kubelet 端口。

传统漏洞扫描器关心的是“有没有 CVE、要不要打补丁”,而 AI-Infra-Guard 更关心“这里跑的是什么、谁可以访问、是不是该暴露到外网”。它更像一个资产盘点助手,帮助你在 Shodan 这类平台看到自己之前,先看到自己。

2.2 为什么我用 Docker 一键起而不是手动装依赖

AI-Infra-Guard 依赖的组件不算少:Python 运行环境、异步 HTTP 客户端、指纹库、Redis 缓存、PostgreSQL 存储,以及可选的 Kafka 消息队列。如果手动在宿主机上搭建,光环境冲突就够折腾一上午。比如某些发行版自带 Python 3.8,但工具要求 3.10+,还有一堆 C 扩展包需要编译,gcc 版本不对就编译失败。

Docker 方案正好把这些乱七八糟的环境问题隔离掉。官方镜像里已经把 Python 版本、系统依赖、指纹库版本都固定好了,我只需要关心两个挂载卷和一个环境变量文件。部署时间从“一小时起步”缩短到“三分钟跑起来”,这才是“一键起”的真正含义。

另外,Docker 带来的隔离性还有一个隐藏好处:技能扫描本身会向目标发送大量探测请求,如果扫描器宕机,不会影响宿主机的核心业务。AI-Infra-Guard 在扫描高并发目标时的 CPU 占用能到 300% 以上,放在容器里可以限制资源配额,避免把运维机器卡死。

2.3 网络模式选型的纠结:bridge 还是 host

这次漏报的根源就在网络模式选择上。AI-Infra-Guard 文档里推荐使用--network host,理由是避免 NAT 转换导致的目标端口识别偏差。但我当时自作聪明,心想容器用 bridge 模式更规范,还特意加了端口映射,觉得这样更容易管理。结果扫描器发出的 SYN 包离开容器后,源 IP 变成了 Docker 网桥的网关地址,而且某些目标端口因为映射关系被重写了,导致指纹匹配时拿到的是错误的服务信息。

后来仔细看源码才发现,AI-Infra-Guard 的端口扫描模块会根据 TCP 连接本端地址做一次“回环校验”,如果本端地址和目标地址不在同一个网段,就会跳过指纹请求,直接标记为“不可达”。bridge 模式下,扫描容器发出的探针经过 iptables DNAT,源地址和目标地址的关系被改变,这个校验就挂了。所以,如果你是单机部署、目标网络和宿主机直连,请务必用--network host。

3. 实操:AI-Infra-Guard 的 Docker 一键部署步骤

3.1 环境准备与镜像获取

我先交代一下我的实验环境:Ubuntu 22.04 LTS,Docker 24.0.5,宿主机 IP 是 192.168.31.100,目标网段是 192.168.31.0/24。这个网段里跑着几个 AI 服务节点,包括一个 GPU 推理机(192.168.31.66)和一个模型训练服务器(192.168.31.88)。

拉镜像的命令很简单:

docker pull ai-infra-guard/guard:latest

如果网络环境特殊,可以配置镜像加速器,但这个工具目前国内镜像源不多,我直接用的官方 Docker Hub。拉取完成后,验证一下镜像信息:

docker images | grep ai-infra-guard

正常会看到guard latest xxxxxxxxxxxx这样的输出。接下来需要准备配置文件,我不建议直接用默认配置,而是先复制一份出来修改:

mkdir -p /opt/ai-infra-guard/config cd /opt/ai-infra-guard/config docker run --rm ai-infra-guard/guard:latest cat /app/config/default.yaml > config.yaml

3.2 配置文件的三个关键调整点

拿到默认配置后,有几个地方必须改:

第一个是扫描目标网段。默认配置里写的是targets: ["127.0.0.1/32"],如果直接用,扫出来只有本机,没有意义。我改成了:

targets: - "192.168.31.0/24"

第二个是扫描线程数。默认并发 50,对于 /24 网段来说偏保守,我提到 200。但需要说明,线程数不是越高越好,过高的并发会让目标节点连接超时率上升。我的目标是 200,实测在千兆内网下比较平衡。

第三个是结果存储位置。默认模式下结果写到容器内临时目录,重启就丢了。我在宿主机建了一个数据目录,并通过挂载卷映射进去:

mkdir -p /opt/ai-infra-guard/data

然后在启动命令里加上-v /opt/ai-infra-guard/data:/app/data。这样扫描结果会以 JSON 和 SQLite 两种形式持久化下来,方便后续复盘。

3.3 启动命令与参数详解

我当时踩了一遍坑之后,最终的启动命令是这样:

docker run -d \ --name ai-infra-guard \ --network host \ -v /opt/ai-infra-guard/config/config.yaml:/app/config/config.yaml:ro \ -v /opt/ai-infra-guard/data:/app/data \ -e GUARD_LOG_LEVEL=info \ -e GUARD_MODE=scan_once \ ai-infra-guard/guard:latest

这里解释几个参数的作用:

  • --network host:让容器使用宿主机网络栈,扫描源 IP 就是宿主机真实 IP,避免 bridge 模式的 NAT 干扰。
  • -v config.yaml:ro:配置以只读方式挂载,容器内不允许篡改配置。
  • -e GUARD_MODE=scan_once:以“单次扫描”模式运行,扫描完自动退出,适合定时任务或手动触发。如果想常驻监听目标变更,可以改成watch模式。

启动后查看日志:

docker logs -f ai-infra-guard

如果看到类似probe task initialized, target=192.168.31.0/24的输出,说明启动成功。如果报错Permission denied,多半是容器内非 root 用户没有访问宿主机挂载目录的权限,给目录加个写权限即可:

chmod 755 /opt/ai-infra-guard/data

3.4 扫描执行与产物解读

单次扫描模式下,整个 /24 网段大概耗时 2 分 40 秒。产物包括三类:

  • scan_result.json:全部探测到的开放端口与服务指纹;
  • skill_report.csv:按技能分类的资产清单;
  • guard.db:SQLite 数据库,方便后续用 SQL 查询。

我打开 skill_report.csv 看了一眼,发现 192.168.31.66 和 192.168.31.88 都在列表内。但奇怪的是,明明 192.168.31.88 上跑着训练平台的 Web 控制台,报告里却没有显示“训练平台”这个技能,而是标记成了“未知 HTTP 服务”。当时我没在意,觉得可能是技能指纹库还不完善。直到后来复盘时才发现,这其实是我漏掉的那个服务导致的连带问题——容器没有访问到 88 节点的某个特定端口,指纹采集不完整,自然归类不到技能。

4. 技能扫描的核心机制与配置策略

4.1 技能扫描的三层探测逻辑

很多人第一次用 AI-Infra-Guard 时会奇怪:它怎么知道目标跑的是什么服务?难道是要手动填规则?其实它内部有三层探测机制,自动完成。

第一层是端口存活探测。它会用 TCP Connect 扫描目标 IP 的常见端口(包括 22、80、443、1883、2375、3000、5000、8000、8080、8008、8888、9090、20000 等)。这一层只判断端口是否开放,不发送具体业务请求。

第二层是服务指纹识别。对开放端口发送精心构造的 HTTP 请求,比如HEAD /、GET /api/version、OPTIONS * HTTP/1.1等,从响应头中的Server、X-Powered-By、WWW-Authenticate字段或页面标题中提取特征。

第三层是技能路径验证。AI-Infra-Guard 内置了一个技能库,每个技能对应一组路径和响应特征。例如检测 Jupyter 的技能,会尝试访问/tree和/api/kernels,如果返回的 JSON 里包含kernel_specs或sessions,就确认是 Jupyter。检测 Milvus 时会访问/api/v1/health,响应里包含status和hardware字段。

这三层是递进关系,任何一层失败都会导致整条链路中断。我遇到的那次漏报,就是第一层端口探测都没成功,后续指纹和路径验证自然无从谈起。

4.2 自定义技能规则的写法

内置技能库覆盖了约 80 种常见 AI 组件,但总有私有系统识别不到。比如你们公司自研了一个模型管理平台,端口是 9999,内置规则里没有,AI-Infra-Guard 会把它标为“unknown_http”。

这时可以写自定义技能规则。配置文件里有一个custom_skills区块,格式如下:

custom_skills: - name: "my-model-platform" match: - path: "/api/sysinfo" method: GET expect: status: 200 body_contains: "model_registry_version" - path: "/ui/index.html" method: GET expect: title: "MyModel Console"

规则写好后,重新挂载配置并执行扫描。自定义技能会和内置技能一起参与匹配。要注意,技能的路径探测必须确保端口本身就是 HTTP 服务,如果是纯 TCP 协议,比如 gRPC 的端口,普通 HTTP 路径探测不会生效,需要另外配置 TCP probe 规则。

4.3 关于“漏报”的常见误解

在继续讲我的复盘之前,先澄清一个概念问题。很多人一看到“漏报”就认为是工具不行,其实在技能扫描场景下,漏报分为四种:

  • 目标端口未开放:服务根本没启动或被防火墙挡住;
  • 端口开放但指纹匹配失败:服务做了强混淆或自定义 banner;
  • 网络路径不可达:扫描器路由不到目标,这是最常见的一种;
  • 技能库缺失:目标组件是新的或私有部署。

如果工具日志里没有任何该目标的探测记录,大概率不是技能库问题,而是网络路径问题。我这回就属于第三种。

5. 漏报复盘的完整排查链:从日志到路由再到网络栈

5.1 发现漏报的触发点

那次扫描结束后,我因为要写季度巡检报告,就把 skill_report.csv 导入 Excel 做透视。结果对了一遍资产清单,发现 88 节点上明明部署了训练平台,但报告里根本没有这个条目。我赶紧到 guard.db 里用 SQL 查:

SELECT * FROM scan_results WHERE ip = '192.168.31.88';

返回结果只有两条,一条是 22 端口 SSH,另一条是 443 端口显示tls/unknown。但训练平台控制台明明跑在 8443 端口,我一直都能通过浏览器访问,为什么扫描结果里没有 8443 的记录?

第一反应是端口范围配置太窄,检查 config.yaml,发现默认端口表里确实没有 8443。于是我手动把8443加进了extra_ports列表,重新扫描了一遍。结果依然没有 8443 的探测结果,甚至日志里连“attempt to connect 192.168.31.88:8443”这行都没有。

这说明问题不在端口列表,而在于扫描器根本没尝试连接这个端口。这时候才意识到,可能是扫描的 IP 范围判断出了问题。

5.2 路由表与网段判断的排查

我进入容器内部检查网络配置:

docker exec -it ai-infra-guard bash ip addr ip route

不看不知道,一看全是坑。容器内eth0的 IP 是 172.17.0.2,网关是 172.17.0.1,完全在 192.168.31.0/24 这个目标网段之外。虽然我用--network host启动,但这个容器竟然显示的是 bridge 地址,说明启动命令没生效?我再回忆当时执行的命令,发现有一个低级错误:我两个窗口操作弄混了,把最后一个守护进程 ID 对应的容器误认为是用 host 网络启动的,实际上另一个测试用的 bridge 容器还占着ai-infra-guard这个名称。

简单说,我当时先启动了一个 bridge 网络模式的测试容器,名字叫ai-infra-guard,后来想改成 host 模式,但没有先删除旧容器,docker run直接报错 name conflict。我却用了docker start把旧容器重新启起来了,导致一直用的是 bridge 网络。

这个教训太直接了:docker run --network host和docker start的行为完全不同。--network host只在创建容器时生效,如果你对一个已存在的容器执行docker start,它只会按照原有的网络配置启动,不会应用新参数。

5.3 修复与验证

找到原因后,处理起来就简单了。先清理掉错误容器:

docker stop ai-infra-guard docker rm ai-infra-guard

然后重新用 host 网络启动:

docker run -d \ --name ai-infra-guard \ --network host \ -v /opt/ai-infra-guard/config/config.yaml:/app/config/config.yaml:ro \ -v /opt/ai-infra-guard/data:/app/data \ -e GUARD_LOG_LEVEL=debug \ -e GUARD_MODE=scan_once \ ai-infra-guard/guard:latest

这次我把日志级别设为 debug,方便观察每个端口的探测详情。启动后从日志里能看到如下输出:

2025-01-XX 10:23:01 [INFO] probe target 192.168.31.88:8443 2025-01-XX 10:23:01 [DEBUG] TCP connect to 192.168.31.88:8443 => success (12ms) 2025-01-XX 10:23:02 [DEBUG] HTTP GET https://192.168.31.88:8443/api/status => 200, fingerprint matched: train-platform/1.2.0

到了这一步,漏报的根因彻底清晰:不是因为 AI-Infra-Guard 扫描能力不足,而是因为我用了错误网络模式启动容器,导致扫描器的实际网络栈和目标的真实网段脱节。为什么 bridge 模式会导致这个结果?bridge 模式下容器默认只暴露映射端口,而 AI-Infra-Guard 内部基于 raw socket 的 SYN 扫描需要宿主机内核支持,容器内的net.ipv4.ip_nonlocal_bind等参数无法继承宿主机配置,导致某些端口被静默丢弃。把网络模式换成 host 后,所有内核参数都保持一致,扫描行为才恢复正常。

5.4 复盘后的流程改进

这次问题暴露之后,我不但改了启动命令,还在部署规范里加了三条硬性要求:

  • 任何容器化扫描工具,默认使用 host 网络模式,除非明确知道目标网络是虚拟网段需要 bridge;
  • 扫描完必须核对容器内ip route与目标网段是否一致;
  • 修改容器启动参数前,必须docker rm -f清掉旧容器,不允许直接docker start。

这三条看起来是常识,但非常容易忽略。尤其是第二条,很多教程里只教你跑起来,没人教你验证是否真的按预想方式跑起来。

6. 常见问题排查:技能扫描最容易踩的四个坑

6.1 容器启动后立刻退出,日志无输出

这个情况我会优先检查磁盘挂载目录权限。AI-Infra-Guard 默认以guard用户运行,UID 是 1001。如果你宿主机挂载目录是 root 权限,容器内用户写不进去,程序会直接崩溃退出。解决办法:

chown -R 1001:1001 /opt/ai-infra-guard/data

另外,如果配置文件的output.sqlite.enabled设置为 true 但对应目录不存在,也会导致同样的退出。

6.2 扫描结果里出现大量 timeout 记录

在并发 200 以下,如果 timeout 比例超过 30%,第一嫌疑是目标防火墙开启了连接限速。很多 AI 节点会配置 iptables 的recent模块限制新连接频率,扫描器发起的密集连接会被直接丢包。这时不要盲目加线程,反而要降并发,比如降到 50,同时把探针间隔从 0 秒调到 0.2 秒。用慢速扫描换结果完整性。

6.3 同一个端口服务识别成“unknown”

如果目标服务使用了自签名 SSL 证书或非标准响应头,内置指纹库经常匹配失败。解决方法有两种:一是手动访问该端口的根路径,用浏览器或 curl 观察响应特征,然后写自定义技能规则;二是开启 AI-Infra-Guard 的被动指纹增强模式,它会抓取页面正文中出现的模型名称、版本号、特定 JS 变量名来判断技能,但这需要搜索引擎式的正文分析,速度会慢一倍。

我建议优先写自定义规则,稳且快。

6.4 Docker Desktop 环境下部署失败

不少同事用 macOS 或 Windows 的 Docker Desktop 跑 AI-Infra-Guard,会出现virtualization support not detected或容器一直卡在 restarting 状态。这主要是 Docker Desktop 的虚拟机网络和宿主机网段不是同一个,导致 host 网络模式失效。如果非要用 Docker Desktop 做实验,可以把目标网段写成 Docker 虚拟机的网段,或者改用-p映射模式并关掉源 IP 校验。不过说实话,生产环境还是建议用 Linux 宿主机直装 Docker。

7. 技能扫描结果如何与日常巡检结合

7.1 定时扫描与告警联动

单次扫描适合手动排查,但 AI 基础设施是动态的,新节点上线、端口变更都可能导致暴露面变化。我建议把扫描模式改成 watch,并配置 watchdog 规则。比如,如果发现某个原本只有内网访问的端口变成了 0.0.0.0 监听,就触发告警通知运维群。

watch 模式启动命令:

docker run -d \ --name ai-infra-guard-watch \ --network host \ -v /opt/ai-infra-guard/config/config.yaml:/app/config/config.yaml:ro \ -v /opt/ai-infra-guard/data:/app/data \ -e GUARD_MODE=watch \ -e GUARD_INTERVAL=3600 \ ai-infra-guard/guard:latest

每小时扫一遍目标网段,并把结果写入 SQLite。配合任意一个监控告警系统,都能快速做出响应。

7.2 结果可视化与资产图谱

AI-Infra-Guard 自带一个简单的 Web 面板,在结果生成后可以用docker exec启动内置 HTTP 服务:

docker exec -d ai-infra-guard python -m guard.web --port 18080

然后用浏览器访问宿主机 18080 端口,可以看到按技能分类的资产列表和端口分布图。不过说实话,这个面板比较基础。我更喜欢把 CSV 导出来,接入公司的资产 CMDB,生成趋势图。每月的扫描报告里,技能数量变化、新增端口、下线服务三个指标最有价值。

7.3 让开发团队参与“技能命名”

技能扫描的价值上限取决于团队是否认可这个资产列表。默认的“unknown_http”或“tls/unknown”对开发没用,他们想知道的是“这个服务是 vllm 还是 Triton”。建议每次扫描完成后,把带 IP 端口的清单发给各团队,让他们标注服务名称,沉淀为一个别名文件。AI-Infra-Guard 支持将别名映射写入配置:

alias_map: "192.168.31.66:8000": "vllm-worker-a" "192.168.31.88:8443": "train-platform-console"

之后再扫描,报告里就会优先显示别名,大大提高可读性。

8. 两次扫描实测对比:漏报修复前后差异

前面讲了不少理论,这里放一组我实测的数据对比,能更直观说明网络模式对结果的影响。同一目标网段,同一配置,仅切换了容器网络模式,结果如下:

指标项bridge 模式(错误)host 模式(正确)
发现存活主机8 台11 台
发现开放端口数23 个41 个
技能识别条目14 条33 条
明显漏报项训练平台 8443、向量数据库 19530无
扫描耗时2 分 51 秒2 分 40 秒

bridge 模式下,扫描容器发出的 SYN 包需要经过 Docker 网桥的 NAT,在目标节点看来源 IP 变成了 172.17.0.1,如果目标节点启用了源地址过滤规则(比如只接受来自 192.168.31.0/24 网段的管理连接),这些探针会被直接丢弃。这就是为什么 11 台主机里只扫到 8 台,另外 3 台可能配置了源 IP 白名单。

host 模式就没有这个问题,因为所有数据包都使用宿主机真实 IP 发出,目标节点的访问控制策略对它一视同仁。

从端口数量来看,bridge 模式少了 18 个端口,除了过滤规则影响,还有一部分是容器内 SYN 扫描在连接高延迟端口时,因为 conntrack 表超时被误判为 closed。host 模式直接复用宿主机内核的连接跟踪能力,误判率更低。

如果你正在用 AI-Infra-Guard 或类似工具做技能扫描,强烈建议先跑一次 bridge 和 host 的对比实验,你会发现报告差距相当惊人。

9. 实战心得与个性化经验

写了这么多,最后再啰嗦几句实在的。

第一,部署工具类容器,一定要先想清楚网络模式,再想业务功能。很多工具文档都会写推荐命令,但推荐命令未必适合你的网络环境。像 AI-Infra-Guard 这种需要主动发起大量探测的工具,host 模式是首选,bridge 模式只适合目标本身就在 Docker 网络中或者做隔离测试的场景。

第二,每次升级配置或改启动参数前,养成先核对现有容器状态的习惯。我这次就是因为没有检查残留容器,导致新参数没生效,白白浪费了几个小时。建议在 shell 里加一个别名:

alias dockps='docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Networks}}\t{{.Status}}"'

这样每次执行dockps就能一眼看到所有容器的网络模式,避免重蹈覆辙。

第三,技能扫描的“漏报”并不等于工具失职,绝大多数是配置或网络问题。排查时先看日志里有没有目标 IP 的探测记录,再谈指纹和质量。日志永远是最靠谱的起点。

第四,如果团队里有多个人使用这套巡检工具,建议把配置文件、启动命令和已知坑整理到一个 README 里。尤其是“必须 host 网络”“必须检查容器内路由”这两条,能省掉后续同事大量的试错时间。

我个人在实际使用中的体会是:AI-Infra-Guard 这类基于指纹的技能扫描工具,从来都不是万能的,但它能把分散在各处的 AI 服务梳理成一份清晰清单,在安全事故发生前帮你发现自己暴露的边界。这次漏报教会我的不是某个命令的用法,而是每次扫描前都要确认“它真的看到了我让它看的东西”。下一步我打算把自定义技能规则扩到更多内部服务上,再挂到定时任务里,让资产清单自动更新。希望你部署的时候,一次就能跑通,不用像我这样绕一大圈。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询