sec-analyzer — Paket- und Schwachstellen-Inventar für Linux-Server
Ein kleines Werkzeug, das auf jedem Server nachsieht, welche Pakete installiert sind, und an zentraler Stelle beantwortet: Welcher Server hat welche Sicherheitslücke, und reicht ein Update, um sie zu schließen?
Zwei Programme in Go, ohne externe Abhängigkeiten:
| Komponente | Läuft auf | Aufgabe |
|---|---|---|
sec-agent |
jedem Server, als systemd-Dienst | listet die installierten Pakete, aktualisiert den Paketindex, ermittelt die anstehenden Updates und meldet das Ergebnis |
sec-server |
Kubernetes, als Container | sammelt die Berichte, gleicht jede Paketversion gegen Schwachstellen-Daten ab und erzeugt die Übersichten |

Warum
apt list --upgradable sagt, dass 23 Pakete neuer sein könnten. Es sagt nicht,
welche davon eine Sicherheitslücke schließen, wie schlimm die ist, oder auf
welchen der zwölf Server das gleiche Problem noch offen steht. Und wenn ein CVE
in den Nachrichten steht, ist die Frage nicht „habe ich curl installiert",
sondern „ist meine Version betroffen, und gibt es überhaupt schon einen Fix".
Genau diese Lücke füllt sec-analyzer: Inventar sammeln, gegen die Schwachstellen-Daten der jeweiligen Distribution halten, und das Ergebnis so sortieren, dass die Arbeit danach klar ist.
Unterstützte Distributionen
| Distribution | Paketmanager | Schwachstellen-Quelle |
|---|---|---|
| Debian 11+ | apt | OSV.dev |
| Ubuntu | apt | OSV.dev, inkl. Ubuntu-Pro-Kennzeichnung |
| Proxmox VE / PBS | apt | OSV.dev (als Debian), wird als Variante erkannt |
| AlmaLinux 8/9/10 | dnf | OSV.dev |
| Rocky Linux 8/9/10 | dnf | OSV.dev |
| RHEL 7+ | dnf/yum | OSV.dev (Hauptstrom, ohne EUS/AUS/TUS) |
| SLES, SLE Micro | zypper | OSV.dev |
| openSUSE Leap / Tumbleweed | zypper | OSV.dev |
| Fedora | dnf5 | Errata des Herstellers über den Paketmanager |
| CloudLinux, Oracle Linux | dnf/yum | Errata des Herstellers über den Paketmanager |
| CentOS / CentOS Stream | dnf/yum | Red-Hat-Advisories der gleichen Hauptversion |
Inventar und anstehende Updates funktionieren auf allen genannten Systemen.
Wie es aufgebaut ist
Server 1 ─┐
Server 2 ─┼─ sec-agent ──HTTPS──▶ sec-server ──▶ OSV.dev
Server N ─┘ (systemd) Token (Kubernetes)
│
└──▶ Dashboard, JSON-API, Prometheus
Der Agent läuft viermal täglich über einen systemd-Timer, mit zufälligem
Versatz, damit nicht die ganze Flotte gleichzeitig meldet. Er braucht Root
(der Paketindex lässt sich sonst nicht aktualisieren) und läuft in einer
gehärteten Unit: schreibgeschütztes Dateisystem bis auf die Verzeichnisse des
Paketmanagers, eingeschränkte Capabilities, Syscall-Filter, Nice=10,
IOSchedulingClass=idle, CPUQuota=50%. Das Inventar soll den Betrieb nicht
stören.
Der Collector hält die Daten im Speicher und schreibt sie als JSON auf ein Volume. Ein Scan von fünf Servern braucht etwa 25 MB Arbeitsspeicher; das Image ist 16 MB groß und läuft als nicht-privilegierter Benutzer mit schreibgeschütztem Wurzeldateisystem.
Wie die CVEs ausgewertet werden
Das ist der Teil, der mehr Nachdenken gekostet hat als erwartet, weil jede Distribution ihre Daten anders führt.
1. Was der Agent meldet
Pro Paket Name, Version, Architektur und Quellpaket. Letzteres ist wichtig:
Debian und Ubuntu werden in den Schwachstellen-Daten nach Quellpaketen
indiziert (libcurl4t64 gehört zu curl), die RPM-Distributionen dagegen nach
Binärpaketen. Der Collector fragt je nach Distribution den richtigen Namen ab.
Dazu die anstehenden Updates, ermittelt aus einer simulierten Aktualisierung — es wird nichts installiert:
- apt:
apt-get --just-print dist-upgrade - dnf/yum:
dnf repoquery --upgrades - zypper:
zypper --xmlout list-updates
Bei dnf und zypper kommen Advisory-Kennung und Hersteller-Einstufung gleich mit
(ALSA-2026:88247, Critical).
2. Welches Release wird abgefragt
Aus /etc/os-release wird das passende Ecosystem abgeleitet:
ubuntu + 24.04 → Ubuntu:24.04, almalinux + 9.8 → AlmaLinux:9,
sles + 15.5 → SUSE mit Release 15 SP5.
Nicht jede Distribution lässt sich dabei auf ein Release einschränken. Eine
Abfrage für Red Hat liefert Treffer für alle Red-Hat-Produkte — auch
enterprise_linux:10.2 oder hummingbird:1. Der Collector filtert deshalb
selbst und behält nur die Einträge, die zum Release des Servers gehören. Die
Ströme EUS, AUS und TUS bleiben draußen: sie liefern eigene Fix-Versionen, die
ein Server ohne dieses Abo nie bekommt.
3. Abgleich mit der Datenbank
Alle eindeutigen Kombinationen aus Ecosystem, Paket und Version der gesamten Flotte gehen gebündelt an die OSV-API. Eine Flotte aus zehn gleichen Servern kostet damit eine Abfrage, nicht zehn. Die Details jedes Treffers landen in einem lokalen Cache.
Von jedem Advisory wird nur gespeichert, was diese Flotte betrifft. Ohne diesen Filter wird es schnell absurd: ein einzelnes Ubuntu-Kernel-Advisory listet 164 betroffene Releases mit über 21 000 Binärpaketen, fast alles über Systeme, die hier niemand betreibt. Mit Filter braucht ein Scan von 138 Quellpaketen 37 MB statt 1350 MB — bei identischen Befunden.
4. Zwei Datenquellen
OSV.dev ist eine echte Schwachstellen-Datenbank: sie kennt auch Lücken, für die noch kein Fix veröffentlicht ist, und liefert CVSS-Werte.
Die Repository-Metadaten des Servers selbst springen ein, wo OSV nichts
führt. dnf und zypper bringen die Errata ihres Herstellers mit, inklusive
Advisory-Kennung, Einstufung und CVE-Nummern. Damit sind Fedora, CloudLinux und
Oracle Linux prüfbar, ohne sie einer anderen Distribution zuzuordnen.
Der Unterschied, der zählt: der Paketmanager kennt nur Lücken, für die ein Fix bereitsteht. Was noch auf einen Patch wartet, taucht dort nicht auf. Welche Quelle einen Befund geliefert hat, steht in der Oberfläche in einer eigenen Spalte.
CentOS ist der Sonderfall: es liefert gar keine Errata-Metadaten —
dnf updateinfo ist dort leer, auch bei anstehenden Updates. Deshalb wird
CentOS automatisch gegen die Red-Hat-Advisories derselben Hauptversion geprüft,
mit einem sichtbaren Hinweis, dass die Daten von dort stammen.
5. Versionsvergleich
Hier liegt eine Falle: dpkg und rpm ordnen Versionen unterschiedlich. rpm
ignoriert Trennzeichen und behandelt ^ eigen, dpkg nicht. Der falsche
Algorithmus liefert stillschweigend falsche Antworten — ein Paket gilt als
gepatcht, obwohl es verwundbar ist.
sec-analyzer bringt beide Vergleicher mit und wählt pro Distribution. Die
rpm-Variante wurde gegen librpm selbst geprüft: 8000 Versionspaare aus echten
Paketdaten, null Abweichung.
6. Aufräumen
- Advisories, die kein Release dieses Servers nennen, werden verworfen.
- Liegt die installierte Version bereits über der Fix-Version, fällt der Treffer weg — eine Gegenprüfung gegen Zuordnungsfehler.
- Advisory-Bündel (USN, DSA, DLA) werden verworfen, wenn ihre CVEs schon als einzelne Einträge für dasselbe Paket gemeldet sind. OSV veröffentlicht beide Sichten; ohne das zählt dieselbe Lücke doppelt.
7. Die entscheidende Frage
Zum Schluss wird abgeglichen, ob die vom Paketmanager angebotene Version den Fix tatsächlich enthält. Daraus ergibt sich die Dreiteilung, um die herum die Oberfläche gebaut ist:
| bedeutet | |
|---|---|
| Update genügt | ein apt upgrade bzw. dnf upgrade schließt die Lücke |
| Update genügt nicht | es gibt ein Update, aber es enthält den Fix nicht |
| kein Fix | für diese Lücke ist noch kein Patch veröffentlicht |
8. Woher die Einstufung kommt
In dieser Reihenfolge:
- CVSS-v3-Vektor, Base Score nach Spezifikation selbst berechnet
- Hersteller-Bewertung aus dem Advisory (
{"type":"Ubuntu","score":"high"}) - Debian-Urgency
- Die Einstufung, die
dnfoderzypperauf dem Server selbst nennt
Punkt 4 ist nicht kosmetisch: AlmaLinux und SUSE veröffentlichen in OSV keine Schwere. Ohne diesen Rückgriff wären sämtliche AlmaLinux-Befunde „ohne Angabe". Mit ihm werden aus 12 unbewerteten Befunden 1× kritisch, 6× hoch, 5× mittel. Die Herkunft der Einstufung steht als „Hersteller" in der Tabelle.
Gibt es keine der vier Angaben, bleibt es bei „ohne Angabe" — geschätzt wird nichts.
Was man in der Oberfläche sieht
Übersicht
Oben eine Leitzahl — dringend offene Lücken — daneben die Verteilung über die Schweregrade. Darunter die Zahlen in einer Zeile, dann die Serverliste nach Risiko sortiert, dann die dringendsten Advisories über die ganze Flotte.

