logo
0
0
WeChat Login
feat: 支持分层清理与缺失报告重建

Offscan - 离线容器镜像扫描平台

Go Version Trivy License

企业级离线容器镜像安全扫描解决方案

功能特性 · 快速开始 · 配置说明 · API文档


项目简介

Offscan 是一个面向内网隔离环境的离线容器镜像安全扫描平台。基于 Trivy 安全扫描引擎构建,提供完整的漏洞检测、配置审计、敏感信息扫描和许可证合规检查能力,专为无法访问公网的企业内部环境设计。

核心价值

  • 🔒 离线优先设计:镜像内置漏洞数据库,无需公网连接即可完成安全扫描
  • 🎯 一站式服务:Web UI + REST API + 后台任务队列,开箱即用
  • 📊 多维度扫描:漏洞、配置错误、敏感信息、许可证四大扫描维度
  • 📋 合规输出:自动生成 SPDX-2.2 格式 SBOM,满足供应链安全合规要求
  • 🔔 即时通知:支持企业微信 Webhook,扫描完成即时推送
  • 🧹 自动清理:内置定时清理器,避免报告文件无限堆积

功能特性

扫描能力

类型说明
漏洞扫描检测操作系统包和应用依赖的已知安全漏洞
配置审计检测 Dockerfile、Kubernetes 配置等的安全问题
敏感信息识别镜像中泄露的 API Key、密码等敏感数据
许可证合规检测软件许可证,规避法律风险

多源支持

  • Registry 模式:直接扫描镜像仓库中的镜像
  • HTTP Download:通过 HTTP/HTTPS 下载镜像包后扫描
  • Filesystem:扫描本地文件系统目录
  • Filesystem:支持多路径合并扫描与自定义过滤目录
  • Repository:扫描 Git 仓库代码
  • SBOM:扫描 SPDX 格式的 SBOM 文件

报告产出

每次扫描生成以下工件:

文件说明
report.html可视化 HTML 报告,便于人工审阅
scan.jsonTrivy 原始 JSON 结果,便于程序消费
native.spdx.jsonTrivy 原生 SPDX 输出
sbom.spdx.json规范化 SPDX-2.2 格式 SBOM
summary.json漏洞统计摘要

插件系统

内置插件通过配置文件激活,支持任务级细粒度控制:

插件说明默认启用
count漏洞数量统计汇总
html-report详细 HTML 报告生成
spdx22-normalizerSPDX 规范化处理
referrerSBOM 附着到 OCI Registry

技术架构

┌─────────────────────────────────────────────────────────────┐ │ Docker Container │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Web UI │ │ REST API │ │ Worker │ │ Scheduler│ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │ │ │ │ └─────────────┴──────┬──────┴──────────────┘ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ Offscan Core │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │ Job │ │ Storage │ │ Trivy │ │ Notify │ │ │ │ │ │ Service │ │ (SQLite)│ │ Executor│ │ Client │ │ │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ │ ┌────────────────────┼────────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌─────────┐ ┌──────────┐ ┌─────────┐ │ │ │ /data │ │ Trivy │ │/workspace│ │ │ │ (持久化) │ │ + Plugins│ │/reports │ │ │ └─────────┘ └──────────┘ └─────────┘ │ └─────────────────────────────────────────────────────────────┘

技术栈

组件技术选型
编程语言Go 1.26+
扫描引擎Trivy v0.69.3
Web 框架Gin
数据存储SQLite (modernc.org/sqlite, 纯 Go 实现)
任务调度darkit/cron
日志系统darkit/slog
前端界面原生 HTML/CSS/JavaScript

快速开始

环境要求

  • Docker 20.10+ 或 Podman 3.0+
  • 至少 2GB 可用内存
  • 约 500MB 磁盘空间(用于内置漏洞数据库)

基础运行

# 创建数据目录 mkdir -p /workspace/data /workspace/reports # 启动服务 docker run -d --name offscan \ -p 8080:8080 \ -v /workspace/data:/data \ -v /workspace/reports:/workspace/reports \ ghcr.io/chipeak/offscan:latest # 访问 Web UI open http://localhost:8080

使用外部数据库

如果需要更新漏洞数据库,可挂载外部数据库:

docker run -d --name offscan \ -p 8080:8080 \ -v /workspace/data:/data \ -v /workspace/reports:/workspace/reports \ -v /path/to/trivy-db:/data/trivy-cache/db \ ghcr.io/chipeak/offscan:latest

数据库优先级

系统按以下优先级加载漏洞数据库:

  1. /data/trivy-cache - 外部数据库(最高优先级)
  2. /data/.offscan/internal-trivy-seed - 持久化内置数据库
  3. /opt/trivy-seed - 镜像内置种子数据库(最低优先级)

配置说明

配置文件结构

配置文件位于 /data/config/ 目录:

