RedFlag/RAF/scanners/02-docker-scanner.md
Fimeg 67e26be2d9 projection: begin the exact-path public epoch
The public tree and its history contain only the listed paths. Earlier projection history remains preserved internally.

Source-Sha: 913fde029b935671833254797f0f20f1eb9fabba

Policy-Sha: 913fde029b935671833254797f0f20f1eb9fabba

Tree-Digest: 180ae530c1058a2a5c89837bdce2d323ae83e669e38590ca72e75b8d92b7262f
2026-09-08 21:59:33 -04:00

111 lines
3.8 KiB
Markdown

# Docker Scanner
**Docker image scanning for container-based agents.**
---
## Component Details
| Property | Value |
|----------|-------|
| Method | Read `/var/run/docker.sock` via Docker SDK |
| Platform | Linux (Docker agents) |
| Execution time | ~5 seconds per scan |
| Output format | `[]client.UpdateReportItem` with full metadata in `Metadata` map |
| Failure modes | Docker daemon unavailable, socket permission denied, registry rate-limited |
---
## Implementation
**File:** `agent/internal/orchestrator/docker_scanner.go`
The Docker scanner connects directly to the local Docker daemon via the Docker SDK, lists all containers, inspects each image, then queries the remote registry (Docker Hub or custom) to compare digests.
```go
// agent/internal/orchestrator/docker_scanner.go
func (s *DockerScanner) Scan() ([]client.UpdateReportItem, error) {
containers, err := s.client.ContainerList(ctx, container.ListOptions{All: true})
// ... inspect each image, compare local vs remote digest ...
items = append(items, client.UpdateReportItem{
PackageType: "docker_image",
PackageName: imageName,
CurrentVersion: localShortDigest,
AvailableVersion: remoteShortDigest,
Severity: severity,
RepositorySource: baseImage,
Metadata: map[string]interface{}{
"has_update": hasUpdate,
"image_id": localShortDigest,
"latest_image_id": remoteShortDigest,
// ... container info, labels, etc.
},
})
return items, nil
}
```
### Registry Client
The `RegistryClient` within `docker_scanner.go` handles:
- Docker Hub token authentication (`auth.docker.io`)
- Registry API v2 manifest fetch via `Docker-Content-Digest` header
- 5-minute TTL cache to avoid rate limits
- Custom registry support (gcr.io, etc.) via domain detection in `parseImageName()`
---
## Integration Points
### 1. Orchestrator Registration
Registered in `agent/internal/agent/loop.go` as a direct `orchestrator.Scanner` implementation:
```go
dockerScanner, _ := orchestrator.NewDockerScanner()
scanOrchestrator.RegisterScanner("docker", dockerScanner, dockerCB, ...)
```
### 2. Command Dispatch
The `HandleScanDocker` handler in `agent/internal/handlers/scan.go` runs the scan once through the orchestrator and reads results from `result.Updates[]` — no double-scanning.
### 3. Server-Side Storage
Scan results are reported via `client.ReportDockerImages()` to `POST /api/v1/agents/:id/docker-images`. The server stores them in `docker_images` table via `server/internal/database/queries/docker.go` (`CreateDockerEventsBatch`, `GetDockerImages`, etc.).
---
## Data Flow
```
Agent Poll → Server creates "scan_docker" command
Agent receives command → orchestrator.ScanSingle("docker")
DockerScanner.Scan() connects to local Docker daemon
List containers, inspect images, check registry digests
Return UpdateReportItems with full metadata
Handler reports results to server API endpoint
Server stores in docker_images table
```
---
## Footer: Assumptions & Connections
**Assumption:** Docker daemon is available on the agent host (`/var/run/docker.sock`). Registry access requires outbound internet (or mirror configuration).
**Connection:** Scanner registration (`agent/internal/agent/loop.go`) wires Docker into the orchestrator alongside APT, DNF, Windows, and Winget scanners — all now implement `orchestrator.Scanner` directly (no wrapper layer).
**Connection:** Registry client (`orchestrator/docker_scanner.go`) uses ETHOS `[TAG] [system] [component]` logging for registry failures.
**Connection:** Server-side `docker_images` table (`server/internal/database/queries/docker.go`) stores reported scan results for dashboard display.
---
*Last reviewed: 2026-05-27*