Zum Inhalt

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 statusaudit-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-haproxy nach 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_LISTEN wieder Hostnamen, die ausschliesslich auf Loopback auflösen (etwa ein /etc/hosts-Eintrag auf 127.0.0.1); die Fehlermeldung bei Ablehnung nennt jetzt die tatsächliche Auflösung.
  • dicom-router-ctl drain-and-shutdown erkennt „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.