Vuls漏洞扫描器实战:无代理架构、多源数据融合与DevSecOps集成
1. 项目概述:Vuls,一个为运维工程师减负的漏洞扫描器
在安全运维的日常里,最让人头疼的几件事是什么?是每天手动刷新NVD(美国国家漏洞数据库)看有没有新漏洞影响自己的服务器?是面对成百上千台机器,根本记不清哪台装了哪个版本的OpenSSL?还是每次出了高危漏洞,都要连夜写脚本、登服务器、查版本、做分析,生怕漏掉一台?如果你对这些问题频频点头,那么Vuls这个工具,可能就是为你量身打造的。
Vuls是一个用Go语言编写的、无代理(Agent-less)的漏洞扫描器,主要面向Linux和FreeBSD系统。它的核心设计理念非常直接:自动化、精准化、可集成。它不负责帮你打补丁,那是配置管理工具或自动化运维平台的事。Vuls的职责是像一个不知疲倦的哨兵,持续地告诉你:“你的资产里,哪些服务器、哪些软件包,受到了哪些已知公开漏洞的影响,严重程度如何,甚至有没有公开的利用代码(PoC)。” 这听起来简单,但要把这件事做准、做全、做得对生产环境友好,里面门道可不少。
我最早接触Vuls是在一次内部安全审计之后,当时我们手动梳理资产和漏洞,耗时耗力还容易出错。引入Vuls后,我们将漏洞扫描集成到了CI/CD流程和日常监控中,实现了从“被动救火”到“主动预警”的转变。接下来,我将结合多年的一线使用经验,为你深入拆解Vuls的设计哲学、实战部署、高级用法以及那些官方文档里不会明说的“坑”。
2. Vuls核心架构与设计哲学解析
2.1 为何选择“无代理”模式?
在安全扫描领域,主要有两种模式:有代理(Agent)和无代理(Agent-less)。有代理模式需要在每台目标服务器上安装一个常驻进程,负责信息收集和上报。它的优点是能实现更细粒度的实时监控。但缺点也很明显:增加部署和维护成本,可能引入新的攻击面,并且在一些严格管控或资源受限的环境下难以推行。
Vuls坚定地选择了无代理模式,通过SSH协议连接到目标服务器执行命令来收集信息。这种选择背后有深刻的考量:
- 部署轻量:你只需要在一台中心服务器(我们称之为Vuls服务器)上安装Vuls,然后配置好到目标服务器的SSH密钥认证即可。无需在成百上千台生产服务器上安装任何额外软件,极大降低了部署复杂度和安全风险。
- 资源零占用:扫描时,Vuls通过SSH远程执行命令(如
rpm -qa,dpkg -l,uname -a),收集完信息后即断开连接。在非扫描时段,目标服务器上没有任何Vuls相关的进程或文件,实现了对生产环境的最小侵入。 - 兼容性极佳:只要目标服务器支持SSH和基本的包管理命令,就能被扫描。这使得它能够轻松覆盖从物理机、虚拟机到各种云主机的复杂环境。
注意:无代理模式并非万能。它依赖于网络连通性和SSH服务的稳定性。如果目标服务器SSH服务宕机或网络隔离,扫描就会失败。因此,Vuls更适合用于对已知、可控的服务器资产进行定期(如每日)扫描,而非对未知网络进行渗透测试。
2.2 漏洞数据源的融合与优先级
一个漏洞扫描器的准确性,根本上取决于其漏洞数据源的质量和覆盖度。Vuls在这方面做得相当扎实,它不是一个简单的CVE编号匹配器,而是融合了多源数据,形成了立体的漏洞判定体系。
2.2.1 官方安全公告与OVAL定义这是最权威的数据源。Vuls会直接拉取各大发行版官方的安全元数据:
- Red Hat系列 (RHEL, CentOS, AlmaLinux, Rocky Linux, Fedora, Oracle Linux):使用RHSA/ELSA公告和Red Hat提供的OVAL(Open Vulnerability and Assessment Language)定义文件。OVAL是一种标准化的语言,能精确描述“某个漏洞是否影响某个特定版本的软件包”。Vuls解析这些文件,能得出非常精确的判定。
- Debian/Ubuntu:使用Debian安全追踪器和Ubuntu CVE追踪器,同样结合其OVAL定义。
- SUSE/openSUSE:使用其官方OVAL仓库。
- Alpine:使用其安全数据库(secdb)。
这些数据源保证了漏洞信息的准确性和相关性。例如,一个上游的CVE漏洞,可能在下游发行版中因为补丁回溯、代码差异等原因并不受影响。直接使用发行版官方的判定,可以避免大量误报。
2.2.2 第三方漏洞与威胁情报除了官方数据,Vuls还集成了丰富的第三方数据源,用于评估漏洞的可利用性和威胁程度:
- Exploit DB / PoC in GitHub:关联漏洞是否有公开的利用代码。这是风险评估的关键一环。一个有公开PoC的高危漏洞,其修复紧迫性远高于没有PoC的同类漏洞。
- Metasploit Modules:关联漏洞是否已被集成到Metasploit框架中。这通常意味着利用已经非常成熟和自动化。
- CISA KEV(已知被利用漏洞目录):这是来自美国网络安全和基础设施安全局的权威清单,列出了在野被积极利用的漏洞。出现在这个列表上的漏洞,必须最高优先级处理。
- JPCERT/US-CERT警报:获取地区性的紧急安全通告。
通过融合这些数据,Vuls生成的报告不仅能告诉你“有漏洞”,还能告诉你“漏洞有多危险”、“有没有可能被轻易利用”,为你的修复优先级排序提供了至关重要的依据。
2.3 多种扫描模式适应不同场景
Vuls提供了几种扫描模式,以适应不同的安全要求和环境限制。
2.3.1 快速扫描 vs. 快速根扫描
- 快速扫描(Fast Scan):这是默认推荐模式。Vuls服务器使用一个普通用户权限(无需root)通过SSH连接目标,执行一些只读命令(如检查已安装的软件包列表、系统版本)。几乎所有漏洞判定都在Vuls服务器端完成,对目标服务器负载几乎为零。它支持离线扫描,适合绝大多数合规性和周期性检查场景。
- 快速根扫描(Fast Root Scan):使用root权限进行扫描。除了完成快速扫描的所有工作外,它还能执行一些需要特权的深度检查:
- 在RHEL系服务器上,使用
yum-ps(或dnf-ps)命令检查哪些系统进程正在使用已升级但未重启的库文件。这能有效发现“打了补丁但未重启服务”的安全死角。 - 在Debian/Ubuntu上,使用
checkrestart(来自debian-goodies包)来识别需要重启的进程。 - 检查未应用的内核更新等。
- 在RHEL系服务器上,使用
2.3.2 远程、本地与服务器模式
- 远程扫描模式:最常用的模式。Vuls服务器主动通过SSH连接所有配置好的目标服务器进行扫描。架构简单明了。
- 本地扫描模式:在目标服务器本地运行Vuls扫描器,扫描结果保存在本地。适用于网络策略严格,不允许中心服务器反向SSH连接的环境。你需要手动将各服务器的扫描结果收集到中心点进行汇总分析。
- 服务器模式:这是一种“反向推送”模式。Vuls服务器启动为一个HTTP服务。在目标服务器上,你只需要运行一个简单的命令(如
vuls scan -server http://vuls-server:5515)来收集本地信息,并将其通过HTTP POST发送给Vuls服务器。Vuls服务器处理并返回JSON格式的扫描结果。这种模式完全不需要在目标服务器上安装Vuls,也无需配置SSH密钥,只需要目标服务器能通过命令行收集基础信息并能访问Vuls服务器的HTTP端口即可,在某些受限环境中非常灵活。
3. 实战部署:从零搭建企业级Vuls扫描体系
纸上谈兵终觉浅,我们来实际部署一套。假设我们有一个中心管理服务器(Vuls Server, IP: 192.168.1.100)和两台需要扫描的目标服务器:一台CentOS 7(IP: 192.168.1.101),一台Ubuntu 20.04(IP: 192.168.1.102)。
3.1 Vuls服务器环境准备
Vuls服务器建议选择一台内存不少于2GB的Linux机器。这里我们以CentOS 8 Stream为例。
3.1.1 安装依赖Vuls是Go二进制程序,但需要SQLite3作为本地数据库,以及一些工具来获取漏洞数据。
# 安装基础编译环境和SQLite sudo dnf install -y git gcc make sqlite # 安装Go语言环境(以Go 1.21为例) wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc source ~/.bashrc # 验证安装 go version3.1.2 安装Vuls本体使用Go的模块安装方式是最简单的。
# 对于Go 1.16及以上版本 go install github.com/future-architect/vuls@latest # 安装完成后,二进制文件会在 $GOPATH/bin 下,通常为 ~/go/bin/vuls # 将其移动到系统路径或添加到PATH sudo cp ~/go/bin/vuls /usr/local/bin/3.1.3 初始化配置与数据Vuls需要一个配置目录来存放数据库、缓存和配置文件。
mkdir -p ~/vuls cd ~/vuls vuls init执行init命令后,会在当前目录生成config.toml样例文件,并创建sqlite3数据库文件。
3.2 目标服务器SSH无密码登录配置
这是远程扫描模式的关键。我们需要在Vuls服务器上生成SSH密钥对,并将公钥部署到所有目标服务器。
在Vuls服务器上操作:
# 如果已有密钥对(如id_rsa),可直接使用。否则生成新的。 ssh-keygen -t rsa -b 4096 -C "vuls-scanner" -f ~/.ssh/vuls_id_rsa # 一路回车,不设密码(用于自动化) # 将公钥内容复制到剪贴板 cat ~/.ssh/vuls_id_rsa.pub在每台目标服务器上操作(以root用户或具有sudo权限的用户为例):
- 登录目标服务器。
- 将上面复制的公钥内容,追加到对应用户的
~/.ssh/authorized_keys文件中。 - 重要:确保
~/.ssh目录权限为700,authorized_keys文件权限为600。chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys - 在目标服务器上,确保用于连接的用户有权限执行
sudo命令来运行rpm、dpkg等包管理命令,且最好配置为无需密码。例如,在/etc/sudoers.d/vuls文件中添加(假设用户名为scanuser):scanuser ALL=(ALL) NOPASSWD: /usr/bin/rpm, /usr/bin/dnf, /usr/bin/yum, /usr/bin/apt-get, /usr/bin/apt, /bin/journalctl实操心得:在生产环境中,为了安全,不建议直接使用root密钥。最佳实践是创建一个专用的、权限受限的扫描用户(如
vuls-scan),并精细配置其sudo权限,仅允许执行必要的只读命令。同时,在Vuls服务器的SSH配置(~/.ssh/config)中指定使用这个密钥和用户,实现更清晰的管理。
3.3 编写Vuls配置文件
回到Vuls服务器,编辑~/vuls/config.toml。这是Vuls的核心。
[default] # Vuls服务器自身的时区 host = "localhost" port = "local" # 扫描结果输出目录 resultsDir = "/home/youruser/vuls/results" # 是否在扫描后通过邮件/Slack通知 # toSlack = false # toEmail = false # 定义第一个服务器组,名为 `servers` [servers] # CentOS 7 目标服务器 [servers.centos-web-01] host = "192.168.1.101" # 目标服务器IP port = "22" # SSH端口 user = "scanuser" # SSH用户名 keyPath = "/home/youruser/.ssh/vuls_id_rsa" # 私钥路径 # 扫描模式:fast(快速)或 fast-root(快速根扫描) scanMode = ["fast"] # 发行版类型,vuls会自动检测,也可手动指定 # type = "redhat" # Ubuntu 20.04 目标服务器 [servers.ubuntu-db-01] host = "192.168.1.102" port = "22" user = "scanuser" keyPath = "/home/youruser/.ssh/vuls_id_rsa" scanMode = ["fast"] # type = "ubuntu"3.4 执行第一次扫描与报告查看
3.4.1 测试连接与配置在扫描前,强烈建议使用configtest命令验证配置和连接。
cd ~/vuls vuls configtest如果一切正常,你会看到类似[INFO] Tested 2 servers的成功信息。如果失败,请根据错误信息检查SSH配置、网络、sudo权限等。
3.4.2 执行扫描
vuls scan -config ./config.toml扫描过程会显示进度。首次扫描会花费较长时间(几分钟到十几分钟),因为Vuls需要下载完整的漏洞数据库(如NVD)并建立本地缓存。后续扫描会快很多,因为它只增量更新变化的部分。
3.4.3 查看扫描报告扫描完成后,结果会以JSON格式保存在resultsDir指定的目录下,同时会在终端以文本形式输出摘要。但更推荐使用Vuls自带的TUI(文本用户界面)查看器或Web UI。
使用TUI查看器:
vuls tui -results-dir /home/youruser/vuls/results这会打开一个交互式界面,你可以用方向键选择服务器和CVE,查看详细信息,包括漏洞描述、严重程度、是否有PoC等。
使用Web UI(VulsRepo): TUI虽然方便,但对于团队协作和长期追踪并不友好。社区有一个优秀的第三方Web UI项目叫VulsRepo。部署它需要额外的步骤(需要Go和Node.js环境),但能提供可视化的仪表盘、图表和更友好的筛选功能,非常适合作为团队的安全门户。部署命令大致如下:
# 安装VulsRepo go install github.com/ishiDACo/vulsrepo@latest # 启动VulsRepo服务器,指向你的扫描结果目录 vulsrepo -results-dir /home/youruser/vuls/results -listen 0.0.0.0:5111然后在浏览器访问
http://your-vuls-server-ip:5111即可。
4. 高级应用场景与集成实践
基础扫描只是开始,Vuls真正的威力在于其灵活性和可集成性。
4.1 扫描非操作系统软件包
现代应用堆栈中,漏洞往往来自应用层依赖,如Python的pip包、Node.js的npm包、Java的JAR包。Vuls通过几种机制来覆盖这些“盲区”。
4.1.1 基于锁文件的扫描这是最常用的方式。Vuls可以解析各种编程语言的依赖锁文件,识别其中包含的库及其版本,然后与漏洞数据库进行匹配。
- 支持的语言/工具:Bundler (Ruby), Composer (PHP), Pipenv (Python), Poetry (Python), npm (Node.js), yarn (Node.js), Cargo (Rust), etc.
- 配置方法:在
config.toml中,为目标服务器添加[servers.xxx.optional]部分,指定锁文件的路径。
Vuls会通过SSH读取这些文件,进行分析。[servers.my-app-server.optional] # 扫描Python项目的依赖 lockfiles = [ "/path/to/your/project/Pipfile.lock", "/path/to/another/project/poetry.lock" ] # 扫描Node.js项目的依赖 lockfiles = [ "/path/to/node/project/package-lock.json", "/path/to/node/project/yarn.lock" ]
4.1.2 GitHub安全警报集成如果你的代码托管在GitHub上,这是一个非常强大的自动化功能。Vuls可以定期调用GitHub API,获取你仓库中依赖图(Dependency Graph)触发的安全警报(Security Alerts),并将这些警报导入到自己的报告中。
- 优势:无需在服务器上定位锁文件,直接与源码仓库的安全状态同步。
- 配置:需要在GitHub上生成Personal Access Token,并在Vuls配置中设置。
[servers.my-gh-project] type = "github" # ... 其他配置 [servers.my-gh-project.github] token = "your_github_personal_access_token" owner = "your_organization_or_username" repository = "your_repo_name"
4.1.3 CPE匹配扫描对于既没有锁文件,也不在GitHub上的自定义编译软件或商业软件,可以使用CPE(通用平台枚举)进行匹配。你需要手动或通过其他工具(如OWASP Dependency-Check)为你的软件生成CPE标识符,然后在Vuls配置中指定。这种方式精度相对较低,但可以作为补充手段。
4.2 与CI/CD管道集成
将安全左移,在构建阶段就发现漏洞,是DevSecOps的核心。Vuls可以轻松集成到Jenkins、GitLab CI、GitHub Actions等CI/CD工具中。
基本思路:
- 在构建代理(Runner)中安装Vuls。
- 构建镜像或编译应用后,在镜像内或针对构建产物(如锁文件)运行Vuls扫描。
- 根据Vuls的扫描结果(例如,是否存在CRITICAL或HIGH级别的漏洞)来决定是否阻断本次构建/部署。
示例:GitHub Actions集成片段
- name: Install Vuls run: | go install github.com/future-architect/vuls@latest sudo cp ~/go/bin/vuls /usr/local/bin/ - name: Run Vuls Scan on Dependencies run: | cd ${{ github.workspace }} # 生成一个极简的config.toml,扫描本地锁文件 cat << EOF > config.toml [default] resultsDir = "./vuls-results" [servers.local] host = "localhost" port = "local" scanMode = ["fast"] [servers.local.optional] lockfiles = ["./Pipfile.lock", "./package-lock.json"] EOF vuls scan -config ./config.toml -format-json # 解析JSON结果,如果发现高危漏洞则失败 # 这里可以使用jq等工具进行判断 if jq -e '.scanned..cves[] | select(.severity == "HIGH" or .severity == "CRITICAL")' ./vuls-results/current/*.json > /dev/null; then echo "发现高危漏洞,构建失败!" exit 1 fi这样,每次代码提交都会触发依赖安全检查,从源头控制风险。
4.3 定时扫描与通知
漏洞信息是动态更新的,因此定期扫描至关重要。最经典的方式是使用Cron定时任务。
4.3.1 创建扫描脚本在Vuls服务器上创建脚本~/vuls/scan.sh:
#!/bin/bash cd /home/youruser/vuls # 更新漏洞数据库 vuls fetch -config ./config.toml # 执行扫描 vuls scan -config ./config.toml # 发送通知到Slack(需提前配置) vuls report -config ./config.toml -to-slack4.3.2 配置Cron Job
# 编辑当前用户的crontab crontab -e # 添加一行,例如每天凌晨2点执行扫描 0 2 * * * /bin/bash /home/youruser/vuls/scan.sh > /home/youruser/vuls/scan.log 2>&14.3.3 配置Slack通知在config.toml中配置Slack Webhook,可以让团队在漏洞发现时第一时间收到警报。
[default] # ... 其他配置 toSlack = true [slack] hookURL = "https://hooks.slack.com/services/XXXXX/XXXXX/XXXXXXXX" channel = "#security-alerts" # 可以设置仅通知特定严重级别以上的漏洞 # notifyOnlyCveSeverityOver = "high" iconEmoji = ":warning:" authUser = "Vuls Bot"这样,每日扫描报告或紧急的高危漏洞都会推送到指定的Slack频道。
5. 避坑指南与常见问题排查
在实际使用中,你肯定会遇到各种问题。以下是我踩过的一些坑和解决方案。
5.1 扫描性能与资源优化
问题:首次扫描或定期vuls fetch更新数据库时速度慢,占用磁盘空间大。
- 原因:Vuls需要下载完整的NVD等漏洞数据库的JSON文件,数据量庞大(几个GB),且默认的SQLite数据库会随之增长。
- 解决方案:
- 使用代理或镜像源:在
config.toml的[default]部分配置HTTP代理,或者如果在内网,可以搭建一个本地镜像源,让Vuls从内网更新。[default] httpProxy = "http://your-proxy:8080" - 定期清理旧报告:
resultsDir下的JSON报告文件会累积。可以写一个清理脚本,只保留最近30天的报告。 - 调整数据库保留策略:Vuls使用go-cve-dictionary库,其数据默认存储在
~/.cache/go-cve-dictionary。可以研究其配置,但一般不建议轻易清理,除非磁盘空间告急。
- 使用代理或镜像源:在
5.2 SSH连接与权限问题
问题:vuls configtest或扫描时提示Permission denied (publickey)或sudo: a terminal is required to read the password。
- 排查步骤:
- 手动SSH测试:在Vuls服务器上,使用
ssh -i /path/to/private/key user@target-host命令手动连接,看是否成功。 - 检查公钥:确认目标服务器上对应用户的
~/.ssh/authorized_keys文件内容正确,且权限为600。 - 检查SSH服务配置:检查目标服务器的
/etc/ssh/sshd_config,确保PubkeyAuthentication yes和PasswordAuthentication no(如果只用密钥)设置正确。修改后需重启sshd服务。 - 检查sudo配置:这是最常见的问题。确保为扫描用户配置的sudo规则是
NOPASSWD,并且命令路径完全正确。可以通过sudo -l -U scanuser命令在目标服务器上验证。另外,某些发行版(如Ubuntu)默认会要求TTY,需要在sudoers规则中添加!requiretty或修改/etc/sudoers中的Defaults行。更安全的方式是在规则中明确指定命令的全路径。
- 手动SSH测试:在Vuls服务器上,使用
5.3 漏洞数据更新失败
问题:vuls fetch失败,提示网络错误或JSON解析错误。
- 原因:NVD等官方数据源偶尔会不可用,或者其JSON格式发生微小变化导致客户端解析失败。
- 解决方案:
- 重试:网络问题可以等待或重试。
- 查看日志:Vuls的日志会给出更详细的错误信息。可以尝试删除本地缓存(
~/.cache/go-cve-dictionary和~/.cache/goval-dictionary),然后重新fetch。注意:这会重新下载全部数据,耗时较长。 - 关注版本:Vuls及其依赖的字典库在持续更新以适配数据源变化。确保你使用的是较新的版本。
5.4 扫描结果误报/漏报
问题:报告里出现了不影响当前系统的漏洞(误报),或者感觉应该有的漏洞没报出来(漏报)。
- 误报处理:
- 检查发行版:首先确认Vuls是否正确识别了目标服务器的操作系统和版本(
type字段)。有时自动检测会出错,可以在配置中手动指定type = "centos"等。 - 理解漏洞状态:Vuls会标记漏洞状态,如
Fixed(已修复)、Not Fixed Yet(未修复)、Will Not Fix(不修复)。已修复的包对应的CVE可能仍会显示,但会标记为已修复状态,这不算误报,而是信息展示。 - 使用忽略列表:对于确认的误报,可以在
config.toml中使用ignoreCves或ignorePkgs配置项将其加入忽略列表。[servers.centos-web-01] # ... 其他配置 ignoreCves = ["CVE-2014-0160"] # 忽略特定CVE ignorePkgs = ["some-package-name"] # 忽略特定包的所有CVE
- 检查发行版:首先确认Vuls是否正确识别了目标服务器的操作系统和版本(
- 漏报排查:
- 更新数据库:确保漏洞数据库是最新的,运行
vuls fetch。 - 检查扫描模式:
fast模式可能无法检测到某些需要root权限才能发现的问题(如未重启的服务)。尝试使用fast-root模式(需配置好root或sudo权限)。 - 检查软件源:确保目标服务器的软件源配置正确,能够获取到最新的安全元数据(如对于CentOS,
yum check-update --security是否能列出更新)。 - 手动验证:根据CVE编号,去目标发行版的安全公告网站手动查询,确认该漏洞是否真的影响你当前使用的软件包版本。
- 更新数据库:确保漏洞数据库是最新的,运行
5.5 与现有监控/运维体系的整合
Vuls生成的JSON报告结构清晰,非常适合被其他系统消费。你可以编写脚本,解析results目录下的JSON文件,将漏洞数据导入到你的CMDB(配置管理数据库)、SIEM(安全信息和事件管理)系统或运维监控大屏中,实现安全状态的可视化集中管理。例如,使用Python的json库或jq命令行工具,可以轻松提取特定服务器、特定严重级别的漏洞数量,作为监控指标。
我个人在将Vuls集成到现有Prometheus+Grafana监控栈时,就写了一个小的导出器(exporter),定期解析Vuls的JSON报告,将“各服务器高危漏洞数量”作为一个Gauge指标暴露给Prometheus,最终在Grafana上形成了一个实时变动的安全态势面板。这个实践让我能一眼看清整个集群的安全水位,效果非常好。
