Offscan 是一个面向内网隔离环境的离线容器镜像安全扫描平台。基于 Trivy 安全扫描引擎构建,提供完整的漏洞检测、配置审计、敏感信息扫描和许可证合规检查能力,专为无法访问公网的企业内部环境设计。
| 类型 | 说明 |
|---|---|
| 漏洞扫描 | 检测操作系统包和应用依赖的已知安全漏洞 |
| 配置审计 | 检测 Dockerfile、Kubernetes 配置等的安全问题 |
| 敏感信息 | 识别镜像中泄露的 API Key、密码等敏感数据 |
| 许可证合规 | 检测软件许可证,规避法律风险 |
每次扫描生成以下工件:
| 文件 | 说明 |
|---|---|
report.html | 可视化 HTML 报告,便于人工审阅 |
scan.json | Trivy 原始 JSON 结果,便于程序消费 |
native.spdx.json | Trivy 原生 SPDX 输出 |
sbom.spdx.json | 规范化 SPDX-2.2 格式 SBOM |
summary.json | 漏洞统计摘要 |
内置插件通过配置文件激活,支持任务级细粒度控制:
| 插件 | 说明 | 默认启用 |
|---|---|---|
count | 漏洞数量统计汇总 | ✅ |
html-report | 详细 HTML 报告生成 | ✅ |
spdx22-normalizer | SPDX 规范化处理 | ✅ |
referrer | SBOM 附着到 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 |
# 创建数据目录
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
系统按以下优先级加载漏洞数据库:
/data/trivy-cache - 外部数据库(最高优先级)/data/.offscan/internal-trivy-seed - 持久化内置数据库/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>.webhooknotifications.profiles.<name>.webhookEnv 指定环境变量名生效优先级如下:
webhookEnv 指向的环境变量OFFSCAN_NOTIFICATIONS_PROFILES_<PROFILE>_WEBHOOKOFFSCAN_<PROFILE>_WEBHOOKwebhook# 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 地址;若设置则优先覆盖配置文件 |
http://localhost:8080server.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。
在 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
#!/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
# 通过 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 build -t offscan:local .
当前 Dockerfile 已兼容 linux/amd64 与 linux/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-64bitarm64 -> Linux-ARM64arm64 额外维护第二套插件包。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 可模拟清理
A: 检查后台 Worker 是否正常运行,查看日志是否有错误信息。
A: 使用 offscan-export-db 命令导出最新数据库到 /data/trivy-cache,或从外部下载 Trivy 数据库文件放入该目录。
A: 检查 plugins.yaml 中 html-report 插件是否正确配置,查看日志中的具体错误信息。
A: 先确认 notificationProfile 选择了正确的 profile,再检查 OFFSCAN_WECOM_WEBHOOK 是否已正确设置;如果未使用环境变量,则检查 notifications.profiles.<name>.webhook 是否配置有效。
我们欢迎所有形式的贡献!
gofmt 格式化代码go test ./...# 创建功能分支
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 许可证。
Made with ❤️ by Chipeak Security Team