LinkMesh

Doku, Blog und Changelog durchsuchen

Die Dokumentation ist nur auf Englisch verfügbar.

ENDE
Eine unversehrte Draht-Blei-Plombe durch den geschlossenen Riegel eines Schranks gecrimpt
LinkMeshObservability Data Collection Management
OpenTelemetryCompliance

Secrets in der Collector-Konfiguration

Halten Sie Credentials aus der Konfiguration heraus, die Sie committen.

linkmesh.io
10 Min. Lesezeit

Jede nützliche OpenTelemetry-Collector-Konfiguration braucht irgendwann ein Secret. In dem Moment, in dem Sie einen Exporter hinzufügen, brauchen Sie ein Credential, um sich gegenüber dem Ziel zu authentifizieren: einen Datadog-API-Key, ein Splunk-HEC-Token, einen Grafana-Cloud-Benutzernamen und ein Access-Policy-Token, ein Client-Zertifikat für mTLS zu Ihrem eigenen Gateway. Die Collector-Konfiguration ist nur YAML, und der naheliegende Ort für diesen Key ist direkt dort neben dem Exporter, der ihn verwendet – was genau der Fehler ist.

Das Problem ist nicht, dass der Collector kein Secret halten kann. Es ist, dass Collector-Konfiguration in die Versionskontrolle gehört, und die Versionskontrolle ist der eine Ort, an dem ein Klartext-Credential niemals landen darf. In diesem Beitrag geht es darum, diese Spannung aufzulösen: wie Sie Secrets aus dem YAML heraushalten, das Sie committen, während Sie dem laufenden Collector dennoch die Credentials übergeben, die er braucht, wie Sie sie ohne Ausfallzeit rotieren und wo die ehrlichen Grenzen der eingebauten Handhabung des Collectors liegen.

Für wen dieser Leitfaden ist

Platform-Engineers, SREs und Security-Teams, die OpenTelemetry Collectors betreiben und ihre Konfiguration in Git speichern. Wenn Sie je einen API-Key in einen exporters:-Block eingefügt und dann vor git commit gezögert haben, ist das hier für Sie. Voraussetzungen: ein Collector-Fleet (oder ein Plan dafür) und Zugriff auf den Secret-Store, den Ihre Plattform bereits verwendet.

Wo Secrets in einer Collector-Konfiguration auftauchen

Secrets sammeln sich an ein paar vorhersehbaren Stellen, fast alle in Exportern und Receivern, die mit etwas Authentifiziertem sprechen:

  • Backend-API-Keys und -Tokens – Datadog api::key, Splunk-HEC-token, ein otlphttp-Exporter mit einem Bearer Token in headers, Grafana-Cloud-Basic-Auth.
  • TLS-Material – Client-Zertifikate und Private Keys für mTLS, CA-Bundles, die Pfade tls::key_file und tls::cert_file sowohl auf Exportern als auch auf Receivern.
  • Upstream-Credentials – ein Kafka-Receiver oder -Exporter mit SASL-Benutzernamen und -Passwort, ein Prometheus-Remote-Write-Endpunkt hinter Auth.

Jedes davon ist ein Wert, der Zugriff auf ein System gewährt. Die Konfiguration, die auf sie verweist, ist etwas, das Sie in Git haben wollen – reviewt, versioniert, diffbar – aus all den Gründen, aus denen jedes Infrastructure-as-Code-Artefakt dorthin gehört. Das ist die gesamte GitOps-Spannung in einem Satz: die Konfiguration muss committet werden; die Secrets dürfen es nicht.

Die Regel: niemals hardcoden, niemals committen

Ein Secret in committetes YAML zu hardcoden, ist schlimmer, als es aussieht, denn Git vergisst nicht. Sobald ein Key in einem Commit landet, ist er in der Historie auf jedem Clone, jedem Fork, jedem CI-Cache und jedem Entwickler-Laptop – und ihn aus der aktuellen Datei herauszurotieren, ändert nichts an den zwanzig Stellen, an denen der alte Wert noch lebt. Ein geleaktes Credential in der Git-Historie ist ein Alles-rotieren-Incident, kein git revert.

