Was ist neu in v1.17.2¶
Veröffentlicht am 28. August 2026.
Schwerpunkt ist das Audit-Webview: Authentifizierung mit Rollen, vier neue Seiten — Gerätekatalog, Zielkatalog, Statistik und Betriebslog —, ein gemeinsamer Zeitraum-Filter, der sichtbar gemachte Q/R-Bogen und ein Härtungspaket. Am Router selbst eine Korrektur, die jede Auswertung betrifft — die Zeitstempel des Audit-Trails standen nicht in UTC —, dazu Schema v9 und eine Reihe kleinerer Korrekturen.
Vor dem Update¶
Audit-Zeitstempel standen bisher nicht in UTC¶
Bis 1.17.1 schrieb der Router die Zeitstempel des Audit-Trails in der lokalen
Zone des Knotens statt in UTC. Auf einem Knoten in Europe/Zurich liegen alle
vor dem Update geschriebenen Zeilen in associations, dimse_operations und
router_events damit 1–2 Stunden daneben — und mit ihnen alles, was diese
Spalten liest: Anzeige und Zeitraum-Filter des Webviews, Grafana, die
Retention-Statements (die dadurch zu früh löschen) und jede Korrelation mit
PACS- oder Modality-Logs. Läuft der Knoten selbst in UTC, ist nichts
verschoben. Zeilen ab 1.17.2 sind korrekt; ein aktualisierter Bestand hat
an der Stelle des Updates einen Sprung.
Wer den Altbestand korrigieren will, findet in
share/audit-timestamp-utc-fix.sql.example ein einmaliges Korrekturskript.
Wichtig dabei:
- Vor dem Stopp der alten Instanz — also vor dem Update, nicht erst vor dem
Skript-Lauf — den Audit-Spool leerlaufen lassen (
dicom-router-ctl status→audit-backlog: 0). Eine Zeile, die 1.17.1 in den Spool schrieb, liefert 1.17.2 mit korrektem Zeitpunkt nach; das Skript würde sie fälschlich mitverschieben. - Das Skript braucht die Zeitzone des Knotens als Eingabe (als IANA-Name,
z. B.
Europe/Zurich), läuft pro Knoten und bei gestopptem Router (Anleitung im Skript). - Es ist nicht idempotent — ein zweiter Lauf verschiebt erneut.
Ob korrigiert wird, ist eine Betriebsentscheidung; der Router funktioniert in beiden Fällen.
Schema v9: der Indexbau blockiert Schreiber kurzzeitig¶
Die Migration auf Schema v9 ist additiv bis auf das Entfernen eines nie
genutzten Spalten-Defaults (verhaltensneutral) — dazu kommen ein Index auf
dimse_operations.rq_at und die View device_capabilities; ein laufender
1.17.1-Knoten schreibt unverändert gegen eine v9-Datenbank weiter. Startet ein
1.17.1-Knoten gegen die migrierte Datenbank neu, bootet er dagegen nicht mehr
(Downgrade-Guard) — einen solchen Knoten aktualisieren statt neu starten. Der Indexbau nimmt aber
ein Schreib-Lock auf dimse_operations. Auf einem grossen Bestand steht das
Update dort eine Weile; das Audit puffert währenddessen über seinen Spool. Wer
das Fenster vermeiden will, legt den Index vorab mit CREATE INDEX
CONCURRENTLY unter demselben Namen idx_dimse_rq_at an — die Migration findet
ihn dann und überspringt ihn. Für Grafana kommt ein GRANT SELECT auf
device_capabilities dazu.
Webview: netzgebunden nur noch mit Authentifizierung¶
Bis 1.17.1 durfte das Webview auf eine beliebige Adresse binden und warnte nur
im Log. Eine Installation mit WEBVIEW_LISTEN=0.0.0.0:8406 (oder einer festen
Netzadresse) startet nach dem Update nicht mehr — unter systemd läuft sie
wegen Restart=on-failure in eine Neustartschleife; im Log steht „refusing to
listen on … without authentication". Drei Wege hinaus: zurück auf 127.0.0.1
und Zugriff über einen SSH-Tunnel, WEBVIEW_AUTH=file mit Benutzerdatei und
TLS, oder WEBVIEW_AUTH=proxy hinter einem Reverse-Proxy, der bereits
authentifiziert. Die Anleitung steht in webview/README.md. Ein Bind auf
Loopback ändert sich nicht.
Webview: keine iframe-Einbettung mehr¶
Jede Antwort trägt frame-ancestors 'none' und X-Frame-Options: DENY. Wer
den Viewer in eine Portalseite eingebettet hat, braucht künftig einen Link
statt eines Frames.
Webview: Grants für die neuen Seiten¶
Gerätekatalog, Zielkatalog und Statistik lesen die Basistabellen
associations, presentation_contexts,
presentation_context_transfer_syntaxes und dimse_operations direkt. Fehlt
dem Datenbank-Benutzer webview_ro das SELECT auf eine davon, antwortet die
Seite mit einem Datenbankfehler. Der vollständige Grant-Block steht in
webview/README.md.
dicom-router-ctl: Exit-Code 3 bei Ablehnung¶
drain, resume, reload und shutdown beenden sich jetzt mit 3, wenn
der Router das Kommando ablehnt — bisher war das eine 0, und eine
Automatisierung unter set -e las Erfolg, während etwa die alte Konfiguration
weiterlief. 1 bleibt „nicht erreichbar". Ein drain auf einen bereits
drainenden und ein resume auf einen bereits bereiten Router bleiben bei 0,
damit Retry-Schleifen nicht brechen.
Das Audit-Webview¶
Authentifizierung mit Rollen¶
Drei Betriebsarten: none (Default, nur auf Loopback zulässig), file mit
Anmeldeformular gegen eine bcrypt-Benutzerdatei, proxy mit der Identität aus
dem Header eines vertrauenswürdigen Reverse-Proxy. Dazu die Rollen phi und
nophi: nophi sieht Patientenname und Patienten-ID maskiert und kann nicht
danach suchen — die Maskierung sitzt in der SQL-Abfrage, nicht erst in der
Anzeige. Ein Zugriffs-Log hält fest, wer wann wonach gesucht hat, und optional
terminiert das Webview TLS selbst.
Gerätekatalog und Zielkatalog¶
/devices zeigt, welches Gerät von welcher IP anruft, mit welcher
Implementierung, welche Presentation Contexts es vorschlägt, welche akzeptiert
und welche abgelehnt werden — letzteres beantwortet ohne SQL, warum ein Gerät
etwas nicht senden kann. Die Geräteseite fasst „was geht wohin" in einer Zeile
je SOP-Klasse und Ziel zusammen, gezählt in Associations statt in einzelnen
Kontexten. /destinations ist die Gegenrichtung: die Stellen, an die der
Router sendet — mit der Implementierungs-Kennung des Ziels, dem, was es
annimmt und was nicht, und den zurückgegebenen DIMSE-Status-Codes, dem
Unterschied zwischen „erreichbar" und „gesund". Beide Kataloge verlinken
dieselbe Gegenstelle untereinander, und beide Listen sind über die
Spaltenköpfe sortierbar.
Statistik¶
/stats zeigt über den gewählten Zeitraum Verkehrsmatrix, Top-Talker,
SOP-Klassen, Antwortzeiten, DIMSE-Fehler und abgewiesene Associations — die
Betriebsübersicht für Installationen ohne Grafana. Bei den Antwortzeiten steht
neben dem Mittelwert das 95. Perzentil, weil weder Mittelwert noch Maximum die
Frage „hängt dieses Ziel regelmässig" beantworten.
Betriebslog¶
/events listet die zentral erfassten WARN- und ERROR-Meldungen aller Knoten —
einschliesslich der Ereignisse ohne Association (Start, Reload-Fehler,
Datenbank-Warnungen), die bisher nur per SQL sichtbar waren. Gefiltert wird
über Zeitraum, Level, Knoten und Meldungstext; die Textsuche gibt es nur für
die Rolle phi, weil die Meldungen für nophi maskiert sind.
Gemeinsamer Zeitraum und Q/R-Bogen¶
Ein Zeitraum-Filter — 24 Stunden, 7 Tage, 30 Tage (Vorgabe), gesamter Bestand oder ein freies Von–Bis — gilt für beide Kataloge, die Statistik und das Betriebslog und wird gemerkt; ein Zeitraum in der URL gewinnt, damit geteilte Links funktionieren. Und der Q/R-Bogen ist sichtbar: die Detailansicht verlinkt die zwei Associations eines Retrieves in beide Richtungen und zeigt die vom Router vergebene MessageID, über die die C-STOREs des einen Beins dem C-MOVE des anderen zuzuordnen sind.
Komfort mit und ohne JavaScript¶
Eine schmale, statische JavaScript-Schicht ergänzt Kopier-Knöpfe an den Monospace-Feldern (UIDs, AE Titles, IPs; sie brauchen HTTPS oder localhost), lässt Preset- und Auswahlfilter bei Änderung direkt submitten und zeigt zu jedem UTC-Zeitstempel die Browser-Ortszeit als Tooltip. Inline-Skripte bleiben per Content-Security-Policy blockiert, und jede Seite funktioniert weiter ohne JavaScript — sortiert und gefiltert wird serverseitig.
Dazu ein Härtungspaket: Sicherheits-Header auf jeder Antwort, HTTP-Zeitlimits und ein geordneter Shutdown, eine Login-Sperre, die auch parallelen Versuchen standhält, und CSRF-Schutz der Anmeldung.
Datenbank¶
Der neue Index auf dimse_operations.rq_at beschleunigt Abfragen über schmale
Zeitfenster auf der grössten Tabelle deutlich (gemessen auf 1,69 Mio. Zeilen:
4,6 ms statt 107 ms für eine Stunde). Die neue View device_capabilities
beantwortet „was kann Gerät X" an einer Stelle: eine Zeile je Gerät, Ziel und
SOP-Klasse mit beiden Ausgängen, gezählt in Associations — die bisherigen
Views bleiben für bestehende Dashboards. Und die Zeitstempel-Korrektur selbst:
der Schreibpfad rechnete den Zonen-Offset doppelt heraus; neue Zeilen stehen
in UTC, für den Bestand gilt der Abschnitt oben unter „Vor dem Update".
Weitere Korrekturen¶
--render-haproxynach stdout bleibt sauber: weder reguläre Log-Ausgaben noch Logback-Statusmeldungen landen mehr in der umgeleiteten HAProxy-Konfiguration.- Locale-Härtung: die Zustandswörter der Management-Antworten und die
Kommandonamen in
commands:-Selektoren werden locale-unabhängig verarbeitet — unter einer türkischen System-Locale griffen beide bisher daneben. - Das Webview akzeptiert für
WEBVIEW_LISTENwieder Hostnamen, die ausschliesslich auf Loopback auflösen (etwa ein/etc/hosts-Eintrag auf127.0.0.1); die Fehlermeldung bei Ablehnung nennt jetzt die tatsächliche Auflösung. dicom-router-ctl drain-and-shutdownerkennt „läuft schon draining" am präzisen Zustandswort und wertet eine Ablehnung nicht mehr als Erfolg.
Dazu eine Reihe kleinerer Korrekturen im Webview und an den Skripten; die vollständige Liste steht im CHANGELOG des Pakets.