Three passages still described cve/images.txt, and one of them instructed the next person to keep it in step with the stack on every change — the exact habit ADR-0026 abolished, and the one that had left coverage at 52 percent. The section now says there is nothing to keep in step, names the three sources the set is derived from, and carries the warning that matters: a failed derivation shrinks the desired set, which makes coverage look better rather than worse. Whoever edits this must not trim the freshness stamps away. The compose comment said the same thing and is corrected with it.
236 lines
8.2 KiB
YAML
236 lines
8.2 KiB
YAML
services:
|
|
prometheus:
|
|
image: prom/prometheus:v3.3.1
|
|
container_name: prometheus
|
|
restart: unless-stopped
|
|
volumes:
|
|
# Verzeichnis- statt Einzeldatei-Mount: Docker haengt Einzeldatei-Mounts am
|
|
# Inode auf, und `git pull` ersetzt Dateien per Rename (neuer Inode) - der
|
|
# Container sah die Aenderung dann nie, waehrend SIGHUP brav Erfolg meldete.
|
|
# Verzeichnis-Mounts loesen bei jedem Zugriff ueber den Pfad auf.
|
|
# Config-Pfade bleiben unveraendert (--config.file zeigt weiter dorthin).
|
|
- ./prometheus:/etc/prometheus:ro
|
|
- prometheus_data:/prometheus
|
|
command:
|
|
- '--config.file=/etc/prometheus/prometheus.yml'
|
|
- '--storage.tsdb.path=/prometheus'
|
|
- '--storage.tsdb.retention.time=45d'
|
|
- '--storage.tsdb.retention.size=15GB'
|
|
# Remote-Write-Receiver: k3s-Cluster und Matrix-Server pushen hierher.
|
|
# ACHTUNG: Port 9090 ist oeffentlich und ohne Auth erreichbar --
|
|
# per Hetzner Cloud Firewall auf die Absender-IPs einschraenken!
|
|
- '--web.enable-remote-write-receiver'
|
|
ports:
|
|
- "9090:9090"
|
|
networks:
|
|
- traefik
|
|
|
|
alertmanager:
|
|
image: prom/alertmanager:v0.28.1
|
|
container_name: alertmanager
|
|
restart: unless-stopped
|
|
volumes:
|
|
# Verzeichnis-Mount, gleicher Grund wie bei Prometheus. Die beiden .py aus
|
|
# diesem Ordner liegen dadurch mit unter /etc/alertmanager - ungenutzt und
|
|
# harmlos, alertmanager laedt ausschliesslich --config.file.
|
|
- ./alertmanager:/etc/alertmanager:ro
|
|
- alertmanager_data:/alertmanager
|
|
command:
|
|
- '--config.file=/etc/alertmanager/alertmanager.yml'
|
|
- '--storage.path=/alertmanager'
|
|
# bewusst kein oeffentlicher Port - nur Prometheus/Receiver im traefik-Netz
|
|
networks:
|
|
- traefik
|
|
|
|
# Alertmanager-Webhook -> Matrix (wartung-Raum), gleiche Machart wie
|
|
# maintenance-notify (gitops Issue #24). Secrets kommen aus .env.
|
|
matrix-alerts:
|
|
image: python:3.13-slim
|
|
container_name: matrix-alerts
|
|
restart: unless-stopped
|
|
environment:
|
|
- MATRIX_HOMESERVER=${MATRIX_ALERT_HOMESERVER}
|
|
- MATRIX_ROOM_ID=${MATRIX_ALERT_ROOM_ID}
|
|
- MATRIX_TOKEN=${MATRIX_ALERT_TOKEN}
|
|
# Raum-Routing (gitops#47): Alerts mit Label room=security -> Security-Raum
|
|
- MATRIX_ROOM_SECURITY=${MATRIX_RELEASE_ROOM_ID:-}
|
|
# Ohne das liegt der State unter /tmp im Writable Layer: der ueberlebt
|
|
# zwar ein "compose restart", aber kein "up -d", das den Container neu
|
|
# baut -- also genau jeden Deploy. Dann verlieren offene Alarme ihre
|
|
# Event-Zuordnung und loesen sich ueber den Fallback als separate
|
|
# Nachricht auf, statt die Firing-Nachricht abzuhaken.
|
|
- MATRIX_STATE_FILE=/state/matrix-alerts-state.json
|
|
volumes:
|
|
- ./alertmanager:/app:ro
|
|
- matrix_alerts_data:/state
|
|
command: python3 /app/matrix-alerts.py
|
|
networks:
|
|
- traefik
|
|
|
|
# Release-/Advisory-Watch (gitops#22): meldet neue Releases der Element-Stack-
|
|
# Upstreams in den Alerts-Raum (🚨 bei Security-Verdacht). Gleicher Bot/Raum
|
|
# wie matrix-alerts, eigener State (Erstlauf merkt nur, flutet nicht).
|
|
release-watch:
|
|
image: python:3.13-slim
|
|
container_name: release-watch
|
|
restart: unless-stopped
|
|
environment:
|
|
- MATRIX_HOMESERVER=${MATRIX_ALERT_HOMESERVER}
|
|
- MATRIX_ROOM_ID=${MATRIX_ALERT_ROOM_ID}
|
|
# CVE-/Release-Raum (gitops#47): leer lassen = Fallback Alerts-Raum
|
|
- MATRIX_RELEASE_ROOM_ID=${MATRIX_RELEASE_ROOM_ID:-}
|
|
- MATRIX_TOKEN=${MATRIX_ALERT_TOKEN}
|
|
volumes:
|
|
- ./alertmanager:/app:ro
|
|
- release_watch_data:/state
|
|
command: python3 /app/release-watch.py
|
|
networks:
|
|
- traefik
|
|
|
|
# CVE-Pipeline (gitops#47), Teil 1: Trivy scannt die real deployten Images
|
|
# (Ziele aus /targets/targets.txt - abgeleitet vom cve-exporter, NICHT von Hand
|
|
# pflegen: #0106 / ADR-0026)
|
|
cve-scan:
|
|
image: aquasec/trivy:0.58.2
|
|
container_name: cve-scan
|
|
restart: unless-stopped
|
|
entrypoint: ["/bin/sh", "/config/scan-loop.sh"]
|
|
volumes:
|
|
- ./cve:/config:ro
|
|
- cve_results:/results
|
|
- cve_trivy_cache:/root/.cache
|
|
# Ab Slice 3 die Quelle der Ziele; in Slice 1 nur eingehaengt, damit
|
|
# der Pfad existiert und beim Umschalten kein Rennen entsteht.
|
|
- cve_targets:/targets:ro
|
|
networks:
|
|
- traefik
|
|
|
|
# Teil 2: Reports -> Prometheus-Metriken (Schema + Pflichtfelder siehe gitops#47)
|
|
cve-exporter:
|
|
image: python:3.13-slim
|
|
container_name: cve-exporter
|
|
restart: unless-stopped
|
|
environment:
|
|
- RESULTS_DIR=/results
|
|
- STATE_FILE=/state/first-seen.json
|
|
# Soll-Menge der Scan-Ziele (#0106, ADR-0026): abgeleitet, nicht gepflegt.
|
|
# Der Exporter bildet sie, weil er fuer die Deckungsmetrik ohnehin BEIDE
|
|
# Mengen kennen muss - ein zweiter Ort waere eine zweite Wahrheit.
|
|
- TARGETS_FILE=/targets/targets.txt
|
|
- PROMETHEUS_URL=http://prometheus:9090
|
|
- REGISTRY_HOST=rohana.axion1337.de
|
|
# Reichweite (Entscheidung sorb 2026-08-21, Option C): Bestand plus die
|
|
# letzten drei Fassungen je Repo - so weit, wie ein Rollback zielt.
|
|
- REGISTRY_TAGS_JE_REPO=3
|
|
# Nicht je Scrape herleiten: bei 15 s waeren das ~21 000
|
|
# Registry-Anfragen am Tag; eine Runde kostet ~90.
|
|
- SOLL_INTERVALL_SEKUNDEN=3600
|
|
volumes:
|
|
- ./cve:/app:ro
|
|
- cve_results:/results:ro
|
|
- cve_exporter_state:/state
|
|
- cve_targets:/targets
|
|
command: python3 /app/cve-exporter.py
|
|
networks:
|
|
- traefik
|
|
|
|
loki:
|
|
image: grafana/loki:3.7.1
|
|
container_name: loki
|
|
restart: unless-stopped
|
|
volumes:
|
|
- ./loki:/etc/loki:ro
|
|
- loki_data:/loki
|
|
command: -config.file=/etc/loki/loki-config.yaml
|
|
# Port 3100 oeffentlich: externe Alloys pushen Logs hierher.
|
|
# Ebenfalls per Firewall auf bekannte Absender-IPs einschraenken.
|
|
ports:
|
|
- "3100:3100"
|
|
networks:
|
|
- traefik
|
|
|
|
alloy:
|
|
image: grafana/alloy:v1.16.0
|
|
container_name: alloy
|
|
restart: unless-stopped
|
|
volumes:
|
|
- ./alloy:/etc/alloy:ro
|
|
- /var/log:/var/log:ro
|
|
- /var/lib/docker/containers:/var/lib/docker/containers:ro
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
# Muss persistent sein, sonst ist die Positions-Datei nach jedem
|
|
# Recreate weg und Alloy liest alle Container-Logs von vorn. Loki
|
|
# weist die alten Eintraege dann ab (reject_old_samples, Default 7d).
|
|
- alloy_data:/var/lib/alloy
|
|
command: run --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy
|
|
cap_add:
|
|
- DAC_READ_SEARCH
|
|
- NET_RAW
|
|
networks:
|
|
- traefik
|
|
depends_on:
|
|
- prometheus
|
|
- loki
|
|
|
|
grafana:
|
|
image: grafana/grafana:12.0.0
|
|
container_name: grafana
|
|
restart: unless-stopped
|
|
environment:
|
|
# Greift nur beim allerersten Start mit leerem Volume.
|
|
# Der Live-Admin ist davon unabhaengig (Passwortaenderungen in der UI).
|
|
- GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER}
|
|
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
|
|
volumes:
|
|
- grafana_data:/var/lib/grafana
|
|
- ./grafana/provisioning:/etc/grafana/provisioning:ro
|
|
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.docker.network=traefik"
|
|
- "traefik.http.routers.grafana.rule=Host(`selendis.axion1337.de`)"
|
|
- "traefik.http.routers.grafana.entrypoints=websecure"
|
|
- "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
|
|
- "traefik.http.services.grafana.loadbalancer.server.port=3000"
|
|
depends_on:
|
|
- prometheus
|
|
- loki
|
|
networks:
|
|
- traefik
|
|
|
|
node-exporter:
|
|
image: prom/node-exporter:v1.9.1
|
|
container_name: node-exporter
|
|
restart: unless-stopped
|
|
volumes:
|
|
- /proc:/host/proc:ro
|
|
- /sys:/host/sys:ro
|
|
- /:/rootfs:ro
|
|
command:
|
|
- '--path.procfs=/host/proc'
|
|
- '--path.sysfs=/host/sys'
|
|
- '--path.rootfs=/rootfs'
|
|
- '--web.listen-address=:9100'
|
|
- '--web.disable-exporter-metrics'
|
|
ports:
|
|
- "9100:9100"
|
|
networks:
|
|
- traefik
|
|
|
|
volumes:
|
|
prometheus_data:
|
|
alertmanager_data:
|
|
matrix_alerts_data:
|
|
release_watch_data:
|
|
cve_results:
|
|
cve_trivy_cache:
|
|
cve_exporter_state:
|
|
cve_targets:
|
|
grafana_data:
|
|
loki_data:
|
|
alloy_data:
|
|
|
|
networks:
|
|
traefik:
|
|
external: true
|