/data/config/ ├── app.yaml # 主配置文件 ├── plugins.yaml # 插件配置 └── registries.yaml # 镜像仓库凭证

主配置示例

# app.yaml app: name: "offscan" organization: "Organization: Your Company" creatorPerson: "Person: Security Team" server: addr: ":8080" publicBaseURL: "http://your-domain:8080" readTimeout: "30s" writeTimeout: "30s" trivy: timeout: "20m" severity: - "CRITICAL" - "HIGH" - "MEDIUM" - "LOW" scanners: - "vuln" - "misconfig" - "secret" - "license" offlineScan: true cleanup: enabled: true schedule: "0 0 0 * * *" # 每天 00:00 执行 reportRetentionDays: 30 evidenceRetentionDays: 90 imageCacheRetentionDays: 1 notifications: profiles: wecom-default: webhook: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR-KEY" webhookEnv: "OFFSCAN_WECOM_WEBHOOK" # 若设置该环境变量,将优先覆盖 webhook

通知配置支持两种方式:

  • 直接在 app.yaml 中配置 notifications.profiles.<name>.webhook
  • 通过 notifications.profiles.<name>.webhookEnv 指定环境变量名

生效优先级如下:

  1. webhookEnv 指向的环境变量
  2. OFFSCAN_NOTIFICATIONS_PROFILES_<PROFILE>_WEBHOOK
  3. OFFSCAN_<PROFILE>_WEBHOOK
  4. 配置文件中的 webhook

镜像仓库凭证

# registries.yaml profiles: harbor-prod: usernameEnv: "HARBOR_USERNAME" passwordEnv: "HARBOR_PASSWORD" insecureSkipTLSVerify: false docker-hub: username: "your-username" password: "your-password"

环境变量

变量名说明
OFFSCAN_CONFIG_DIR配置文件目录
OFFSCAN_LOG_LEVEL日志级别 (debug/info/warn/error)
OFFSCAN_LOG_FORMAT日志格式 (text/json)
OFFSCAN_WECOM_WEBHOOK企业微信 Webhook 地址;若设置则优先覆盖配置文件

API 文档

Base URL

  • 开发环境:http://localhost:8080
  • 生产环境:由 server.publicBaseURL 配置

核心接口

创建扫描任务

POST /api/v1/jobs Content-Type: application/json { "sourceType": "registry", "imageRef": "nginx:latest", "registryProfile": "harbor-prod", "pluginProfile": "default", "notificationProfile": "wecom-default" }

响应202 Accepted

{ "jobId": "zscan-20240101120000-abc123", "status": "queued", "targetRef": "nginx:latest" }

查询任务状态

GET /api/v1/jobs/{jobId}

响应

{ "jobId": "zscan-20240101120000-abc123", "status": "completed", "targetRef": "nginx:latest", "dbSource": "external", "summary": { "critical": 2, "high": 5, "medium": 10, "low": 3 }, "artifacts": [ { "name": "report.html", "kind": "html", "downloadUrl": "/api/v1/jobs/zscan-xxx/artifacts/download/report.html", "sha256": "abc123..." } ] }

任务状态流转

queued → pulling → scanning → rendering → notifying → completed ↘ failed

获取系统状态

GET /api/v1/system/status

获取配置选项

GET /api/v1/system/options

完整 API 文档请参考 docs/api/README.md


使用场景

1. CI/CD 集成

在 CI 流水线中集成安全扫描:

# GitLab CI 示例 container-scan: stage: security script: - | RESPONSE=$(curl -s -X POST http://offscan:8080/api/v1/jobs \ -H "Content-Type: application/json" \ -d '{"sourceType":"registry","imageRef":"'$CI_REGISTRY_IMAGE':'$CI_COMMIT_SHA'"}') JOB_ID=$(echo $RESPONSE | jq -r .jobId) - | while true; do STATUS=$(curl -s http://offscan:8080/api/v1/jobs/$JOB_ID | jq -r .status) if [ "$STATUS" = "completed" ]; then exit 0; fi if [ "$STATUS" = "failed" ]; then exit 1; fi sleep 5 done

2. 批量镜像扫描

#!/bin/bash IMAGES=("nginx:latest" "redis:7" "postgres:15") for img in "${IMAGES[@]}"; do curl -X POST http://offscan:8080/api/v1/jobs \ -H "Content-Type: application/json" \ -d "{\"sourceType\":\"registry\",\"imageRef\":\"$img\"}" done

3. 离线镜像包扫描

# 通过 HTTP 下载镜像包 curl -X POST http://offscan:8080/api/v1/jobs \ -H "Content-Type: application/json" \ -d '{ "sourceType": "http-download", "targetRef": "https://internal-artifacts.local/images/app-v1.tar.gz" }'

开发指南

本地构建