Server
Pro Server die Befunde nach Schwere, die anstehenden Updates, der Zustand des Paketindex und wann zuletzt gemeldet wurde. Die farbige Kante am Zeilenanfang zeigt die höchste Schwere auf diesem System.

Ein einzelner Server
Alles zu einem System: anstehende Updates mit Advisory-Kennung, und darunter die Lücken mit Quelle, Einstufung, Istzustand und Zielversion.

Das wiederkehrende Element ist der Versions-Übergang. Überall, wo eine Version wechselt, wird der gemeinsame Teil gedimmt und nur das Abweichende hervorgehoben:
7.76.1-40.el9_8.5 → 7.76.1-40.el9_8.8 ein Patch-Level
1:3.5.8-1.el9_8 → 1:3.5.8-2.el9_8 ein neuer Build
2.34-275.el9_8 → 2.34-283.el9_8 viele Revisionen zurück
Damit ist auf einen Blick erkennbar, wie weit ein Paket zurückliegt — eine Information, die in gleichförmigen Zeichenketten sonst untergeht.
Sicherheitslücken
Alle Advisories über die Flotte, schwerste zuerst, filterbar nach Server, Paket und Schwere. Die Spalte „Behebbar" trennt, was ein Update löst, von dem was eine Entscheidung braucht.

