https://github.com/davidcollom/komodor-security-reporter
low-noise bridge between running Kubernetes workloads, vulnerability scanners (Trivy, Snyk, Wiz), and Komodor
https://github.com/davidcollom/komodor-security-reporter
Last synced: 25 days ago
JSON representation
low-noise bridge between running Kubernetes workloads, vulnerability scanners (Trivy, Snyk, Wiz), and Komodor
- Host: GitHub
- URL: https://github.com/davidcollom/komodor-security-reporter
- Owner: davidcollom
- License: mit
- Created: 2026-05-07T12:22:37.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-07T16:16:10.000Z (about 2 months ago)
- Last Synced: 2026-05-07T16:40:46.549Z (about 2 months ago)
- Language: Go
- Size: 86.9 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
README
# Komodor Image Vulnerability Watcher
A Kubernetes-native watcher that detects container image vulnerabilities and publishes actionable security events to Komodor.
## Overview
This application provides a secure, low-noise bridge between running Kubernetes workloads, vulnerability scanners (Trivy, Trivy Operator reports, Snyk, Wiz, Clair), and Komodor. It:
- Watches Kubernetes workloads (Deployments, StatefulSets, DaemonSets, Jobs, CronJobs)
- Extracts container images from running workloads
- Resolves mutable tags to immutable digests
- Scans images for vulnerabilities using pluggable scanner drivers
- Normalises scanner-specific results into a common model
- Publishes meaningful vulnerability events to Komodor
- Deduplicates scan results to avoid alert fatigue
## Security Model
The watcher is designed with a strong security posture:
- **No host access**: No hostPath mounts, runtime sockets, or privileged containers
- **No Secret access**: Does not require Kubernetes API permissions to read Secrets
- **No impersonation**: Does not impersonate workload ServiceAccounts
- **Minimal RBAC**: Read-only access to workload metadata only
- **Restricted container**: Runs as non-root with read-only filesystem and dropped capabilities
See [RFC](docs/rfc.md) for full security principles.
## Quick Start
### Prerequisites
- Kubernetes 1.24+
- A container image scanner (e.g., Trivy)
- Komodor account and API key
### Installation
```bash
# Using make
make install-tools
make build
# Or manually
go install ./cmd/komodor-security-reporter
```
### Configuration
Copy and customise the example configuration:
```bash
cp docs/example-config.yaml config.yaml
```
Key configuration options:
```yaml
clusterName: prod-eks-01
# Namespaces to watch
namespaces:
include:
- production
- platform
exclude:
- kube-system
# Workload kinds to watch
workloads:
kinds:
- Deployment
- StatefulSet
# Scanners to use
scanners:
scanners:
- name: trivy
type: trivy
enabled: true
command:
binary: /usr/local/bin/trivy
timeout: 5m
# Publishing policies
publishing:
mode: komodor
minimumSeverity: high
includeTopFindings: 5
publishCleanScans: false
dedupeTTL: 24h
komodor:
baseURL: https://app.komodor.io
```
### Running Locally
```bash
# Set Komodor API key (required for publish mode: komodor or both)
export KOMODOR_API_KEY=your-api-key
# Run with config
./bin/komodor-security-reporter -config config.yaml
# Enable debug logging
./bin/komodor-security-reporter -config config.yaml -log-level debug
```
## Development
See [DEVELOPMENT.md](./docs/DEVELOPMENT.md) for more info
## Architecture
### Components
1. **Kubernetes Workload Watcher**: Observes supported workload resources and detects image references
2. **Image Extractor**: Reads images from containers, initContainers, and ephemeralContainers
3. **Digest Resolver**: Resolves image tags to immutable digests using go-containerregistry
4. **Scanner Registry**: Loads configured scanner drivers and invokes them
5. **Scanner Drivers**: Scan images and return structured results (Trivy, Trivy Operator, Snyk, Wiz, Clair)
6. **Finding Normaliser**: Converts scanner-specific results to a common model
7. **Policy Evaluator**: Determines whether to publish based on severity and deduplication
8. **Komodor Publisher**: Publishes normalised events to Komodor
9. **State Store**: ConfigMap-backed deduplication state
### Data Flow
```plain
Kubernetes Workload
↓
Image Extractor
↓
Digest Resolver
↓
Scanner Registry
↓
Finding Normaliser
↓
Policy Evaluator
↓
Komodor Event Publisher
↓
State / Dedupe Store
```
## Testing
```bash
# Run all tests
make test
# Run with coverage
make coverage
# Run short tests only (excludes integration tests)
make test-short
# Run specific test
go test -run TestFingerprint ./internal/policy
```
### Test Strategy
- **Unit tests**: All core logic components (config, scanners, registry, policy)
- **Table-driven tests**: For parsing logic and policy evaluation
- **Mock/fake clients**: For Kubernetes and Komodor interactions
- **Integration tests**: Optional for scanner drivers (skipped by default)
## Deployment
### Kubernetes Manifests
See `helm/` directory for complete Helm chart.
Minimal ServiceAccount and RBAC:
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: komodor-security-reporter
namespace: security
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: komodor-security-reporter
rules:
- apiGroups: [""]
resources: [namespaces]
verbs: [get, list, watch]
- apiGroups: [apps]
resources: [deployments, statefulsets, daemonsets]
verbs: [get, list, watch]
- apiGroups: [batch]
resources: [jobs, cronjobs]
verbs: [get, list, watch]
# For ConfigMap-backed state store
- apiGroups: [""]
resources: [configmaps]
verbs: [get, list, create, update]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: komodor-security-reporter
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: komodor-security-reporter
subjects:
- kind: ServiceAccount
name: komodor-security-reporter
namespace: security
```
### Pod Security
```yaml
spec:
securityContext:
runAsNonRoot: true
runAsUser: 65534
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
containers:
- name: watcher
image: komodor-security-reporter:latest
imagePullPolicy: IfNotPresent
env:
- name: KOMODOR_API_KEY
valueFrom:
secretKeyRef:
name: komodor-credentials
key: api-key
ports:
- name: metrics
containerPort: 8080
volumeMounts:
- name: config
mountPath: /etc/komodor-security-reporter
readOnly: true
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/komodor-security-reporter
volumes:
- name: config
configMap:
name: komodor-security-reporter
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
```
## Metrics
Prometheus metrics are exposed on `:8080/metrics`:
- `image_vuln_watcher_images_observed_total` - Total images observed
- `image_vuln_watcher_images_resolved_total` - Total images resolved to digest
- `image_vuln_watcher_image_resolution_errors_total` - Resolution failures
- `image_vuln_watcher_scans_total` - Total scans performed
- `image_vuln_watcher_scan_errors_total` - Scan failures
- `image_vuln_watcher_scan_duration_seconds` - Scan duration histogram
- `image_vuln_watcher_events_published_total` - Events published
- `image_vuln_watcher_event_publish_errors_total` - Publishing failures
- `image_vuln_watcher_dedupe_hits_total` - Deduplicated events
## Debugging Kubernetes Events
When running with `--publish-mode=events` or `--publish-mode=both`, the reporter creates native Kubernetes `Warning` Events on the affected workload. You can inspect these with `kubectl`:
```bash
# List all VulnerabilityScan events across all namespaces
kubectl get events -A --field-selector reason=VulnerabilityScan
# Filter to a specific namespace
kubectl get events -n --field-selector reason=VulnerabilityScan
# Watch events in real time
kubectl get events -A --field-selector reason=VulnerabilityScan -w
# Show full event details for a specific workload
kubectl describe deployment/ -n | grep -A5 VulnerabilityScan
# Filter by the reporting component
kubectl get events -A \
--field-selector reportingComponent=komodor-security-reporter
```
Events use `reason=VulnerabilityScan` and `type=Warning` (or `Normal` for clean scans). The `MESSAGE` column contains the vulnerability summary, e.g.:
```plain
summary="critical=3 high=21 medium=16 low=1 total=49" scanner=trivy image=... findings=49 (critical=3 high=21 medium=16 low=1)
```
> **Note:** For human-readable local output, add `--log-format=text` to the reporter flags.
## Logging
Structured logging uses JSON output with contextual fields:
```json
{"level":"info","msg":"loaded configuration","cluster":"prod-eks-01","time":"2024-01-15T10:30:45Z"}
{"level":"warn","msg":"failed to resolve image digest","image":"ghcr.io/acme/app:1.0","error":"404 not found"}
```
Log levels: `debug`, `info` (default), `warn`, `error`
## Observability
### Health Checks
`GET /healthz` - Returns 200 OK if service is healthy
### Metrics
`GET /metrics` - Prometheus-format metrics
### Logs
All events are structured with fields:
- `cluster` - Cluster name
- `namespace` - Workload namespace
- `workloadKind` - Kind of workload (Deployment, etc)
- `workloadName` - Workload name
- `container` - Container name
- `image` - Image reference
- `digest` - Image digest
- `scanner` - Scanner name
## Release
### Build Release Locally
```bash
# Build snapshot (development)
make release-snapshot
# Create a release (requires git tag)
# 1. Tag the release: git tag v0.1.0
# 2. Run release:
make release
```
The release process:
1. Builds binaries for linux/amd64, linux/arm64, darwin/amd64, darwin/arm64
2. Creates archives and checksums
3. Builds and pushes Docker images (requires Docker credentials)
4. Creates GitHub release with binaries
5. Generates changelog from commits
### Docker Images
Images are published to: `ghcr.io/davidcollom/komodor-security-reporter`
Tags:
- `vX.Y.Z` - Specific release
- `latest` - Latest release
## Configuration Reference
See [docs/example-config.yaml](docs/example-config.yaml) for full configuration options.
## Architecture Decision Records
See [docs/rfc.md](docs/rfc.md#decision-record) for design decisions:
- No CRDs in v1
- No Kubernetes Secret read RBAC
- No impersonation
- No host-level access
## Contributing
1. Ensure tests pass: `make test`
2. Format code: `make fmt`
3. Run linters: `make lint`
4. Create a pull request
## License
Apache License 2.0
## Support
For issues, questions, or suggestions, please open a GitHub issue.