Hashicorp Vault
HashiCorp Vault ist ein Tool für den sicheren Zugriff auf Secrets (Passwörter, API‑Keys, Zertifikate etc.) über eine einheitliche Schnittstelle – mit detaillierten Audit‑Logs.
Einige Funktionen von Vault:
- Verschlüsselung
- Erzeugung dynamischer Secrets mit Ablaufzeit
- Erneuerung und Widerruf von Secrets
Vieles davon bieten auch Azure Key Vault und AWS Key Management Service. Vault ist jedoch Open Source und nicht an eine bestimmte Cloud gebunden.
Secrets werden in einem Storage‑Backend gespeichert (RAM, Disk, DB, Azure Blob, AWS S3, …). Vault
stellt die Schnittstelle bereit.
Im anderen Artikel zeige
ich, wie Vault die Key‑Value‑Store des Consul nutzt.
Vault ist in Go geschrieben und kommt als Einzel‑Binary. Man kann per CLI, HTTP‑REST oder Web‑UI arbeiten.
Secrets Engines
Secrets Engines speichern, erzeugen und verschlüsseln Daten.
Manche Engines speichern nur Daten, andere verbinden sich zu Diensten und erzeugen on‑demand Zugangsdaten. Wieder andere bieten „Encryption‑as‑a‑Service“, generieren TOTP und Zertifikate.
Die Vault‑Seite listet knapp 50 Engines (u. a. AWS, Azure, MySQL, PostgreSQL, Active Directory, PKI, SSH).
Engines werden unter einem Path aktiviert. vault path-help zeigt Beschreibung und akzeptierte
Paths.
Lebenszyklus einer Secrets Engine:
- Enable:
vault secrets enable - Disable:
vault secrets disable - Move:
vault secrets move - Tune:
vault secrets tune
KV Secrets Engine
Die KV‑Engine speichert Key‑Value‑Secrets.
Es gibt zwei Versionen:
- KV v1:
- Kein Versioning
- Gelöschte Secrets sind nicht wiederherstellbar
- Bessere Performance
- KV v2:
- Versioning (Standard 10 Versionen)
- Soft Delete
- Geringere Performance
Lebenszyklus von Secrets
- Create:
kv put - Read:
kv get - Update:
kv put - Delete/Undelete:
kv delete/kv undelete - Destroy:
kv destroy - Metadata Destroy:
kv metadata delete
Authentifizierungsmethoden
Vault unterstützt viele Auth‑Methoden, z. B. LDAP, Azure Active Directory und AWS IAM.
- Aktive Methoden auflisten:
vault auth list - Methode aktivieren:
vault auth enable [methode] - Methode konfigurieren:
vault write auth/[methode]/config - Rolle hinzufügen:
vault write auth/[methode]/role/[rollenname] - Methode deaktivieren:
vault auth disable [methode]
Zugriffsrichtlinien
Eine Vault‑Policy (Vault Policy) ist ein Dokument in HCL oder JSON, das fein granular regelt, wer was wo darf.
Man kann sehr genau steuern, welche Kommandos an welchen Paths erlaubt sind.
Die Doku hat viele Beispiele.
- Policies auflisten:
vault policy list - Policy erstellen:
vault policy write [policy] [policy_file.hcl] - Policy aktualisieren:
vault write sys/policy/[policy]/ policy=[policy_file.hcl] - Policy löschen:
vault delete sys/policy/[policy]
Dynamische Secrets
Dynamic Secrets werden on‑demand mit Lease erzeugt. Ohne Erneuerung werden sie widerrufen.
Cubbyhole Response Wrapping
Statt einen Token direkt zu geben, legen wir ihn in ein Cubbyhole und erzeugen einen kurzlebigen One‑Time‑Token, der Zugriff auf das Cubbyhole hat – Cubbyhole Response Wrapping.
Vorteile:
- Secret ist nur kurz sichtbar (Lease TTL)
- Leaked Token ist nutzlos (Einmal‑Token)
- Nutzer sieht nicht das Secret, sondern eine Referenz
Auditing
Vault loggt alle Requests und Responses (inkl. Fehler) als JSON. Mehrere Audit‑Devices (Datei, Syslog, Socket) sind möglich.
Ohne log_raw=true werden nur Hashes der Secrets geloggt.
Sicherheit: Vault akzeptiert keine Requests, wenn kein Audit‑Device verfügbar ist.
- Audit‑Device aktivieren:
vault audit enable -path=vault_path [type] - Audit‑Device deaktivieren:
vault audit disable [path] - Audit‑Devices listen:
vault audit list --detailed
Praxis‑Walkthrough
Beispiele zu den obigen Konzepten.
Vault installieren und starten
Über den Paketmanager deiner Distro oder wie im anderen Artikel beschrieben.
# pacman -S vault# cat /etc/vault.hcl backend "file" { path = "/var/lib/vault" } listener "tcp" { tls_disable = 1 }# systemctl start vault.service$ vault operator init Unseal Key 1: p/rCDw28akkqMBogtkpuH7OoB0IbwNK6fOPE1/K56mCw Unseal Key 2: 4Qy/bymi4BShb0JaoMnQm8Qry/MrrM3pP3CQDeJu93s+ Unseal Key 3: +Qdh8mBl1yzqi5V2u1tKG1u3pWtRfXEQ1cwMjoYpuSAy Unseal Key 4: lG6Vl8iiJhUpvmmWF5S8EgKd89wMUHsL4PNHuT4xGZ82 Unseal Key 5: rsufBM3HiobRE39El8ErIkzx+qkxz9unzIrMb/w5gZTB Initial Root Token: s.3iZvNY9lhNdeJVH5fIHU1RDvUmgebungsvariablen mit Adresse und Token setzen
$ export VAULT_ADDR='http://127.0.0.1:8200' $ export VAULT_TOKEN='s.3iZvNY9lhNdeJVH5fIHU1RDv'Wenn Vault in einem Docker‑Container läuft, ist folgender Alias praktisch:
$ alias vault='docker exec -it -e VAULT_TOKEN=$VAULT_TOKEN vault.server1 vault "$@"'Unseal
$ vault operator unseal p/rCDw28akkqMBogtkpuH7OoB0IbwNK6fOPE1/K56mCw … Sealed true Unseal Progress 1/3 …$ vault operator unseal +Qdh8mBl1yzqi5V2u1tKG1u3pWtRfXEQ1cwMjoYpuSAy … Sealed true Unseal Progress 2/3 …$ vault operator unseal rsufBM3HiobRE39El8ErIkzx+qkxz9unzIrMb/w5gZTB Sealed falseLogin
$ vault loginSecrets‑Backend aktivieren
$ vault secrets enable -path=kv -version=2 kv Success! Enabled the kv secrets engine at: kv/Maximal 2 Versionen speichern
$ vault write kv/config max_versions=2 Success! Data written to: kv/configSecret schreiben
$ vault kv put kv/meu_app senha=123456 Key Value --- ----- created_time 2021-04-19T04:11:34.948088026Z deletion_time n/a destroyed false version 1Zweite Version anlegen
$ vault kv put kv/meu_app senha=123456 usuario=julio Key Value --- ----- created_time 2021-04-19T04:11:44.839504165Z deletion_time n/a destroyed false version 2Dritte Version anlegen
$ vault kv put kv/meu_app senha=654321 usuario=julio Key Value --- ----- created_time 2021-04-19T04:11:53.768980035Z deletion_time n/a destroyed false version 3Letzte Version lesen
$ vault kv get kv/meu_app ====== Metadata ====== Key Value --- ----- created_time 2021-04-19T04:11:53.768980035Z deletion_time n/a destroyed false version 3 ===== Data ===== Key Value --- ----- senha 654321 usuario julioZweite Version lesen
$ vault kv get -version=2 kv/meu_app ====== Metadata ====== Key Value --- ----- created_time 2021-04-19T04:11:44.839504165Z deletion_time n/a destroyed false version 2 ===== Data ===== Key Value --- ----- senha 123456 usuario julioErste Version lesen
Diese Version wurde zerstört, da wir nur die letzten zwei Versionen behalten.
$ vault kv get -version=1 kv/meu_app No value found at kv/data/meu_appZweite Version als JSON ausgeben
$ vault kv get -format=json -version=2 kv/meu_app { "request_id": "379bbb04-a21b-cebe-9e99-392b85372592", "lease_id": "", "lease_duration": 0, "renewable": false, "data": { "data": { "senha": "123456", "usuario": "julio" }, "metadata": { "created_time": "2021-04-19T04:11:44.839504165Z", "deletion_time": "", "destroyed": false, "version": 2 } }, "warnings": null }Mit
jqfiltern$ vault kv get -format=json -version=2 kv/meu_app | jq -r .data.data.senha 123456Zweite Version über die API lesen
$ curl -k --header "X-Vault-Token: $VAULT_TOKEN" $VAULT_ADDR/v1/kv/data/meu_app?version=2 | jq .data.data { "senha": "123456", "usuario": "julio" }Versionen 2 und 3 löschen
Wird keine Version angegeben, wird die letzte gelöscht.
$ vault kv delete -versions="2,3" kv/meu_app Success! Data deleted (if it existed) at: kv/meu_app$ vault kv get -version=2 kv/meu_app ====== Metadata ====== Key Value --- ----- created_time 2021-04-19T04:11:44.839504165Z deletion_time 2021-04-19T04:14:27.514456987Z destroyed false version 2$ vault kv get -version=3 kv/meu_app ====== Metadata ====== Key Value --- ----- created_time 2021-04-19T04:11:53.768980035Z deletion_time 2021-04-19T04:14:27.514457229Z destroyed false version 3Version 2 wiederherstellen
$ vault kv undelete -versions=2 kv/meu_app Success! Data written to: kv/undelete/meu_appVersion 2 endgültig zerstören
$ vault kv destroy -versions=2 kv/meu_app Success! Data written to: kv/destroy/meu_app$ vault kv get -version=2 kv/meu_app ====== Metadata ====== Key Value --- ----- created_time 2021-04-19T04:11:44.839504165Z deletion_time n/a destroyed true version 2Alle Metadaten löschen
$ vault kv list kv/ Keys ---- meu_app$ vault kv metadata delete kv/meu_app Success! Data deleted (if it existed) at: kv/metadata/meu_app$ vault kv list kv/ No value found at kv/metadataPolicy „admin“ erstellen
$ cat policy_admin.hcl # Mount secrets engines path "sys/mounts/*" { capabilities = [ "create", "read", "update", "delete", "list" ] } # Configure the database secrets engine and create roles path "database/*" { capabilities = [ "create", "read", "update", "delete", "list" ] } # Manage the leases path "sys/leases/+/database/creds/readonly/*" { capabilities = [ "create", "read", "update", "delete", "list", "sudo" ] } path "sys/leases/+/database/creds/readonly" { capabilities = [ "create", "read", "update", "delete", "list", "sudo" ] } # Write ACL policies path "sys/policies/acl/*" { capabilities = [ "create", "read", "update", "delete", "list" ] } # Manage tokens for verification path "auth/token/create" { capabilities = [ "create", "read", "update", "delete", "list", "sudo" ] }$ vault policy write admin policy_admin.hcl Success! Uploaded policy: adminPolicy „apps“ erstellen
$ cat policy_apps.hcl # Get credentials from the database secrets engine 'readonly' role. path "database/creds/readonly" { capabilities = [ "read" ] }$ vault policy write apps policy_apps.hcl Success! Uploaded policy: appsPolicies listen
$ vault policy list admin apps default rootDocker starten und PostgreSQL‑Image ziehen
# systemctl start docker.service $ docker pull postgres:13Datenbank starten
$ docker run \ --name postgres \ --env POSTGRES_USER=root \ --env POSTGRES_PASSWORD=rootpassword \ --detach \ --publish 5432:5432 \ postgres:13Mit der DB verbinden und eine Rolle
romit Lesezugriff erstellen$ docker exec -it postgres psql root=# CREATE ROLE ro NOINHERIT; CREATE ROLE root=# GRANT SELECT ON ALL TABLES IN SCHEMA public TO "ro"; GRANT root=# \qDatabase‑Engine aktivieren
$ vault secrets enable database Success! Enabled the database secrets engine at: database/Engine für Postgres konfigurieren
$ vault write database/config/postgresql \ plugin_name=postgresql-database-plugin \ connection_url="postgresql://{{username}}:{{password}}@localhost:5432/postgres?sslmode=disable" \ allowed_roles=readonly \ username="root" \ password="rootpassword"SQL zum Erzeugen der Credentials
$ cat readonly.sql CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}' INHERIT; GRANT ro TO "{{name}}";Rolle
readonlyerstellen$ vault write database/roles/readonly \ db_name=postgresql \ creation_statements=@readonly.sql \ default_ttl=1h \ max_ttl=24h Success! Data written to: database/roles/readonlyDB‑Credentials generieren
$ vault read database/creds/readonly Key Value --- ----- lease_id database/creds/readonly/hTFHqyO2AVcCBQRFmRTv8bGV lease_duration 1h lease_renewable true password NMVS-H2YO5BNMAkzP6Nl username v-root-readonly-sKjgJBT41RYrrLtB1MtS-1618807755Postgres‑User listen
$ docker exec -it postgres psql root=# \du List of roles Role name | Attributes | Member of ----------------------------+-------------------------------------------+----------- ro | No inheritance, Cannot login | {} root | Superuser, Create…Replication, Bypass RLS | {} v-root-readonly-sKj…7755 | Password valid until 2021-04-19 05:49:20+00 | {ro} root=# \qLeases listen
$ vault list sys/leases/lookup/database/creds/readonly Keys ---- hTFHqyO2AVcCBQRFmRTv8bGVLease‑ID in Variable speichern
$ LEASE_ID=$(vault list -format=json sys/leases/lookup/database/creds/readonly | jq -r ".[0]")$ echo $LEASE_ID hTFHqyO2AVcCBQRFmRTv8bGVLease verlängern
Wird ein zu hoher Wert angefragt, wird er auf das Maximum gekappt.
$ vault lease renew -increment=3600 database/creds/readonly/$LEASE_ID$ vault lease renew -increment=96400 database/creds/readonly/$LEASE_ID WARNING! The following warnings were returned from Vault: * TTL of "26h46m40s" exceeded the effective max_ttl of "23h33m18s"; TTL value is capped accordingly Key Value --- ----- lease_id database/creds/readonly/hTFHqyO2AVcCBQRFmRTv8bGV lease_duration 23h33m18s lease_renewable trueroot=# \du List of roles Role name | Attributes | Member of ----------------------------+-------------------------------------------+----------- ro | No inheritance, Cannot login | {} root | Superuser, Create…Replication, Bypass RLS | {} v-root-readonly-sKj…7755 | Password valid until 2021-04-20 04:49:20+00 | {ro} root=# \qLease widerrufen
$ vault lease revoke database/creds/readonly/$LEASE_ID All revocation operations queued successfully!$ docker exec -it postgres psql root=# \du List of roles Role name | Attributes | Member of -----------+----------------------------------------------------+----------- ro | No inheritance, Cannot login | {} root | Superuser, Create role, …, Replication, Bypass RLS | {} root=# \q$ vault list sys/leases/lookup/database/creds/readonly No value found at sys/leases/lookup/database/creds/readonlyAktive Auth‑Methoden listen
$ vault auth list Path Type Accessor Description ---- ---- -------- ----------- token/ token auth_token_5e067aef token based credentialsuserpassaktivieren$ vault auth enable userpass Success! Enabled userpass auth method at: userpass/Verfügbare Optionen ansehen
$ vault path-help auth/userpassBenutzer hinzufügen
$ vault write auth/userpass/users/julio \ password=minha_senha \ policies=admin Success! Data written to: auth/userpass/users/julioBenutzer listen
$ vault list auth/userpass/users Keys ---- julioIn neuem Terminal mit dem Benutzer einloggen
$ export VAULT_ADDR='http://127.0.0.1:8200' $ vault login -method=userpass username=julio Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run "vault login" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.8vRGmBVEQ1indCPtGL796od3 token_accessor CmTWhmRHFq4RMLb6wccFgOFT token_duration 768h token_renewable true token_policies ["admin" "default"] identity_policies [] policies ["admin" "default"] token_meta_username julioBenutzer löschen
$ vault delete auth/userpass/users/julio Success! Data deleted (if it existed) at: auth/userpass/users/julioSecret im Vault speichern
$ vault kv put kv/app-server api-key=123456 Key Value --- ----- created_time 2021-04-19T06:02:13.217806976Z deletion_time n/a destroyed false version 1Wrapping‑Token (5 Minuten) erzeugen
$ vault kv get -wrap-ttl=300 kv/app-server Key Value --- ----- wrapping_token: s.wqyqcD8lPcaOGznSg4ZNVFPC wrapping_accessor: sy6OAxfoKcmPLbUufzGhxkFY wrapping_token_ttl: 5m wrapping_token_creation_time: 2021-04-19 13:08:13.210327049 -0300 -03 wrapping_token_creation_path: kv/data/app-serverSecret mit diesem Token abrufen
$ curl --header "X-Vault-Token: s.wqyqcD8lPcaOGznSg4ZNVFPC" --request POST \ $VAULT_ADDR/v1/sys/wrapping/unwrap | jq { "request_id": "69598fb3-0f90-a1c8-22d8-387f5c79564e", "lease_id": "", "renewable": false, "lease_duration": 0, "data": { "data": { "api-key": "123456" }, "metadata": { "created_time": "2021-04-19T06:02:13.217806976Z", "deletion_time": "", "destroyed": false, "version": 1 } }, "wrap_info": null, "warnings": null, "auth": null }KV‑Store am Pfad
webkvanlegen$ vault secrets enable -path=webkv kv Success! Enabled the kv secrets engine at: webkv/Secret in
webkvschreiben$ vault kv put webkv/app-server api-key=123456 Success! Data written to: webkv/app-serverWeb‑Policy anlegen
$ cat webpol.hcl path "webkv/*" { capabilities = ["read", "list"] }$ vault policy write web webpol.hcl Success! Uploaded policy: webToken mit Web‑Policy (5 Minuten) erstellen
$ vault token create -policy=web -wrap-ttl=300 Key Value --- ----- wrapping_token: s.FPSHG7GJ3pWWliA0zSDEb77w wrapping_accessor: oLR1tRBpyQACPICrMIWrtfDm wrapping_token_ttl: 5m wrapping_token_creation_time: 2021-04-19 14:58:31.807307058 -0300 -03 wrapping_token_creation_path: auth/token/create wrapped_accessor: eP85Zj7EmwrDgE1GoagsNJ6pToken auspacken
$ curl --header "X-Vault-Token: s.FPSHG7GJ3pWWliA0zSDEb77w" --request POST \ $VAULT_ADDR/v1/sys/wrapping/unwrap | jq { "request_id": "4cfe3fd2-942a-d7e3-34e5-2b7d644b3e28", "lease_id": "", "renewable": false, "lease_duration": 0, "data": null, "wrap_info": null, "warnings": null, "auth": { "client_token": "s.lDqthe94B6bFVyXxNqLMuwYO", "accessor": "eP85Zj7EmwrDgE1GoagsNJ6p", "policies": [ "default", "web" ], "token_policies": [ "default", "web" ], "metadata": null, "lease_duration": 2764800, "renewable": true, "entity_id": "", "token_type": "service", "orphan": false } }Secret mit dem erhaltenen Token lesen
$ curl --header "X-Vault-Token: s.lDqthe94B6bFVyXxNqLMuwYO" $VAULT_ADDR/v1/webkv/app-server | jq { "request_id": "f63dcc67-b9f6-43f6-dbb8-756c709e6a2f", "lease_id": "", "renewable": false, "lease_duration": 2764800, "data": { "api-key": "123456" }, "wrap_info": null, "warnings": null, "auth": null }
Links
- https://github.com/scarolan/aws-vault-in-five-minutes/blob/master/docs/index.md
- https://github.com/scarolan/vault-aws-cf
- https://jessequinn.info/blog/articles/hashicorp-boundary
- https://learn.hashicorp.com/tutorials/consul/deployment-guide
- https://learn.hashicorp.com/tutorials/vault/ha-with-consul
- https://medium.com/@pcarion/a-consul-a-vault-and-a-docker-walk-into-a-bar-d5a5bf897a87
- https://www.katacoda.com/courses/docker-production/vault-secrets
- https://www.youtube.com/watch?v=a-fq30II2cM
- https://discuss.hashicorp.com/t/vault-enable-https-with-openshift4-and-helm3/21574
- https://churrops.io/2017/08/30/meetup-churrops-iniciando-a-trilha-com-o-vault-hands-on/
- https://jovandeginste.github.io/2016/07/20/use-vault-with-client-certificates.html
- http://javierblog.com/deploy-a-hashicorp-vault-cluster-with-consul-backend/
- https://werner-dijkerman.nl/2017/01/15/setting-up-a-secure-vault-with-a-consul-backend/
- https://itspyworld.blogspot.com/2020/07/installing-hashicorp-vault-password.html
- https://learn.hashicorp.com/tutorials/consul/tls-encryption-openssl-secure
- https://github.com/ned1313/Getting-Started-Vault