Paket-Updates
Rein paketmanager-getrieben: was apt, dnf oder zypper einspielen würden —
einmal nach Server und einmal nach Paket gruppiert. Die Spalte „schließt" zeigt,
wie viele Lücken ein Update nebenbei erledigt.

Maßnahmen
Die Gegenprobe: Pakete mit bekannten Lücken, die ein einfaches Upgrade nicht schließt. Kein Fix veröffentlicht, Fix nur mit Ubuntu Pro, oder ein Release-Upgrade nötig. Das ist die Liste, die eine Entscheidung braucht.
Anmeldung

Kurzanleitung
Collector ausrollen
# Token für die Agenten und ein Konto für das Dashboard erzeugen
make token
make user-add USER=anna ROLE=admin
# beides in deploy/secret.yaml eintragen, dann
make login REGISTRY_USER=<user>
make k8s-set-tag
make release-server
make k8s-apply
Mitgeliefert werden Namespace, Deployment, Service, ConfigMap, Secret, PVC, Traefik-IngressRoute, cert-manager-Certificate und NetworkPolicy.
Agent ausrollen
make release-client
make rollout-client HOSTS="web01 db02" TOKEN=<token-aus-dem-secret>
Das Rollout-Skript kopiert das Paket per SSH und ruft das Installationsskript
auf. Für mehr als eine Handvoll Server eine hosts.txt anlegen. Ein Upgrade
behält Token, Konfiguration und Planungsart bei:
make release-client
make rollout-client HOSTS_FILE=hosts.txt
make agent-versions UI_AUTH=anna:passwort # prüfen, was wo läuft
Benutzer und Rechte
Zwei Rollen:
| Rolle | darf |
|---|---|
viewer |
alles lesen: Übersichten, API, CSV-Export |
admin |
zusätzlich Server entfernen, aufräumen, Scans auslösen |
Konten liegen im Kubernetes-Secret, eine Zeile je Konto, erzeugt mit
make user-add. Das Passwort wird ohne Echo eingelesen und nur als
PBKDF2-HMAC-SHA256-Hash ausgegeben (600 000 Iterationen). Wer nur lesen darf,
bekommt die Entfernen-Schaltflächen gar nicht erst angezeigt.
Server wieder entfernen
Im Dashboard über „entfernen" mit Bestätigungsseite, oder:
make host-delete HOST=testvm01 UI_AUTH=anna:passwort
make hosts-prune DAYS=30 DRY_RUN=1 UI_AUTH=anna:passwort
API
GET /api/v1/summary Flotten-Kennzahlen
GET /api/v1/hosts alle Server mit Zählern
GET /api/v1/vulnerabilities ?host= &package= &min_severity= &fixable=1
GET /api/v1/updates Paketmanager-Sicht pro Server
GET /api/v1/actions was ein Update nicht löst
DELETE /api/v1/hosts/{name} Server entfernen
GET /export.csv?kind=vulns|updates
GET /metrics /healthz /readyz ohne Anmeldung, für Prometheus und Probes
Technisches
- Go, nur Standardbibliothek. Kein einziges externes Modul — auch das
Passwort-Hashing nicht, seit
crypto/pbkdf2in der Stdlib ist. - Oberfläche ohne JavaScript. Content-Security-Policy
default-src 'none', Stylesheet aus eigener Quelle, keine externen Schriften, keine Inline-Styles. Helles und dunkles Farbschema nach Systemeinstellung, Kontraste mindestens 4,5:1 für Kleintext. - Container: 16 MB,
scratch-Basis, nicht-privilegiert, schreibgeschütztes Wurzeldateisystem, alle Capabilities entzogen. - Prometheus-Metriken pro Server: Befunde nach Schwere, anstehende Updates, Zeitpunkt des letzten Reports, Reboot-Bedarf.
Grenzen
Ehrlichkeitshalber, weil ein Sicherheitswerkzeug nicht so tun soll, als sähe es mehr als es sieht:
- Pakete außerhalb der Distributions-Repositories — selbst gebaut, Drittanbieter-Quellen wie Docker CE — sind in keiner der Datenquellen enthalten und bleiben ungeprüft. Sie erscheinen im Inventar, aber nie in den Befunden.
- Der Paketmanager als Quelle (Fedora, CloudLinux, Oracle) kennt nur Lücken mit verfügbarem Fix. Offene Lücken ohne Patch sind dort unsichtbar.
- CentOS Stream läuft RHEL voraus. Seine Pakete sind oft neuer als die in den RHSA genannten Fix-Versionen, weshalb die Liste dort eher zu knapp als zu großzügig ausfällt.
- Ein nennenswerter Teil der Debian-Advisories hat keine zugewiesene Schwere und erscheint als „ohne Angabe".
- Ubuntu-Lücken, die nur über Ubuntu Pro behoben sind, werden als solche gekennzeichnet — ohne Abo gibt es dafür kein Update.
- Die Daten sind so aktuell wie der letzte Report. Ein Server, der sich nicht meldet, wird als veraltet markiert, nicht als sauber.
Die Screenshots zeigen erfundene Server und Pakete aus der mitgelieferten
Vorschau (make preview), keine echten Systeme.