Worum es geht
Ein System wird überwacht, indem es einen kleinen Endpunkt bereitstellt, den der Statusdienst regelmäßig abfragt. Der Endpunkt läuft auf einem eigenen Port und ist per Firewall ausschließlich für die IP des Statusservers erreichbar.
Eine einzige Abfrage kann mehrere Komponenten speisen: Der Endpunkt meldet beliebig viele Einzelprüfungen — Datenbank, Mailversand, Speicherplatz — und jede davon kann im Statusdienst zu einer eigenen Zeile werden.
1. Endpunkt bereitstellen
Pfad und Fassung sind festgelegt:
GET https://<host>:9443/mike-status/v1/health
Authorization: Bearer <projektspezifisches Token>Die Antwort ist JSON und sieht so aus:
{
"schema": 1,
"dienst": "technikerportal",
"version": "1.4.2",
"gestartet": "2026-08-20T04:12:00Z",
"geprueft": "2026-08-22T10:00:03Z",
"status": "ok",
"pruefungen": [
{ "schluessel": "db", "name": "Datenbank", "status": "ok", "dauerMs": 3 },
{ "schluessel": "mail", "name": "Mail-Relay", "status": "degraded", "dauerMs": 1240,
"hinweis": "Warteschlange bei 412 Nachrichten" },
{ "schluessel": "disk", "name": "Speicher", "status": "ok", "dauerMs": 1, "wert": "38%" }
]
}Felder
| Feld | Pflicht | Bedeutung |
|---|---|---|
schema | ja | Fassung des Vertrags. Derzeit immer 1. |
dienst | ja | Kurzname des Systems, klein geschrieben, stabil. |
version | nein | Ausgelieferte Version, rein informativ. |
gestartet | nein | Startzeitpunkt des Prozesses, ISO-8601 in UTC. |
geprueft | nein | Zeitpunkt der Messung, ISO-8601 in UTC. |
status | ja | Gesamtstatus: ok, degraded oder down. |
pruefungen | nein | Liste der Einzelprüfungen. Fehlt sie, zählt nur der Gesamtstatus. |
pruefungen[].schluessel | ja | Stabiler Schlüssel. Er verbindet die Prüfung mit einer Komponente und darf sich nie ändern. |
pruefungen[].name | nein | Anzeigetext für Mitarbeiter. Darf sich jederzeit ändern. |
pruefungen[].status | ja | ok, degraded oder down. |
pruefungen[].dauerMs | nein | Laufzeit dieser Einzelprüfung. |
pruefungen[].hinweis | nein | Kurzer Klartext für Mitarbeiter. Keine Fehlerausgabe. |
pruefungen[].wert | nein | Messwert als Text, z. B. 38%. |
2. Regeln für die Umsetzung
- Antwort in unter zwei Sekunden. Jede Einzelprüfung bekommt ein eigenes Timeout, Richtwert eine Sekunde, und darf niemals blockieren.
- Ergebnis 5–10 Sekunden zwischenspeichern. Ohne Cache ist der Endpunkt ein Hebel, um die Anwendung über ihre eigenen Prüfungen lahmzulegen.
- Keine internen Angaben ausgeben. Keine Verbindungszeichenfolgen, keine Stacktraces, keine internen Hostnamen, IPs oder Benutzerdaten.
- Nur lesen. Der Endpunkt schreibt nichts, legt nichts an und verändert keinen Zustand.
- Eigener Port, eigener Listener. Nicht unter der öffentlichen Domain und nicht hinter dem öffentlichen Reverse-Proxy.
- Rate-Limit auf dem Endpunkt. Unabhängig von der Firewall — zwei Schlösser, nicht eins.
- Schlüssel stabil halten. Ein umbenannter Schlüssel gilt als neue Prüfung; die alte Komponente verliert ihre Datenquelle.
3. Zugang absichern
Zwei unabhängige Sperren. Die Firewall ist die Hauptabsicherung, das Token die zweite Linie: IP-Adressen wechseln, und auf geteilter Infrastruktur ist „die IP" nicht dasselbe wie „dieser Dienst".
Firewall (nftables)
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Statusdienst darf den Health-Port abfragen - sonst niemand.
ip saddr <IP-DES-STATUSSERVERS> tcp dport 9443 accept
}
}Token
32 Byte Zufall, in der .env des Projekts, Vergleich in konstanter Zeit:
openssl rand -base64 32TLS
Auch intern verschlüsselt. Ein selbstsigniertes Zertifikat genügt: Im Statusdienst wird dessen SHA-256-Fingerabdruck hinterlegt, und nur genau dieses Zertifikat wird akzeptiert. Das spart einen weiteren Let's-Encrypt-Namen für einen Endpunkt, der ohnehin nur von einer IP erreichbar ist. Fingerabdruck ermitteln:
openssl x509 -in zertifikat.pem -noout -fingerprint -sha2564. Anbindung in ASP.NET Core
Für MIKE-Projekte gibt es das Paket Mike.Status.HealthEndpoint. Damit ist der Anschluss drei Zeilen:
builder.Services.AddMikeStatus(o =>
{
o.Dienst = "technikerportal";
o.Port = 9443; // eigener Listener
o.Token = builder.Configuration["STATUS_TOKEN"]!;
});
builder.Services.AddMikeCheck<DatenbankPruefung>("db", "Datenbank");
builder.Services.AddMikeCheck<MailPruefung> ("mail", "Mail-Relay");
app.MapMikeStatus();Eine Einzelprüfung ist eine kleine Klasse:
public sealed class DatenbankPruefung(NpgsqlDataSource quelle) : IMikeCheck
{
public async Task<MikeCheckErgebnis> PruefenAsync(CancellationToken ct)
{
await using var verbindung = await quelle.OpenConnectionAsync(ct);
await using var befehl = verbindung.CreateCommand();
befehl.CommandText = "select 1";
await befehl.ExecuteScalarAsync(ct);
return MikeCheckErgebnis.Ok();
}
}Für alles, was nicht .NET ist, gilt schlicht der JSON-Vertrag oben — die Umsetzung ist in jeder Sprache eine Handvoll Zeilen.
5. Jobs statt Dienste: Puls senden
Nächtliche Sicherungen, Abgleiche und Aufgaben hinter NAT lassen sich nicht sinnvoll anpingen. Sie melden sich stattdessen selbst. Der Statusdienst legt dafür einen Monitor an und gibt einen Puls-Schlüssel aus, der genau einmal angezeigt wird:
curl -fsS -X POST https:///beat/<schluessel>Am Ende des Jobs aufrufen. Bleibt der Puls länger als das eingestellte Fenster aus, gilt die Komponente als gestört. In einer crontab steht das so:
30 3 * * * /opt/backup/lauf.sh && curl -fsS -X POST https:///beat/<schluessel>&& ist die halbe Miete: Der Puls
geht nur raus, wenn der Job auch erfolgreich war. Ein ; würde auch
nach einem Fehlschlag melden, dass alles in Ordnung sei.6. Freigabe im Backend
Nach dem Anlegen des Monitors zeigt ein Testlauf sofort die Rohantwort — Fehler bei Firewall, Token oder Zertifikat fallen dort auf und nicht erst im Betrieb. Anschließend wird je gefundenem Prüfschlüssel entschieden: verwerfen, verborgen mitlaufen lassen oder als Komponente veröffentlichen.
7. Maschinenlesbarer Status
Der aktuelle Gesamtzustand steht als JSON bereit und lässt sich von anderen Systemen auswerten:
curl -fsS https:///status.json