Die Disziplin ist also absolut: Die committete Konfiguration enthält niemals ein Klartext-Secret. Was sie stattdessen enthält, ist eine Referenz – ein Platzhalter, der in dem Moment, in dem der Collector startet, aus einer Quelle, die nicht Ihr Git-Repository ist, zum echten Wert aufgelöst wird.

Konfiguration in Git key: ${env:DD_API_KEY} nur Referenz kein Klartext Umgebungsvariable K8s Secret / Vault Laufender Collector löst beim Start auf echter Wert im Speicher Committetes Artefakt Secret-Quelle (nicht Git) Nur Prozess

Umgebungsvariablen-Substitution: das eingebaute Primitiv

Der native Mechanismus des Collectors dafür ist Substitution. Überall in der Konfiguration können Sie ${env:VAR_NAME} schreiben, und der Collector expandiert es zur Ladezeit aus der Prozessumgebung – und dieselbe Maschinerie löst ${file:/path/to/secret} aus einer gemounteten Datei auf, was sich natürlich mit Kubernetes Secrets oder einem Vault-CSI-Volume paart. Ihr committetes YAML hält nur den Platzhalter:

exporters:
  datadog:
    api:
      key: ${env:DD_API_KEY}
  otlphttp/vendor:
    endpoint: https://otlp.vendor.example
    headers:
      authorization: ${env:VENDOR_BEARER_TOKEN}

Der echte DD_API_KEY taucht niemals in der Datei auf. Er muss in der Umgebung des Collectors vorhanden sein, wenn der Prozess startet, und dort erledigt der Rest der Plattform die Arbeit. Das ist bewusst grundlegend – es ist String-Substitution, kein Secrets-System. Der Collector vertraut darauf, dass alles, was die Umgebungsvariable gesetzt hat, dies sicher getan hat, und bietet selbst nichts für Speicherung, Rotation oder Zugriffskontrolle. Diese Ehrlichkeit zählt: Sie komponieren den Collector mit einem echten Secret-Manager; Sie verlassen sich nicht darauf, dass der Collector einer ist.

Kubernetes Secrets als Umgebungsvariablen

Auf Kubernetes ist das gängige Muster, das Credential in einem Secret zu speichern und es als Umgebungsvariable in den Collector-Container zu projizieren, die die ${env:...}-Syntax dann aufgreift:

env:
  - name: DD_API_KEY
    valueFrom:
      secretKeyRef:
        name: otel-backend-credentials
        key: dd-api-key

Nun sagt die Konfiguration in Git ${env:DD_API_KEY}, das Deployment verweist per Name auf ein Secret, und der tatsächliche Wert lebt nur im Kubernetes Secret – verwaltet mit seinem eigenen RBAC, idealerweise durch Encryption at Rest abgesichert. Seien Sie klarsichtig über die Grenzen: Ein Standard-Kubernetes-Secret ist Base64-kodiert, nicht verschlüsselt, und jeder, der Secrets im Namespace lesen kann, kann es lesen. Aktivieren Sie Encryption at Rest, sperren Sie RBAC ab und greifen Sie für stärkere Garantien zu einem externen Manager.

Externe Secret-Manager und Injection

Für echte Rotation, Auditing und Least-Privilege-Zugriff lebt das Credential in einem dedizierten Secret-Manager – HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault – und wird zur Deploy-Zeit in die Umgebung des Collectors injiziert. Zwei gängige Formen:

  • Sync in ein Kubernetes Secret. Tools wie der External Secrets Operator ziehen aus Vault oder einem Cloud-Manager und materialisieren ein Kubernetes Secret, das Sie dann genau wie oben als Umgebungsvariablen mounten. Die Source of Truth ist Vault; das Secret ist eine kurzlebige Projektion.
  • Sidecar-/Agent-Injection. Ein Vault Agent (oder CSI-Treiber) holt das Secret und schreibt es in eine gemountete Datei (die die Konfiguration direkt mit ${file:...} lesen kann) oder in die Prozessumgebung, sodass es etcd überhaupt nicht berührt.

