Vault nach OpenBao: die Tür ist offen, nur schmaler als sie aussieht

18.08.2026

Thema: alle Beiträge zu OpenBao

Die OpenBao-Doku sagt, ab Vault 1.15.0 geht der Umstieg nicht mehr. Gemessen geht er, auch von Vault 2.0.3. Die echte Grenze sind Mounts ohne Plugin.

Inhaltsverzeichnis

Ein Nebensatz in der Migrationsanleitung

Es gibt einen vollständig offenen Fork von HashiCorp Vault. Er heißt OpenBao, liegt bei der Linux Foundation, steht unter MPL 2.0 und ist produktionsreif.

In der Migrationsanleitung dieses Forks stand bis vor kurzem ein Satz, der die gute Nachricht fast wieder einkassiert:

This guide will not work on Vault versions 1.15.0 and newer; use at your own risk.

Vault 1.15.0 ist von September 2023. Wer den Satz beim Wort nimmt, liest daraus: Für deine Installation ist der Fork nicht erreichbar. Praktisch jede Vault, die heute produktiv läuft, ist neuer.

Ich habe es nachgemessen. Die Migration funktioniert, auch von einem aktuellen Vault 2.0.3 aus. Das ist die gute Nachricht, und sie ist zugleich die gefährliche. Denn sie funktioniert nicht vollständig, und es sagt dir niemand, was fehlt.

Alle Messungen unten laufen in Containern gegen hashicorp/vault:1.14.1, 1.15.6 und 2.0.3 sowie openbao/openbao:2.6.1, Raft-Storage, Shamir-Unseal, ein Knoten. Die Registry-Digests der Images liegen mit im Repo, denn ein Tag taugt nicht als Beleg, er kann neu geschoben werden.

Die Zeitleiste in vier Daten

August 2023: HashiCorp wechselt für alle Produkte die Lizenz, weg von MPL 2.0, hin zur Business Source License 1.1. Die BUSL ist von der Open Source Initiative nicht anerkannt. Zwei Zahlen tragen den Rest dieses Beitrags: Vault 1.14.8 ist die letzte Version unter MPL, 1.15.0 die erste unter BUSL.

Dezember 2023: OpenBao startet als Fork, unter der Linux Foundation, weiter MPL 2.0. Es ist derselbe Test wie bei OpenTofu, den ich in Wer liefert deine Provider? beschrieben habe: Die Frage ist nicht nur, welche Lizenz draufsteht, sondern wer den Stift hält. Bei einer Stiftung hält ihn niemand mehr allein.

Februar 2025: IBM schließt die Übernahme von HashiCorp ab.

April 2026: Vault springt von 1.21 auf 2.0 und damit in den IBM-Lebenszyklus. Daran zählt hier nur eines: Zwischen dem Fork-Punkt und dem heutigen Vault liegen acht Releases Storage-Entwicklung.

Der Fork ist offen, die Lizenz ist geklärt. Reicht das, um hinüberzukommen? Die Antwort steht nicht im Lizenztext. Sie liegt im Store.

Was im Store wirklich liegt

Vault 1.14.1, ein Secret rein, damit es echte Daten sind:

vault kv put secret/produktion/datenbank benutzer=app passwort=s3hr-geheim

Und jetzt der Blick von außen. Bis hierhin ging alles über die Vault-CLI, mit einem Token. Ab jetzt nicht mehr. Im Datenverzeichnis liegt am Ende genau eine Datei:

$ ls -l data/base
drwx------ 3 chris chris     4096 Aug  6 09:23 raft
-rw------- 1 chris chris 16801792 Aug  6 09:23 vault.db

Sechzehn Megabyte, binär. Da sehe ich direkt hinein, ohne Vault zu fragen. Kein Token, kein Unseal, der Server dürfte dabei sogar aus sein. strings holt aus einer Binärdatei alles heraus, was wie lesbarer Text aussieht:

$ strings vault.db | grep -E '^(core|logical|sys)/' | sort -u
core/mounts
core/seal-config{"type":"shamir","secret_shares":3,"secret_threshold":2,...}
core/shamir-kek
core/versions/1.14.1
logical/a3540101-.../6fcf2b61-.../versions/6f8/52de2f966aed3b95d82db9d249ead9db...
sys/policy/default

Die Struktur liegt offen da: die Mount-Tabelle, die Seal-Konfiguration, die Policies. Und mittendrin die Zeile, um die es geht.

$ strings vault.db | grep -o 'core/versions/[0-9.]*' | sort -u
core/versions/1.14.1

Der Store führt Buch darüber, welche Version ihn angefasst hat, im Klartext. Diese Zeile kommt gleich wieder.

Damit hier kein falscher Eindruck entsteht, die Gegenprobe. Ich suche in derselben Datei den Pfad meines Secrets und das Passwort:

$ strings vault.db | grep -c produktion
0
$ grep -c s3hr-geheim vault.db
0

Beides null. Der Pfad ist gesalzen und gehasht, das macht KV Version 2. Das Passwort ist verschlüsselt. Damit ist die Ebene benannt, auf der wir uns bewegen: Vault ist nicht der Ort, an dem deine Secrets liegen. Vault ist der Prozess, der den Schlüssel hat. Der Ort ist ein stinknormaler Key-Value-Store, eine Datei auf einer Platte.

Die Schlüsselkette

Damit klar wird, warum der Prozesstausch gleich funktioniert, drei Stufen von außen nach innen.

Die Unseal-Keys in deiner Hand sind Anteile eines Schlüssels, aufgeteilt nach dem Shamir-Verfahren. Mit genug Anteilen bekommst du den Root Key. Der Root Key entschlüsselt den Keyring. Und im Keyring liegen die Schlüssel, mit denen die eigentlichen Daten verschlüsselt sind.

Jetzt die entscheidende Stelle: Der Store auf der Platte enthält all das, auch den verschlüsselten Root Key. Was er nicht enthält, ist der Anteil in deiner Hand. Deshalb ist ein versiegelter Vault nach dem Neustart erst einmal ein Haufen unlesbarer Bytes, und deshalb muss jemand aufschließen.

Daraus folgt die Möglichkeit, um die es hier geht: Wenn ein anderes Programm dieselbe Schlüsselkette implementiert und dieselben Strukturen kennt, kann es denselben Store aufschließen.

Der Prozesstausch

Vault stoppen, OpenBao auf demselben Datenverzeichnis starten, bao server statt vault server. Die Konfigurationsdatei unterscheidet sich in genau einer Zeile: disable_mlock gibt es in OpenBao nicht mehr.

$ bao status
Seal Type          shamir
Initialized        true
Sealed             true
Total Shares       3
Threshold          2
Version            2.6.1
Storage Type       raft

Versiegelt, wie erwartet. Jetzt entsiegeln, mit denselben Shamir-Anteilen, die Vault vor dem Tausch erzeugt hat:

$ bao operator unseal <Anteil 1>
$ bao operator unseal <Anteil 2>
Sealed          false
Version         2.6.1
Cluster Name    vault-cluster-574404b9

Sealed false, und der Cluster heißt weiter vault-cluster. Den Namen hat OpenBao nicht angefasst.

$ bao kv get secret/produktion/datenbank
benutzer    app
passwort    s3hr-geheim

Dasselbe Secret. Das allein wäre noch kein Beweis, das könnte auch eine gute Kopie sein. Der Beweis ist die Versionshistorie:

$ bao version-history
Version  Installation Time     Commit Date
-------  -----------------     -----------
1.14.1   2026-08-06T07:23:45Z  2023-07-21T10:15:14Z
2.6.1    2026-08-06T07:24:53Z  2026-07-22T14:22:20Z

