1. Risiko übermäßiger Sichtbarkeit im Actuator
Wenn wir zum ersten Mal env und configprops aktivieren, scheint es, dass dies einfach eine praktische Fehlersuche ist: man schaut nach und versteht, was aus YAML geladen wurde, was durch eine Umgebungsvariable überschrieben wurde und was von den Startparametern stammt. Das Problem ist, dass Actuator kein „Log für Entwickler“ ist, sondern eine HTTP‑Oberfläche. Und HTTP hat bekanntlich die unangenehme Angewohnheit, nicht nur für uns zugänglich zu werden.
Stellen Sie sich vor, Sie haben zu Hause einen „Sicherungskasten“ mit Automaten, und Sie entscheiden, die Tür dafür durchsichtig zu machen, um „schneller zu verstehen, was wo ist“. Solange es in der Wohnung ist, ist es okay. Aber wenn jemand diesen Kasten in den Flur rausholt, oder Sie versehentlich den Zugang öffnen, wird die Transparenz plötzlich zu einem Geschenk für „alle Interessierten“.
Mit dem Actuator ist es genauso. Die Endpunkte env und configprops können zeigen:
- welche Property‑Quellen es überhaupt in der Anwendung gibt, und damit – wo man nach Geheimnissen suchen kann;
- welche Werte gewonnen haben, und damit – was genau gerade verwendet wird;
- woher sie kommen, und damit – wo das liegt: Datei, Umgebungsvariablen, Systemeigenschaften usw.
Und selbst wenn Sie diese Endpunkte nicht „nach außen“ geöffnet haben, bleiben praktische Szenarien, in denen dies zum Problem wird: gemeinsamer Dev‑Stand, öffentlicher Testserver „nur für einen Moment“, weitergeleiteter Port, zufälliger Reverse‑Proxy, „ich habe einfach include: "*" hinzugefügt, um mich nicht zu quälen“. Der Actuator hat kein eingebautes Schamgefühl, er antwortet ehrlich. Deshalb fügen wir einen „Sicherheitsgurt“ in Form von Sanitization und einem sorgfältigen Management‑Port hinzu.
2. Was sind sensitive Werte im Kontext eines Backend‑Service
Einsteiger denken oft, dass „sensitiv“ strikt Passwort und Token bedeutet und alles andere gezeigt werden kann. In der Praxis ist Sensitivität weiter gefasst: Es sind alle Daten, die helfen, das System anzugreifen, die Privatsphäre verletzen oder die interne Küche offenlegen. Und das Unangenehmste: Manchmal ist der Wert an sich harmlos, aber die Kombination „Schlüssel + Quelle + Umgebung“ wird bereits zu nützlicher Information für einen Angreifer (oder einfach für einen neugierigen Kollegen im Büro‑Wi‑Fi).
Unten eine kleine „Landkarte“, damit Sie verstehen, was plötzlich sensitiv sein kann, selbst im Lehr‑catalog-service:
| Kategorie | Beispielwert | Warum es unangenehm zu zeigen ist | Wo taucht es oft auf |
|---|---|---|---|
| Geheimnisse (offensichtlich) | Passwörter, API‑Keys, Token, Client‑Secrets | direkter Zugriff auf fremde Dienste/Daten | env, configprops, manchmal info (wenn Sie es dort ablegen) |
| Interne Adressen | jdbc:..., interne Hostnamen, URLs interner APIs | hilft, die Infrastruktur „abzutasten“ | env, configprops |
| Pfade zu Dateien/Verzeichnissen | /opt/app/config/..., C:\Users\... | hilft, die Umgebung zu verstehen, manchmal Datenlecks | configprops (origin/inputs), env |
| Geschäftsvertraulich | Preise, Kurslisten, Features, Flags „bald veröffentlicht“ | Es ist kein „Geheimnis“, aber möglicherweise nicht öffentlich erwünscht | configprops (Ihre @ConfigurationProperties) |
| Personenbezogene Daten | E‑Mails, Telefonnummern, Benutzer‑IDs | Privatsphäre und Compliance | Häufiger in Logs und Domänen‑Endpunkten, aber manchmal auch in der Konfiguration |
Für unseren catalog-service ist besonders der „geschäftsvertrauliche“ Teil aussagekräftig: Die Liste der Kurse und Preise halten wir in der Konfiguration. Technisch gesehen ist es kein Passwort. Aber aus Produktsicht kann es durchaus Informationen sein, die Sie nicht jedem geben möchten, der die URL /actuator/configprops errät. Daher lautet das Hauptprinzip für heute: Sanitization ist nützlich, ersetzt aber nicht den gesunden Menschenverstand und die Einschränkung der Exposure.
3. Sanitization: Wie Spring Boot Werte maskiert
Wenn Sie zum ersten Mal env oder configprops öffnen, bemerken Sie vielleicht eine angenehme Sache: Einige Werte werden nicht „wie sie sind“ angezeigt, stattdessen erscheint ******. Das ist Sanitization – das Maskieren potenziell sensibler Daten. Boot versucht, Ihr Freund zu sein und kein „Rate meinen Token aus den Logs“ direkt aus der Box zu veranstalten.
Aber es ist wichtig, zwei verschiedene Stellschrauben zu verstehen:
Die erste Stellschraube ist, ob Werte überhaupt angezeigt werden sollen. Dafür haben viele Endpunkte eine Einstellung im Stil von show-values (insbesondere für env und configprops). Die zweite Stellschraube ist, welche Schlüssel immer maskiert werden sollen, auch wenn das Anzeigen von Werten grundsätzlich erlaubt ist. Dies wird normalerweise über keys-to-sanitize konfiguriert.
Um dies nicht als „Abstraktion“ im Kopf behalten zu müssen, ist es praktischer, show-values als Ausgabemodus zu betrachten:
| show-values | Was Sie in der Antwort sehen | Für wen geeignet |
|---|---|---|
| never | Werte sind immer verborgen (Maske anstelle des Werts) | Sicherer Basiswert, besonders ohne Sicherheit |
| when_authorized | Werte werden nur „autorisierten“ Benutzern angezeigt | Nützlich, wenn es einen Autorisierungsmechanismus gibt (in unserem Kurs nicht vorhanden) |
| always | Werte werden angezeigt, außer denen, die durch Schlüssel in die Sanitization fallen | Lokale Fehlersuche „auf dem eigenen Laptop“, und das nur vorübergehend |
Und hier ist der entscheidende Punkt des Kurses: Solange wir keine Sicherheit haben, verwandelt sich der Modus when_authorized meistens in „Überraschung: Sie sind immer noch nicht autorisiert“ oder „Überraschung: Sie zeigen trotzdem alles, weil der Schutz nicht so konfiguriert ist, wie Sie denken“. Daher ist es im catalog-service auf Kursebene sinnvoller, sich auf never als sicheren Standard zu stützen und nur gezielt always einzuschalten, wenn Sie bewusst in einer lokalen Umgebung sind und die Konsequenzen verstehen.
Um ein Gefühl dafür zu bekommen, wie das aussieht, hier ein typischer Ausschnitt aus einer Antwort, bei dem der Wert maskiert ist:
{
"property": {
"source": "systemEnvironment",
"value": "******"
}
}
Wichtig: Selbst wenn der Wert verborgen ist, können die bloße Tatsache des Vorhandenseins einer Eigenschaft, ihr Name und ihre Quelle bereits nützliche Informationen sein. Daher ist die „Maske“ keine Freikarte für „kann allen geöffnet werden“.
4. Konfigurieren von show-values für env und configprops im catalog-service
Wenn Sie env und configprops diagnostizieren, besteht die Versuchung, sie im Modus der offenen Lupe zu halten: So ist es einfacher zu sehen, welcher Wert gewonnen hat und wie er verknüpft ist. Aber ein solcher Modus ist nur als vorübergehende lokale Diagnose gut. Für den Dauerbetrieb ist es im catalog-service sicherer, dass die Endpunkte verfügbar sein können, die Werte aber standardmäßig verborgen bleiben.
Wir haben bereits eine Exposure‑Policy konfiguriert: In local/dev öffnen wir mehr diagnostische Endpunkte, in prod belassen wir ein Minimum. Jetzt fügen wir eine weitere Regel hinzu: Selbst wenn der Endpunkt in local verfügbar ist, werden die Werte standardmäßig dennoch verborgen.
Beginnen wir mit application-local.yaml. Hier nehmen wir an, dass wir in local env und configprops sehen möchten, aber ohne die Werte „offen zu legen“:
# src/main/resources/application-local.yaml
management:
endpoints:
web:
exposure:
# In local kann die erweiterte Diagnose aktiviert werden...
include: "health,info,env,configprops"
endpoint:
env:
# ...aber die Werte werden standardmäßig nicht angezeigt (nur Tatsache und Quelle).
show-values: never
configprops:
# Das Gleiche für @ConfigurationProperties.
show-values: never
Aus Verhaltenssicht bedeutet das Folgendes. Die Endpunkte sind über HTTP verfügbar, weil wir sie in include aufgenommen haben, aber die Werte werden maskiert. Das ist ein guter Kompromiss für einen Anfänger: Sie können überprüfen, dass eine Eigenschaft existiert und woher sie kommt, aber Sie verwandeln den Actuator nicht in ein „Schaufenster der Umgebungsvariablen“.
Für prod halten wir eine „schmale“ Oberfläche und öffnen env/configprops gar nicht. Dann wird show-values in der Produktion zweitrangig, weil die Endpunkte von außen einfach nicht erreichbar sind:
# src/main/resources/application-prod.yaml
management:
endpoints:
web:
exposure:
# In prod belassen wir nur die grundlegenden Endpunkte.
include: "health,info"
Nun ein wichtiger praktischer Gedanke. Manchmal müssen Sie in local tatsächlich den Wert sehen. Zum Beispiel debuggen Sie, warum app.catalog.title nicht stimmt oder warum max-featured-count plötzlich 0 ist. In diesem Moment ist der sicherste Weg, die Datei nicht zu ändern, sondern die Einstellung vorübergehend über ein Startargument oder eine Umgebungsvariable zu überschreiben.
Zum Beispiel können Sie lokal mit temporären Overrides starten:
--management.endpoint.env.show-values=always
--management.endpoint.configprops.show-values=always
Wenn Sie nur einen Endpunkt benötigen, aktivieren Sie nur diesen.
Der Sinn ist, dass Sie den „gefährlichen Modus“ nicht im Repository festschreiben, sondern ihn als einmalige Lupe verwenden. In der Welt realer Projekte hilft das sehr, sich nicht in zwei Wochen nach der „vorübergehenden Fehlersuche“ zu fragen: „Warum werden im Dev‑Stand allen Token angezeigt?“
5. keys-to-sanitize: Wenn selbst „nicht ganz Geheimnisse“ maskiert werden müssen
Ein sehr menschlicher Fehler ist zu glauben, es reiche aus, show-values: always im lokalen Profil zu setzen und dann „aber wir maskieren ja die Geheimnisse“. Das Problem ist, dass Geheimnisse anhand von Schlüsselmustern maskiert werden. Und Menschen, uns eingeschlossen, nennen Schlüssel manchmal so, als wollten sie das System absichtlich täuschen: app.catalog.magicString, app.catalog.superValue, app.catalog.pleaseDontLookHere. Und was macht die Sanitization? Richtig: Sie zeigt höflich alles so, wie es ist.
Daher ist keys-to-sanitize eine Möglichkeit, Boot mitzuteilen: „Diese Schlüsselmuster immer verbergen, auch wenn ich die Anzeige von Werten aktiviert habe.“ Für env und configprops kann dies separat festgelegt werden. In einem Lehrprojekt speichern wir normalerweise keine echten Geheimnisse, aber die Umgebung (insbesondere auf einem Arbeitslaptop) kann reich an Variablen wie *_TOKEN und *_KEY sein.
Beispiel einer Basiskonfiguration in local, die das Verhalten vorhersehbarer macht:
# src/main/resources/application-local.yaml
management:
endpoint:
env:
# Maskieren typischer „gefährlicher“ Schlüssel, selbst wenn die Anzeige von Werten aktiviert ist.
keys-to-sanitize: "password,secret,token,key"
configprops:
# Das Gleiche für configprops (Ihre @ConfigurationProperties).
keys-to-sanitize: "password,secret,token,key"
Beachten Sie die Feinheit: keys-to-sanitize löst nicht das Exposure‑Problem. Es hilft nur, den Wert nicht „wie er ist“ preiszugeben. Die Schlüssel selbst, ihr Vorhandensein und ihre Quellen sind sichtbar, wenn der Endpunkt geöffnet ist. Daher geht es bei diesem Mechanismus darum, „den Schaden zu minimieren“, nicht darum, „es insgesamt sicher zu machen“.
Und noch eine amüsante, aber nützliche Erkenntnis: Wenn Sie doch geheime Parameter in der Konfiguration anlegen, ist es besser, sie so zu nennen, dass sie unter die Sanitization fallen. Ein Token sollte ein Token sein, kein „kristallener String des Schicksals“.
6. Separater Management‑Port
Selbst bei sorgfältigem include/exclude und verborgenen Werten bleibt eine praktische Schmerzstelle: Die Actuator‑Endpunkte leben neben den normalen URLs Ihrer Anwendung. Für einen Entwickler ist das nicht schlimm, aber für die Organisation von Umgebungen und für die Gewohnheit, „Produktionsanfragen und Diagnose nicht zu vermischen“, ist ein separater Management‑Port oft sehr praktisch.
Die Idee ist einfach: Der Hauptdienst antwortet auf einem Port, z. B. 8080, und die administrativen Actuator‑Endpunkte auf einem anderen, z. B. 8081. Visuell sieht das ungefähr so aus:
flowchart LR U["Browser / curl / Postman"] -->|"http://localhost:8080"| A["Product API + Landing Page"] U -->|"http://localhost:8081"| M["Actuator endpoints"]
In Spring Boot wird das mit einer einzigen Einstellung erledigt:
# src/main/resources/application-local.yaml
management:
server:
# Separater Port nur für /actuator/** (Bequemlichkeit, nicht Sicherheit).
port: 8081
Wichtig: Dies ist ein optionaler lokaler/Dev‑Zweig, keine neue Hauptvariante des Projekts. Das Basisszenario ist immer noch einfacher: Die Anwendung und der Actuator leben auf demselben Port, und Sie steuern, was sichtbar ist, über die Exposure‑Policy. Ein separater Port ist sinnvoll, wenn Sie Produktionsanfragen und Diagnose physisch trennen möchten, aber er ist nicht erforderlich.
Grob gesagt und ohne in die Tiefe zu gehen, startet Boot einen separaten Webserver für die Management‑Endpunkte. Ihre normalen Controller bleiben auf dem Hauptport, während /actuator/** auf den Management‑Port umzieht.
Was das ändert. Der Datenverkehr wird geteilt: Sie können Health, Info und Konfiguration überprüfen, ohne dies mit normalen Kataloganfragen zu vermischen. Für manche Umgebungen vereinfacht das das Leben: Sie können standardmäßig mit der Anwendung auf 8080 arbeiten und die Diagnose auf 8081 halten.
Was das nicht ändert. Es ist keine „eingebaute Sicherheit“. Wenn der Management‑Port für jemanden von außen erreichbar ist, sind die Endpunkte genauso zugänglich, als wären sie auf dem Hauptport. Das ist, als würde man einen Ersatzschlüssel unter die Fußmatte der Nebentür legen: Formal ist die Tür eine andere, aber die Schutzwirkung… fragwürdig.
Daher betrachten wir im Rahmen des Kurses den separaten Port als organisatorische Option für Local/Dev und nicht als zwingend erforderlich oder als „Ersatz für eine durchdachte Exposure‑Policy“.
Links und Gewohnheiten mit separatem Port
Sobald Sie den Actuator auf einen separaten Port auslagern, taucht ein alltägliches Detail auf, das leicht übersehen wird: Links auf der Landing‑Page wie <a href="/actuator/health"> verweisen auf denselben Port wie die Seite selbst. Das heißt, auf dem Hauptport führen sie zu 404, weil /actuator/** jetzt auf 8081 lauscht.
Das ist kein Fehler von Spring Boot – wir haben die physische „Adressierung“ der Management‑Endpunkte geändert. Daher sollte man im Voraus vereinbaren, wie man das für das Team oder für sich selbst in der Zukunft gestaltet, wenn man das Projekt in einem Monat wieder öffnet und sagt: „Warum funktioniert der Link nicht?“. Hier ist es wichtig, einen defekten Zustand nicht als Norm zu akzeptieren. Solange der Actuator auf demselben Port lebt, sind Links wie <a href="/actuator/health"> und <a href="/actuator/info"> auf der Landing‑Page praktisch und beschleunigen tatsächlich die Überprüfung des Dienstes. Wenn Sie vorübergehend einen separaten Management‑Port aktiviert haben, werden diese relativen Links auf der Hauptseite nutzlos: Sie führen immer noch zum Hauptport, auf dem der Actuator nicht mehr lauscht.
Daher sollte ein separater Port besser als separater Zweig der Local/Dev‑Konfiguration betrachtet werden. In diesem Zweig ist es besser, die normalen Links auf der Landing‑Page durch einen neutralen Hinweis auf den Pfad /actuator zu ersetzen und den korrekten Port separat zu öffnen, als einen schönen, aber toten 404 auf der Seite zu hinterlassen.
Wenn Sie einen solchen Zweig aktiviert haben und dennoch einen Hinweis auf der Seite lassen möchten, können Sie eine neutrale Notiz ohne Bindung an einen bestimmten Port hinzufügen, um prod nicht zu beschädigen:
<!-- src/main/resources/static/index.html -->
<p>
<!-- Hinweis ohne Portbindung: Der Port kann in verschiedenen Umgebungen variieren. -->
Actuator endpoints sind unter dem Pfad <code>/actuator</code> verfügbar.
Wenn in der Umgebung ein separater Management‑Port konfiguriert ist, öffnen Sie den Actuator auf diesem Port.
</p>
Dieser kleine Satz spart Anfängern viel Zeit: Sie hören auf zu vermuten, dass „der Actuator nicht funktioniert“, und beginnen zu prüfen, wohin er genau ausgelagert wurde.
Eine weitere hilfreiche Gewohnheit: Wenn Sie management.server.port aktivieren, schauen Sie direkt nach dem Start der Anwendung in die Start‑Logs. Dort ist meist sichtbar, auf welchem Port der Hauptserver und der Managementserver gestartet sind. Wenn Sie das nicht tun, werden Sie Minuten, manchmal Stunden damit verbringen, herauszufinden, „warum ich 404 bekomme“, obwohl die Antwort bereits in den Logs stand.
7. Sanitization schützt nicht vor Lecks in Logs
Ein sehr heimtückischer Punkt: Die Sanitization bezieht sich auf die Antworten der Actuator‑Endpunkte, hat aber keinerlei Macht über das, was Sie selbst loggen. Wenn Sie einmal log.info("CatalogProperties={}", catalogProperties) machen, dann landet alles im Log: die Liste der Kurse, Preise, Flags und so weiter. Und Logs leben im Gegensatz zum local-Actuator normalerweise lange und landen manchmal an Orten, wo Sie sie nicht erwartet haben.
Um dies mit unserem catalog-service zu verknüpfen, zeige ich zwei kurze Ausschnitte. Der erste – eine „schlechte, aber sehr verlockende Idee“:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// Angenommen, Sie haben einen Logger (z. B. über Lombok @Slf4j oder manuell).
log.info("Catalog properties: {}", catalogProperties); // Druckt das gesamte Konfigurationsobjekt.
So drucken Sie das gesamte Objekt und damit den gesamten Inhalt der Konfiguration. Für ein Lehrprojekt kann das einfach Rauschen sein, für ein reales Projekt ein Leck.
Der zweite – ein reiferer Stil: Nur Aggregate und sichere Flags loggen, die tatsächlich helfen, den Zustand des Dienstes zu verstehen, ohne das Log in einen Dump zu verwandeln:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// Anstatt eines vollständigen Dumps – loggen wir nur das, was wirklich für die Diagnose hilft.
log.info("Catalog loaded: {} courses, featured limit={}",
catalogProperties.courses().size(), // Wie viele Kurse geladen wurden (Aggregat)
catalogProperties.maxFeaturedCount()); // Einschränkung, die das Verhalten des Dienstes beeinflusst
Der Unterschied scheint „klein“, aber genau solche Kleinigkeiten trennen normalerweise einen Dienst, der sich bequem diagnostizieren lässt, von einem Dienst, der sich bequem „versehentlich bei einer Prüfung über Lecks abgeben lässt“.
Zusammengefasst mit dem Thema der heutigen Vorlesung ergibt sich eine einfache Regel: Der Actuator hilft zu sehen, und die Sanitization hilft, nicht zu viel in der Antwort zu sehen. Aber dafür, was Sie in die Logs schreiben, sind Sie selbst verantwortlich, ohne Hinweise von Boot. Genau in dieser Form ist der Actuator nützlich: nicht als offenes Schaufenster der Einstellungen, sondern als präzises Werkzeug zur punktuellen Diagnose – von den Bedingungen der Auto‑Configuration bis zur Routenkarte, zum Start der Anwendung und zu eigenen Health‑Checks.
8. Typische Fehler bei der Konfiguration des Actuators
Fehler Nr. 1: env und configprops „vorübergehend“ öffnen und dann vergessen.
Das ist die häufigste Geschichte. Der Dienst lässt sich bequem diagnostizieren, alles super… bis Sie dieselbe Konfiguration auf den gemeinsamen Dev‑Server ausrollen oder sie im Repository lassen. Das vermeidet man durch Profil‑Disziplin: Erweiterte Endpunkte nur in local/dev, in prod ein minimales Set. Noch besser: temporäre Overrides über Startargumente anstatt Bearbeitung von YAML.
Fehler Nr. 2: Sanitization als „wenn der Endpunkt geöffnet ist, ist alles sicher“ wahrnehmen.
Die Maske ****** verbirgt den Wert, aber nicht den Schlüssel selbst, sein Vorhandensein und seine Quelle. Manchmal reicht das bereits, um zu verstehen, dass Sie z. B. Variablen mit Token haben, woher sie kommen und wie sie heißen. Sanitization ist eine Absicherung für den Fehlerfall, keine Lizenz zur gedankenlosen Offenlegung von Diagnoseinformationen.
Fehler Nr. 3: „Exposure“ und show-values verwechseln.
include/exclude beantwortet die Frage „Ist der Endpunkt überhaupt per HTTP erreichbar?“, während show-values beantwortet „Was zeigt er innerhalb der Antwort an?“. Wenn der Endpunkt geöffnet ist, aber die Werte verborgen sind, ist es immer noch ein Endpunkt. Und umgekehrt: Wenn Sie den Endpunkt in prod nicht geöffnet haben, spielt show-values dort kaum eine Rolle, weil ihn niemand per HTTP sehen kann.
Fehler Nr. 4: Darauf hoffen, dass ein separater management.server.port das Sicherheitsproblem von selbst löst.
Ein separater Port ist eine bequeme Trennung, aber kein Schutz. Wenn der Port erreichbar ist, ist der Endpunkt erreichbar. In einem Lehrprojekt bauen wir keine Netzwerkregeln auf und binden keine Sicherheit ein, daher betrachten Sie den Management‑Port als „bessere Organisation“ und „weniger Verwirrung“, nicht als „jetzt ist alles geschützt“.
Fehler Nr. 5: Das gesamte @ConfigurationProperties oder die gesamte Environment zu Debug‑Zwecken loggen.
Das ist eine typische Gewohnheit aus System.out.println: „Lass uns alles ausgeben“. In einem Boot‑Service wird das schnell zu einem Leckrisiko und einer Unmenge an Rauschen. Viel nützlicher ist es, nur das zu loggen, was wirklich für die Diagnose benötigt wird: Anzahlen, aktive Profile, Modi, Schlüsselflags. Alles andere zum Ansehen „wie es ist“ haben Sie bereits über den Actuator – und zwar mit der Möglichkeit, Werte zu maskieren.
GO TO FULL VERSION