So oder so liest der Collector weiterhin nur ${env:DD_API_KEY} – die Raffinesse liegt vollständig darin, wie diese Variable befüllt wurde. Der Collector weiss nicht und kümmert sich nicht darum, ob der Wert von einem statischen Secret oder einem frisch geprägten Vault-Lease kam, was genau der Grund ist, warum diese Komposition sauber ist.

Secrets ohne Ausfallzeit rotieren

Rotation ist der Ort, an dem hardgecodete Secrets am meisten wehtun und referenzierte Secrets glänzen. Weil der Collector sein Credential beim Start aus der Umgebung liest, ist Rotation: Aktualisieren Sie den Wert im Secret-Manager, dann starten Sie den Collector neu, damit er den neuen aufgreift. Ein paar praktische Hinweise:

  • Rollen, nicht stoppen. In Kubernetes ersetzt ein Rolling Restart des Collector-Deployments oder -DaemonSets die Pods einen nach dem anderen, sodass die Telemetrie weiterfliesst, während jeder Pod den rotierten Wert aufgreift. Nichts geht dunkel.
  • Alt und neu überlappen. Wenn das Backend zwei aktive Keys unterstützt, fügen Sie den neuen Key hinzu, bevor Sie den alten widerrufen. Rollen Sie das Fleet auf den neuen Wert, bestätigen Sie, dass die Exporte erfolgreich sind, und ziehen Sie dann den alten Key aus dem Verkehr. Das vermeidet das Zeitfenster, in dem ein halb ausgerolltes Fleet sich mit einem Key authentifiziert, den Sie bereits abgeschaltet haben.
  • Die Konfiguration ändert sich nie. Das committete YAML sagt durch die gesamte Rotation hindurch weiterhin ${env:DD_API_KEY}. Das ist der Punkt – Rotation berührt den Secret-Store, nicht die versionierte Konfiguration, also hinterlässt sie keinen Diff und braucht kein Config-Review.

Der Collector reloaded Umgebungsvariablen nicht mitten im Prozess, also rechne damit, dass ein Neustart Teil jeder Rotation ist. Das ist eine milde Einschränkung, keine echte, wenn man bedenkt, dass Rolling Restarts günstig sind.

Wo eine Control Plane hineinpasst

Das über ein Fleet hinweg von Hand zu verwalten, ist machbar, aber fummelig, und es ist leicht, dass die Konfiguration eines Nodes dahin driftet, einen literalen Key zu halten, „nur um etwas zu testen”. Hier verdient sich eine Control Plane ihren Platz: Mit LinkMesh geben Sie ein Backend-Credential einmal, zentral ein, auf dem Ziel, das es verwendet – niemand fügt einen Key von Hand in das YAML auf einem einzelnen Node ein. Rotation hört auf, eine fleet-weite Schnitzeljagd zu sein: Aktualisieren Sie das Credential auf dem Ziel, und jeder Collector, der an dieses Backend ausliefert, erhält den neuen Wert automatisch, ohne Änderung pro Node.

Ein LinkMesh-Zielformular: Das API-Token-Feld referenziert ein gespeichertes Tresor-Secret per Name aus einer Auswahlliste („Select a secret…“), statt ein eingefügtes Credential entgegenzunehmen – der Wert liegt verschlüsselt im Tresor, und die committete Konfiguration trägt nur die Referenz.