OpenBao führt in seiner eigenen Versionshistorie die Vault-Version, die vorher auf demselben Store lief. Da wurde nichts kopiert, das ist derselbe Store mit einem neuen Prozess davor. Von außen sieht man dasselbe, die Versions-Zeile von oben hat jetzt zwei Einträge:

$ strings vault.db | grep -o 'core/versions/[^ ]*' | sort -u
core/versions/1.14.1
core/versions/2.6.1

Kein Export, kein Import, kein Wartungsfenster. Ein Prozess geht, ein anderer kommt. Das ist ein Fork, der noch nah genug am Ursprung ist.

Die dokumentierte Grenze, nachgemessen

Jetzt die Behauptung, wegen der ich überhaupt gemessen habe. Derselbe Ablauf, aber der Store wurde vorher einmal von einem neueren Vault hochgefahren. Ich nehme gleich den härtesten Fall, Vault 2.0.3:

$ bao operator unseal <dieselben zwei Anteile>
$ bao kv get secret/produktion/datenbank
passwort    s3hr-geheim

$ bao version-history
Version  Installation Time     Commit Date
-------  -----------------     -----------
1.14.1   2026-08-03T20:59:46Z  2023-07-21T10:15:14Z
2.0.3    2026-08-03T21:06:07Z  2026-06-17T12:39:45Z
2.6.1    2026-08-03T21:06:10Z  2026-07-22T14:22:20Z

Es funktioniert. Entsiegelt, Secret da, drei Versionen auf einem Store. Dieselbe Messung mit Vault 1.15.6, der ersten Version jenseits der Grenze, liefert dasselbe Bild.

Ehrlich dazugesagt: 1.15.0 ist zugleich das erste Release unter BUSL. Kein Zufall, dass die Grenze genau dort gezogen wurde.

Und die Einordnung, die hier wichtiger ist als sonst: Das widerlegt die Dokumentation nicht, es zeigt, dass ihre Begründung zu grob ist. Mein Store im Labor ist klein. Wer Auto-Unseal über einen Cloud-Dienst fährt oder ein halbes Dutzend Engines betreibt, misst womöglich etwas anderes.

Damit ist die nächste Frage die richtige. Nicht: geht es? Sondern: was geht denn dann nicht mit?

Wo die Grenze wirklich liegt

Zwei Begriffe vorweg. Eine Secret Engine ist das, was ein Geheimnis ausgibt: kv legt Key-Values ab, pki stellt Zertifikate aus, aws erzeugt auf Zuruf einen AWS-Zugang. Eine Auth-Methode ist der umgekehrte Weg, sie entscheidet, womit sich ein Client ausweist, bevor er Secrets abfragen darf. Beides sind Plugins.

Und hier liegt der Unterschied zwischen Original und Fork:

  Vault 2.0.3 OpenBao 2.6.1
Secret Engines, eingebaut 19 9
Auth-Methoden, eingebaut 18 9

Schau nicht auf die Anzahl, schau auf die Namen. Was auf der OpenBao-Seite fehlt, heißt aws, azure, gcp, alicloud, oci, okta. Alles Anbindungen an genau einen Hersteller. Der Fork hat aufgeräumt und das Herstellerspezifische in ein eigenes Repository ausgelagert. Er hat auch ergänzt: Bei den Datenbank-Plugins bringt OpenBao eines für valkey mit, das es bei Vault nicht gibt. Womit die Serie sich selbst die Hand gibt, siehe Redis 7.4 in Kubernetes.

Jetzt der Versuch. In Vault eine Secret Engine aktivieren, die OpenBao nicht hat:

$ vault secrets enable aws
Success! Enabled the aws secrets engine at: aws/

Und dann derselbe Prozesstausch. Der Beweis besteht aus zwei Bildern. Das erste ist das Log von OpenBao beim Start:

[ERROR] core: failed to create mount entry: path=aws/ error="plugin not found in the catalog: aws"
[WARN]  core: skipping plugin-based mount entry: path=aws/
[INFO]  core: successfully mounted: type=aws version="v2.0.3+builtin.vault" path=aws/
[ERROR] core: skipping initialization for nil backend: path=aws/ type=aws version="v2.0.3+builtin.vault"

Vier Zeilen zum selben Pfad, darunter ein successfully mounted. Der Start bricht nicht ab. Das zweite Bild ist der laufende Betrieb, also das, was du siehst, wenn du nicht zufällig ins Log guckst:

$ bao secrets list
Path               Type              Accessor                   Description
----               ----              --------                   -----------
agent-registry/    agent_registry    agent-registry_983c72fe    agent registry
aws/               aws               aws_62e3be16               n/a
cubbyhole/         cubbyhole         cubbyhole_facb447e         per-token private secret storage
secret/            kv                kv_5cb6679f                n/a

$ bao list aws
No value found at aws

Das ist derselbe Satz, den auch ein leerer Pfad liefert. Kein Fehler, keine Warnung, keine Spur davon, dass hier etwas fehlt, statt einfach leer zu sein.

Ehrlicher wird die Antwort erst, wenn man schreibt statt liest. Das steht so in keiner Anleitung, ich habe es beim Nachmessen gefunden:

$ bao write aws/config/root access_key=dummy secret_key=dummy region=eu-central-1
Code: 404. Errors:
* no handler for route "aws/config/root". route entry found, but backend is nil.

route entry found, but backend is nil ist die genaue Beschreibung des Zustands. Nur sieht sie niemand, der den Bestand mit list oder read inventarisiert, und genau das tut man beim Umzug.

Der Mount, den niemand aktiviert hat

Schau noch einmal auf die Mount-Liste oben, auf die erste Zeile: agent-registry.

Diesen Mount hat niemand aktiviert. Vault 2.0 legt ihn selbst an, sobald es einen Store hochfährt. Dahinter steckt die neue Agenten-Funktion, eine Registratur, die KI-Agenten als eigene Identitäten mit eigenen Rechten führt. Ob du sie je benutzt, spielt keine Rolle, der Mount ist trotzdem da. Und dieses Plugin gibt es in OpenBao nicht:

[ERROR] core: failed to create mount entry: path=agent-registry/ error="plugin not found in the catalog: agent_registry"
[WARN]  core: skipping plugin-based mount entry: path=agent-registry/

Wer heute von einem aktuellen Vault kommt, hat also mindestens einen Eintrag im Store, den OpenBao stumm überspringt, ohne je eine Entscheidung dazu getroffen zu haben.

Kein Vorwurf an OpenBao, ein Fork kann nur mitnehmen, was er hat. Und es ist reparabel, bao secrets disable räumt den toten Eintrag weg. Man muss nur wissen, dass er da ist.

Und die Rückbindung an den Redis-Fall: Dort war der Bruch laut. Eine Fehlermeldung, ein Pod im CrashLoopBackOff, man kam gar nicht daran vorbei. Hier ist er still. Die laute Variante findet jeder. Die stille ist die gefährlichere.

Was der Doku-Fix ans Licht gebracht hat

Diesen Befund habe ich als Doku-Issue an OpenBao zurückgegeben, openbao/openbao#3707. Ein Maintainer hat ihn bestätigt und um einen Pull Request gebeten, daraus wurde openbao/openbao#3710.

Der Pull Request tauscht die pauschale Versionswarnung gegen das, was tatsächlich getestet ist, und ergänzt im Plugin-Abschnitt eine Inventur vor dem Umzug samt Fehlerbild. Ein Maintainer hat am Wortlaut selbst mitgeschrieben, zwei seiner Vorschläge sind eingearbeitet.

