BinanIbrahim/Container-Security-Scanner
GitHub: BinanIbrahim/Container-Security-Scanner
一款零依赖的 Go CLI 工具,通过构建 SBOM 并查询 Alpine SecDB 来扫描 Alpine Docker 镜像中的已知 CVE 漏洞。
Stars: 1 | Forks: 0
# Sentinel Scanner
Sentinel Scanner 是一个使用 **Go** 编写的**零依赖** **CLI** 工具,
用于扫描基于 **Alpine** 的 **Docker 镜像**以查找已知的安全 **漏洞** —
这一切都在它们发布到生产环境之前完成。
容器镜像打包了数十个 **OS 包**,其中任何一个都可能携带
已公布的 **CVE**。手动跟踪它们是不切实际的:您必须了解
内置到镜像中的每个包和版本,将它们与不断变化的**漏洞数据源**
逐一交叉比对,并理解 **Alpine 的 APK 版本控制**规则,
才能判断已安装的版本是否确实早于修复特定 CVE 的版本。Sentinel
正是将这一流程自动化了。
将其指向一个镜像,它就会作为一个 **pipeline** 运行:它会拉取并保存
镜像,然后从镜像层中**流式传输** **Alpine 包数据库**
以构建轻量级的 **SBOM**(软件物料清单),检测 Alpine OS 版本,
下载匹配的 **Alpine 安全数据库 (SecDB)**,并使用 **支持 APK 的版本比较器**
将每个已安装的包与已知修复进行比较。结果是一份清晰的报告,
包含受影响包的 **CVE ID**、**严重性评分**以及具体的**修复**
指南 — 可以直接打印供人类阅读,或者以 **JSON** 格式输出,
并带有可配置的失败阈值,以便它可以**控制 CI pipeline**。它以单个
**静态二进制文件**的形式发布,并且**不引入任何第三方依赖**。
## 功能
- 通过命令行扫描 **Docker 镜像** (`--image`)。
- 用于 **CI pipeline** 的机器可读 **JSON 输出** (`--format json`)。
- **基于策略的构建失败**阈值 (`--fail-on`)。
- 从保存的 tar 中**流式传输镜像层**(无需完全解压到磁盘)以构建 **SBOM**。
- 从 `alpine-release` 检测 Alpine 版本(带有安全的镜像标签解析回退机制)。
- 从 `main` 和 `community` 仓库获取并合并 **Alpine SecDB**。
- **支持 APK 的版本比较**,包括预发布/后发布后缀语义(见下文)。
- 按包和 CVE **去重**扫描结果。
- 包级别的**严重性评分**(LOW 到 CRITICAL)以及每个包的**修复**提示。
- 打印**扫描上下文指标**以供置信度评估和调试。
## 架构
扫描器作为线性 **pipeline** 运行。每个阶段将其输出传递给
下一个阶段,整个过程由 `cmd/scanner/main.go` 统一编排。
```
--image alpine:3.14
│
▼
┌───────────────────┐
│ docker pull │ ensure the image exists locally
│ docker save │ export it to image.tar ── internal/extractor
└─────────┬─────────┘
│ saved image.tar (manifest.json + layer blobs)
▼
┌───────────────────┐
│ stream outer tar │ find manifest.json (any order)
│ walk each layer │ gzip/plain, in-memory, cap-guarded ── internal/analyzer
│ parse apk DB │ lib/apk/db/installed → SBOM
└─────────┬─────────┘
│ SBOM: []{name, version}
▼
┌───────────────────┐
│ detect version │ alpine-release → v3.14
│ fetch SecDB │ main.json + community.json ── internal/matcher
│ merge + index │ map[pkg] → secfixes
└─────────┬─────────┘
│ vulnerability database
▼
┌───────────────────┐
│ match versions │ installed < fixed ? (apk-aware)
│ score severity │ risk score → LOW…CRITICAL ── cmd/scanner
│ render report │ text or JSON, apply --fail-on
└─────────┬─────────┘
▼
report + exit code
```
单行 Pipeline:
```
docker pull → docker save → stream layers → SBOM → SecDB → match → report
```
### 项目结构
- `cmd/scanner/main.go` — **CLI 入口点**、匹配循环、评分和报告渲染。
- `internal/extractor/` — 拉取并保存 **Docker 镜像**,并提供受权限保护的流式 tar 遍历器。
- `internal/analyzer/` — 读取清单并流式传输镜像层,以从 Alpine 包数据库构建 **SBOM**。
- `internal/matcher/` — 获取/合并 **SecDB** 并提供**支持 APK 的版本比较**。
## 为什么需要自定义 APK 版本比较器?
判断一个已安装的包是否存在漏洞,归根结底是一个
问题:*已安装的版本是否早于修复该 CVE 的版本?*
这个比较是整个工具的**正确性核心** — 如果弄错了,
扫描器就会默默地产生**假阴性**(遗漏漏洞)或
**假阳性**。因此我们自己实现了它,位于
[`internal/matcher/version.go`](internal/matcher/version.go),而不是引入
依赖。原因有三:
1. **APK 版本不是 semver。** Alpine 版本包含预发布后缀
(`_alpha`, `_beta`, `_pre`, `_rc`)、后发布后缀 (`_cvs`, `_svn`,
`_git`, `_hg`, `_p`) 以及内部版本号 (`-rN`)。其顺序是
`1.2_pre1 < 1.2 < 1.2_p1`。通用的 **semver 库**
(`Masterminds/semver`, `hashicorp/go-version`, …) 实现了 semver 规范,
而该规范没有这些后缀的概念 — 它们会将 `1.2.2_pre2` 解析为
垃圾数据,或者将其排名*高于* `1.2.2`,
从而导致在那些极其关键的棘手情况下得出完全相反的结果。
2. **规范的实现是 C 语言。** 权威的排序逻辑存在于
apk-tools 的 **`apk_version.c`** 中。直接移植其**基于 token 的比较**
(≈200 行纯 Go 代码,几乎没有内存分配)比封装 **cgo**
或采用庞大的第三方移植要小巧得多,也更容易审计。
3. **掌握代码让我们能够对其进行穷尽测试,且供应链风险为零。**
比较器是**纯逻辑**,因此有**表驱动**测试和一条包含
**17 个元素的规范排序链**作为支撑,对两个方向的所有配对都进行了检查。
对于安全工具而言,保持 `go.mod` 不包含第三方依赖本身就是
一项功能(参见 [CONVENTIONS.md](CONVENTIONS.md)) — 这样就**没有传递依赖**
需要审计、固定版本或信任。
## 要求
- **Go `1.26.1`**(如 [go.mod](go.mod) 中所定义)
- **Docker CLI** + 正在运行的 **Docker daemon**
- 可访问 `secdb.alpinelinux.org` 的网络
## 快速开始
```
go run cmd/scanner/main.go --image alpine:3.14
```
CI 风格运行(JSON 输出 + 失败策略):
```
go run cmd/scanner/main.go --image alpine:3.14 --format json --fail-on high
```
构建独立的二进制文件:
```
go build -o sentinel ./cmd/scanner
./sentinel --image alpine:3.14
```
## 示例输出
这是一次对 `alpine:3.14` 的真实扫描。当前镜像已完全修补 —
其五个包拥有 SecDB 通告,但每一个都已经处于或高于修复
版本,因此没有被标记出任何问题:
```
=== SENTINEL CONTAINER SCANNER ===
Target: alpine:3.14
[*] Phase 1: Extracting Image...
Pulling image 'alpine:3.14' from registry...
Saving image 'alpine:3.14' (using binary: /usr/local/bin/docker)...
[*] Phase 2: Analyzing Layers...
Found Alpine package database in layer: blobs/sha256/422ed46b1a92...
-> Generated SBOM with 14 installed packages.
[*] Phase 3: Vulnerability Matching...
-> Detected Alpine OS Version: v3.14
-> Loaded 462 packages from Alpine SecDB.
=== VULNERABILITY REPORT ===
[✓] No known vulnerabilities found! Your image is clean.
=== SCAN CONTEXT ===
- Scanned At (UTC) : 2026-06-19T15:13:58Z
- Installed Packages : 14
- SecDB Packages Loaded : 462
- Packages Matched In DB : 5
- Vulnerable Packages : 0
- Unique CVE Findings : 0
- Highest Severity : NONE
```
扫描一个较旧的、已停止维护的标签会暴露出真实的发现。在这里,`alpine:3.10`
报告了一个存在漏洞的 `apk-tools` 及其 CVE、严重性和修复建议:
```
=== SENTINEL CONTAINER SCANNER ===
Target: alpine:3.10
[*] Phase 1: Extracting Image...
Pulling image 'alpine:3.10' from registry...
Saving image 'alpine:3.10' (using binary: /usr/local/bin/docker)...
[*] Phase 2: Analyzing Layers...
Found Alpine package database in layer: blobs/sha256/26d14edc4f17...
-> Generated SBOM with 14 installed packages.
[*] Phase 3: Vulnerability Matching...
-> Detected Alpine OS Version: v3.10
-> Loaded 285 packages from Alpine SecDB.
=== VULNERABILITY REPORT ===
[!] VULNERABILITY FOUND: apk-tools
- Installed Version : 2.10.6-r0
- Earliest Fix In : 2.10.7-r0
- Severity : LOW (score: 30/100)
- CVEs : CVE-2021-36159
- Remediation : Upgrade apk-tools to version 2.10.7-r0 or newer, then rebuild and redeploy the image.
[!] Scan complete. 1 unique CVE findings detected.
=== SCAN CONTEXT ===
- Scanned At (UTC) : 2026-06-19T15:16:33Z
- Installed Packages : 14
- SecDB Packages Loaded : 285
- Packages Matched In DB : 3
- Vulnerable Packages : 1
- Unique CVE Findings : 1
- Highest Severity : LOW
```
## 输出和策略标志
- `--format text|json` (默认值: `text`)
- `--fail-on none|low|medium|high|critical` (默认值: `none`)
`--fail-on` 使得当最高发现严重性达到或超过阈值时,
扫描器以非零状态退出 — 可使用它来**控制构建或 pipeline**。
### 退出代码
| 代码 | 含义 |
|------|-------------------------------------------------------------|
| `0` | 扫描完成;未达到 `--fail-on` 阈值。 |
| `1` | 使用或运行时错误(错误的标志、提取/网络失败)。 |
| `2` | 扫描完成但触发了 `--fail-on` 阈值。 |
## 已知的局限性
这些是当前范围内的有意边界,不是意外的疏漏。其中一些
已在 [ROADMAP.md](ROADMAP.md) 中被列为路线图项目。
- **仅支持 Alpine。** 检测和 SecDB 数据源是 Alpine 专用的。Debian/Ubuntu
(dpkg) 和 RHEL/UBI (rpm) 已计划在 `DistroAdapter` 接口后实现,但尚未
落地。
- **静态镜像层扫描,而非运行时。** 扫描器读取内置在
镜像层中的 apk 数据库。它**不**检查正在运行的容器、观察进程,
也不会捕获运行时安装的包(例如入口点中的 `apk add`)。
- **仅限 OS 包。** 它与 Alpine 包数据库进行匹配。语言级别的
依赖项(`node_modules`, `go.mod`, `requirements.txt`, …)尚未被扫描。
- **严重性衍生自 CVE 数量,而非 CVSS。** 具有一个严重 RCE 漏洞的包
目前的得分可能低于具有多个低影响问题的包。真实的 CVSS
评分(通过 NVD/OSV)是第二阶段的首要路线图项目。
- **需要 Docker daemon。** 镜像通过 `docker pull`/`docker save` 获取。
无 daemon 的 registry 拉取已在计划中。
- **结果追踪实时的 SecDB 快照。** 随着建议
数据的演变,即使镜像标签固定,发现结果也可能随时间而变化。
## 运行测试
```
go test -race ./...
```
## 贡献
开发约定(包边界、错误处理、测试、
零依赖策略)已在 [CONVENTIONS.md](CONVENTIONS.md) 中记录,计划中的工作在 [ROADMAP.md](ROADMAP.md) 中跟踪。
标签:Docker安全, EVTX分析, Go, LLM防护, Ruby工具, SBOM, 动态分析, 文档结构分析, 日志审计, 硬件无关, 请求拦截