Der Teil, der für die Spannung zählt, um die es in diesem Beitrag geht: LinkMesh macht denselben Trick «Referenz statt Wert» wie ${env:...}, aber durchgängig und serverseitig. Ein Credential liegt als benanntes Secret in einem verschlüsselten Speicher (AES-256-GCM, der Schlüssel gehört Ihnen und verlässt nie die Grenze Ihrer Datenbank), und die Ziel-Konfiguration referenziert es per Name – ${secret:grafana-cloud-token}. Diese Referenz ist das, was in Git committet, von der API zurückgegeben und in einem Diff angezeigt wird; der Klartextwert ist nie eines davon. Der echte Wert wird erst bei der Auslieferung eingesetzt – in dem Moment, in dem die Konfiguration über den authentifizierten Kanal an einen Collector gepusht wird –, sodass der laufende Collector genau die Bytes erhält, die er braucht, während Ihr GitOps-Repo, Ihre Konfigurationshistorie und ein PVC-Backup nichts als die Referenz enthalten. Rotieren Sie das Secret einmal, und LinkMesh rendert die Konfiguration neu und liefert sie an jeden Collector aus, der es referenziert.

Das bewahrt die Eigenschaften, die Sie tatsächlich wollen: Jede Konfigurationsänderung ist versioniert und auditierbar (das Modell hinter GitOps für Collector-Konfiguration), der Zugriff auf die verwaltete Konfiguration ist berechtigungsgesteuert, Credentials werden auf jedem Lesepfad geschwärzt und – weil LinkMesh selbst gehostet ist und Pipeline-Telemetrie niemals durch es fliesst – bleiben Ihre Daten und Ihre Credentials auf Ihrer eigenen Infrastruktur. Es komponiert mit demselben Enforcement-Modell, das jede andere Policy über das Fleet hinweg konsistent hält.

Wo anfangen

Sie brauchen Vault nicht am ersten Tag. Die Progression, die funktioniert:

  1. Hören Sie jetzt auf, Secrets zu committen. Ersetzen Sie jedes literale Credential in Ihrer Konfiguration durch ${env:VAR} und verschieben Sie den Wert in die Umgebung. Dann scrubben Sie die Git-Historie, falls bereits etwas geleakt ist.
  2. Sichern Sie es mit Kubernetes Secrets ab (Encryption at Rest an, RBAC eng), sodass der Wert ein verwaltetes Zuhause hat, kein Shell-Export.
  3. Steigen Sie auf einen externen Manager um – Vault oder einen Cloud-Secret-Manager mit Injection –, wenn Sie Rotation, Auditing und Least-Privilege-Zugriff brauchen.
  4. Referenzieren Sie zentral per Name, sodass die committete Konfiguration keines Nodes je Klartext hält und Rotation den Secret-Store berührt statt der Pipeline.

Die eigene Secret-Handhabung des Collectors ist bewusst dünn: ${env:...}- und ${file:...}-Substitution und nichts weiter. Das ist in Ordnung, solange Sie daran denken, dass es ein Referenz-Mechanismus ist, und einen echten Secret-Manager dahinterstellen. Halten Sie den Klartext aus Git heraus, lösen Sie ihn beim Start auf, rotieren Sie ihn im Store – und Ihre sensibelsten Credentials fahren niemals in der Konfiguration mit, die Ihre Pipeline beschreibt.

Müde davon, API-Keys von Hand in per-Node-YAML einzufügen?

LinkMesh speichert ein Backend-Credential einmal als benanntes, verschlüsseltes Secret und referenziert es in der Konfiguration per Name – so enthalten Ihr committetes YAML, Ihre Diffs und Ihre Historie nur die Referenz, nie den Key. Der echte Wert wird bei der Auslieferung eingesetzt; einmal rotieren, und das gesamte Fleet erhält die Konfiguration neu. Selbst gehostet, sodass Ihre Telemetrie und Ihre Credentials auf Ihrer eigenen Infrastruktur bleiben. Stellen Sie eine auf in Minuten, oder sehen Sie sich an, was sie leistet.