# 克隆仓库 git clone https://github.com/chipeak/offscan.git cd offscan # 安装依赖 go mod download # 生成 SQLC 代码 go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.30.0 sqlc generate # 构建 go build -o bin/offscan ./cmd/offscan # 运行测试 go test ./...

构建 Docker 镜像

docker build -t offscan:local .

构建多架构镜像

当前 Dockerfile 已兼容 linux/amd64linux/arm64,推荐使用 docker buildx

# 单架构本地验证 docker buildx build --platform linux/amd64 -t offscan:amd64-local --load . docker buildx build --platform linux/arm64 -t offscan:arm64-local --load . # 双架构推送 docker buildx build \ --platform linux/amd64,linux/arm64 \ -t ghcr.io/chipeak/offscan:latest \ --push .

说明:

  • builder 阶段固定运行在 BUILDPLATFORM,通过 GOOS/GOARCH 交叉编译目标二进制。
  • trivy-assets 阶段按 TARGETARCH 自动选择官方 Trivy 发行包:
    • amd64 -> Linux-64bit
    • arm64 -> Linux-ARM64
  • 插件二进制会随目标平台一起编译并封装,因此无需为 arm64 额外维护第二套插件包。

项目结构

offscan/ ├── cmd/ # 程序入口 │ ├── offscan/ # 主服务 │ ├── trivy-offscan-count/ # Count 插件 │ └── trivy-offscan-html/ # HTML 报告插件 ├── internal/ # 内部模块 │ ├── app/ # 应用初始化 │ ├── config/ # 配置管理 │ ├── job/ # 任务服务 │ ├── model/ # 数据模型 │ ├── notify/ # 通知模块 │ ├── report/ # 报告生成 │ ├── sbom/ # SBOM 处理 │ ├── storage/ # 数据存储 │ ├── system/ # 系统服务 │ ├── trivy/ # Trivy 执行器 │ └── web/ # Web 服务 ├── web/ # 前端资源 │ ├── static/ │ └── templates/ ├── docs/ # 文档 ├── examples/ # 配置示例 └── scripts/ # 脚本

运维指南

数据库更新

# 导出数据库到指定目录 docker exec offscan offscan-export-db /data/trivy-cache # 或使用内置命令 docker exec offscan offscan-export-db --help

健康检查

# 检查系统状态 curl http://localhost:8080/api/v1/system/status # 响应示例 { "ready": true, "dbSource": "external", "dbLoadedAt": "2024-01-01T08:00:00+08:00", "plugins": [...] }

日志查看

# 实时查看日志 docker logs -f offscan # JSON 格式日志 docker run -e OFFSCAN_LOG_FORMAT=json ...

清理策略

系统默认每天 00:00 按分层策略自动清理工件:镜像缓存保留 1 天,可视化报告保留 30 天,原始证据保留 90 天。可通过配置调整:

cleanup: enabled: true schedule: "0 0 0 * * *" reportRetentionDays: 30 evidenceRetentionDays: 90 imageCacheRetentionDays: 1 dryRun: false # 设为 true 可模拟清理

常见问题

Q: 扫描任务一直处于 queued 状态?

A: 检查后台 Worker 是否正常运行,查看日志是否有错误信息。

Q: 如何更新漏洞数据库?

A: 使用 offscan-export-db 命令导出最新数据库到 /data/trivy-cache,或从外部下载 Trivy 数据库文件放入该目录。

Q: HTML 报告生成失败?

A: 检查 plugins.yamlhtml-report 插件是否正确配置,查看日志中的具体错误信息。

Q: 企业微信通知发送失败?

A: 先确认 notificationProfile 选择了正确的 profile,再检查 OFFSCAN_WECOM_WEBHOOK 是否已正确设置;如果未使用环境变量,则检查 notifications.profiles.<name>.webhook 是否配置有效。


贡献指南

我们欢迎所有形式的贡献!

贡献方式

  1. 报告问题:提交 Issue 描述问题或建议
  2. 提交代码:Fork → Branch → Commit → Pull Request
  3. 完善文档:改进文档内容或翻译

代码规范

  • 遵循 Go 官方代码规范
  • 使用 gofmt 格式化代码
  • 提交前运行 go test ./...
  • Commit 信息遵循 Conventional Commits

开发流程

# 创建功能分支 git checkout -b feature/your-feature # 提交更改 git commit -m "feat: add new feature" # 推送分支 git push origin feature/your-feature # 创建 Pull Request

安全披露

如果您发现安全漏洞,请通过 security@example.local 私密报告,不要公开提交 Issue。


许可证

本项目采用 Apache-2.0 许可证。


致谢

  • Trivy - 强大的安全扫描引擎
  • Gin - 高性能 Web 框架
  • 所有贡献者和用户

⬆ 回到顶部

Made with ❤️ by Chipeak Security Team