Beim dritten wurde es interessant. Der Vorschlag lautete, auch den Reparaturweg zu beschreiben: das fehlende Plugin aus dem Plugin-Repository nachinstallieren und den toten Mount darauf umbiegen. Klingt gut. Ich habe es nachgemessen, bevor ich es in eine offizielle Anleitung schreibe, und in dieser Form funktioniert es nicht.

Zuerst fehlt ein Schritt. Ohne bao plugin register scheitert schon der erste Befehl:

$ bao secrets tune -plugin-version=v0.3.1 aws
Code: 400. Errors:
* plugin not found in the catalog: aws, version=v0.3.1

Mit registriertem Plugin läuft der Reload dann durch und meldet Erfolg. Danach ist der Mount aber nicht lebendig, sondern kaputt:

$ bao plugin reload -mounts=aws
Success! Reloaded mounts: [aws]

$ bao write aws/config/root access_key=dummy secret_key=dummy region=eu-central-1
Code: 500. Errors:
* cannot write to storage during setup

Vor dem Reload war der Mount sauber tot, ein 404. Danach ist er halb lebendig, ein 500, stabil über mehrere Versuche. Im Trace-Log sieht man, dass der Core sauber durchläuft: Das Plugin startet, setup und initialize enden beide mit err=<nil>, dann kommt successfully reloaded plugin. Der Fehler entsteht erst im Plugin selbst, dessen Storage-View offenbar dauerhaft in dem Read-only-Modus hängt, den der Core während des Setups anlegt.

Was tatsächlich hilft, ist ein Neustart des Servers. Danach kommt der Mount als externes Plugin hoch und funktioniert vollständig.

Das Trace-Log ist im Pull Request nachgereicht. Am selben Abend hat ein zweiter Maintainer den Pull Request auf blocked gesetzt. Der Doku-Fix hängt jetzt also an einem Fehler im Produkt, den er selbst sichtbar gemacht hat.

Und genau das ist der Teil, den man nur bei offener Software sehen kann. Nicht, dass es keine Fehler gäbe. Sondern dass du sie finden, belegen und öffentlich in die Warteschlange legen kannst, Zeile für Zeile nachlesbar, statt ein Support-Ticket zu schreiben und zu warten.

Inventur statt Datenmigration

Nehmen wir den Normalfall: eine Vault mit Mounts, die es in OpenBao nicht gibt. Was macht man dann?

Man kopiert nicht, man macht Inventur. Was portabel ist, steht in drei Befehlen:

vault secrets list
vault auth list
vault policy list

Secret Engines, Auth-Methoden, Policies. Das ist die Landkarte deiner Installation, und sie ist reiner Text. Die nimmst du überall mit hin.

Nicht portabel sind die Werte, also die gespeicherten Secrets selbst. Und hier kommt der Dreh: Bei allem, was dynamisch erzeugt wird, ist das kein Verlust. Die Database Engine etwa bekommt von dir einmalig einen Admin-Zugang. Fragt eine Anwendung nach Zugangsdaten, erzeugt Vault Benutzernamen und Passwort und legt diesen Benutzer per SQL direkt in der Datenbank an, mit Ablaufdatum. Läuft die Frist ab, löscht Vault den Benutzer wieder, ebenfalls per SQL.

Das dynamische Secret liegt also nie dauerhaft im Store. Ein neuer Store mit demselben Admin-Zugang erzeugt morgen genauso gültige Zugänge wie der alte heute. Übrig bleibt der statische Bestand, und der wird nicht kopiert, der wird rotiert.

Bleibt der ehrliche Rest. Die Landkarte ist lesbar, die Zugangsdaten in der Landkarte nicht:

$ vault read database/config/pg
allowed_roles         [app]
connection_details    map[connection_url:postgresql://{{username}}:{{password}}@lab-pg:5432/... username:postgres]
plugin_name           postgresql-database-plugin

URL, Benutzername, Plugin, alles da. Ein Feld password gibt es in dieser Antwort nicht, auch mit dem Root-Token nicht. Das ist kein Fehler, das ist Absicht, und es verschiebt die Aussage an die richtige Stelle:

Der Lock-in sitzt nicht im Datenformat. Er sitzt in der Zugriffsschicht zu deinen Zielsystemen. Wer diese Werte nicht ohnehin neu beschaffen kann, ist nicht an Vault gebunden, sondern daran, dass niemand mehr weiß, wie diese Zugänge entstanden sind.

Gelöscht ist nicht rotiert

Daraus wird eine Prüffrage, die du heute Abend beantworten kannst: Wenn du deinen Secret-Store morgen ersetzen müsstest, wie viele Werte müsstest du von Hand hinüber retten? Jeder davon ist ein Lock-in, den du selbst gebaut hast.

Die naheliegende Regel wäre jetzt: jedes Secret braucht ein TTL. Die ist falsch, und auch das habe ich gemessen. Ein TTL ist eine Eigenschaft des Zielsystems, nicht des Stores. Räumt KV Version 2 per delete_version_after die eigene Kopie weg, ist das Datenbankpasswort weiter gültig, in der Datenbank ändert sich nichts. Bei einem Database-Lease dagegen ist der Zugang nach Ablauf wirklich weg, weil die Engine die Rolle im Zielsystem löscht.

Die tragfähige Fassung ist deshalb schärfer statt weicher: Kein Secret ohne Besitzer und ohne erprobtes Rotationsverfahren. Das TTL ist die automatisierte Form davon. Wo keins geht, bei Root-Credentials etwa oder beim Schlüssel einer Root-CA, tritt ein dokumentiertes, mindestens einmal durchgeführtes Verfahren mit Termin an seine Stelle. Alles andere ist kein Secret, sondern eine Altlast. Der Vorteil: Diese Fassung ist zählbar.

Die zwei Säulen

Bei einem Secret-Store ist die Lock-in-Frage nicht, ob du deine Werte mitnehmen kannst. Sie lautet, ob du sie wegwerfen kannst. Ein Store, aus dem du nicht herauskommst, weil dein Bestand aus unrotierbaren Langzeit-Geheimnissen besteht, hat dich nicht geschützt, der hat dich eingesperrt. Und der Store ist daran unschuldig, der Bestand ist es.

An diesem Fall liegen die zwei Säulen so eng beieinander wie selten. Die Lizenz hält die Option offen, das ist Säule 2. Das technische Verständnis entscheidet, ob du sie einlöst, das ist Säule 1. Ob der Weg für dich offen ist, steht in deinem Store und nicht in einer Pressemitteilung.

Und damit hier niemand als Missionar rausgeht: OpenBao ist kein Vault-Klon mit besserem Etikett. Es bringt weniger Plugins mit, es entfernt Dinge, es setzt eigene Schwerpunkte. Niemand muss in Panik migrieren. Wer bei Vault bleibt, sollte es nur bewusst tun, und dafür muss er wissen, was auf der OpenBao-Seite fehlt.

Fazit

Das Video zu diesem Beitrag zeigt jede der Messungen im Terminal. Den Spickzettel „Die Vault-Exit-Inventur” mit Zeitleiste, Inventur-Bogen und Cutover-Reihenfolge gibt es über den Newsletter.

© 2026 Trutz Software Consulting GmbH. CC BY 4.0: Dieser Text darf mit Namensnennung weiterverwendet und bearbeitet werden, auch kommerziell. Code-Beispiele darin stehen unter Apache 2.0. Ausgenommen sind Marke und Logo sowie gekennzeichnete Zitate Dritter. Was das genau heißt.

Newsletter

Erhalte praxisnahe Tutorials, Best Practices und echte Lösungen für die häufigsten DevOps-Probleme direkt in Dein Postfach.

Datenschutz: Deine E-Mail-Adresse wird DSGVO-konform in Deutschland gespeichert. Keine Spam, nur relevante Inhalte. Eine E-Mail alle 1 bis 2 Wochen.