So einfach, effektiv und sicher ist Secrets-Management mit HashiCorp Vault

14.09.2022Frederik Möllers
DevOps Authorization / Authentication Certificates CI/CD Cloud Native Computing Foundation DevOps HashiCorp Security

Nachdem Ricky in einem älteren TechUp schon die Vorteile und die Notwendigkeit von HA Key-Value-Stores aufgezeigt hat, soll es in diesem TechUp um das Äquivalent für sensible Daten gehen. HashiCorp Vault ist ein Tool um sensible Daten wie API Keys, Passwörter und Zertifikate nicht nur sicher zu speichern sondern auch hochverfügbar den Clients zur Verfügung zu stellen. Im ersten Teil dieses TechUp’s schauen wir uns erstmal nur die Architektur und den grundlegenden Aufbau von Vault an. Hands-On-Beispiele wird es in einem zukünftigen Artikel geben.

Secrets

Zunächst stellt sich die Frage, um was es sich bei Secrets überhaupt handelt, und wieso man eigene Software benötigt, um diese zu verwalten. Bei Secrets handelt es sich um sensible Daten, auf welche nur eine eingeschränkte Gruppe Zugriff haben soll, da diese z.B. Zugriff auf Systeme oder Bereiche bereitstellen, auf die nicht jeder Zugriff haben soll. Beispiele dafür wären Passwörter, Zertifikate, aber auch Keys zum ver- und entschlüsseln von Daten.

Sobald man sich im Kontext eines grösseren Teams bewegt, kommt schnell die Notwendigkeit auf, Secrets teilen zu können. Angefangen beim WLAN-Passwort, kann es sich dabei aber beispielsweise auch um Datenbank-Credentials handeln, welche eine Development-Team für deren Software benötigt. Ein häufig verwendetes Tool an dieser Stelle ist 1Password aber auch alle grossen Cloud-Anbieter bieten eine Lösung, sowohl um Secrets zu speichern, als auch, um Zugriff darauf zu ermöglichen oder zu beschränken. Mit der Notwendigkeit, den Zugriff zu unterschiedlichsten Secrets genau und kontrolliert verwalten zu können, und diese auch technischen Nutzern hochverfügbar zur Verfügung zu stellen, kommen viele dieser Tools an ihre Grenzen.

Warum HashiCorp Vault?

HashiCorp hat mit Vault im Jahr 2015 ein Produkt ins Leben gerufen, welches als zentrales Element so ziemlich alle Anwendungsfälle und Notwendigkeiten einer grösseren Organisation in Sachen Secrets und Secret Management zur Verfügung stellt. Bei Vault handelt es sich hierbei nicht mehr nur um einen reinen Secrets-Store, sondern um ein vollständiges Secrets-Management-System, welches beispielsweise auch die Möglichkeit bietet, Secrets selbst nach Bedarf zu erstellen. Auf dem CNCF-Landscape ist Vault im Bereich Key-Management einzugliedern.

Genau hier hebt sich Vault aktuell von anderen Tools ab; betrachten wir als Beispiel mal die Unterschiede zum AWS Secrets Manager, der Lösung von Amazon:

Der AWS Secret Manager bietet wie Vault die Möglichkeit, Secrets zu speichern, zu rotieren und den Zugriff zu verwalten. HashiCorp Vault bietet obendrauf die Möglichkeit, Secrets dynamisch zu erstellen und nach einer definierten Ablaufzeit zu löschen. Weitere Funktionen, welche Vault im Vergleich zum Angebot von AWS bietet, ist beispielsweise auch die Möglichkeit, Zertifikate zu generieren, als SSH CA Authority zu fungieren, wie auch die Cross region/Cross Cloud/Cross Datacenter replikation und Ver- und Entschlüsselung-as-a-service.

hashicorp-01

Figure: Flow-Example

In der Darstellung sieht man einen Beispielhaften Flow: Eine Applikation benötigt Datenbankzugriff, die Berechtigungen und Konfigurationen wurden im Vorhinein durch einen Admin in Vault erstellt. Benötigt eine Applikation nun Zugriff auf die Datenbank, kann sie Secrets, welche von Vault dynamisch generiert werden, von Vault abfragen und Zugriff bekommen. Nach einer vordefinierten Zeit (TTL) entfernt Vault die Credentials automatisch.

Verschiedene sogenannte Secret-Engines ermöglichen das Anbinden von zahlreichen Diensten an Vault; welche Funktion die einzelne Secrets-Engine übernimmt ist hier im einzelnen zu betrachten. Eine Secret-Engine für eine Datenbank könnte wie im oben genannten Beispiel Secrets dynamisch generieren und einbinden. Es existieren aber auch Secret-Engines für einfache Key-Value-Stores, welche Key-Value-Paare speichern und zur Verfügung stellen können, sowie auch eine Secrets-Engine, welche verschlüsselte Daten unverschlüsselt zurückgibt, bzw. unverschlüsselte verschlüsselt.

Features von Vault

  • Vault übernimmt Authentifizierung von Clients, Validierung gegen third-party trusted sources, als auch Authorisierung und Access-Management
  • Sichere, verschlüsselte Secret Storage
  • Dynamische Secrets, welche on-demand für eine definierte Lebenszeit generiert werden können
  • Verschlüsselung und Entschlüsselung der Daten ausserhalb von Vault
  • Leasing von Secrets
  • Audit Logs
  • Sowie self-hosted, als auch hosted (HCP Vault)

Architektur

Um die Architektur von Vault anschaulich beschreiben zu können, starten wir zunächst, nachdem Vault installiert wurde, per Kommandozeile einen Vault-Server im Development-Modus mit dem Befehl vault server -dev:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
vault server -dev
==> Vault server configuration:

             Api Address: http://127.0.0.1:8200
                     Cgo: disabled
         Cluster Address: https://127.0.0.1:8201
              Go Version: go1.18.5
              Listener 1: tcp (addr: "127.0.0.1:8200", cluster address: "127.0.0.1:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
               Log Level: info
                   Mlock: supported: false, enabled: false
           Recovery Mode: false
                 Storage: inmem
                 Version: Vault v1.11.2, built 2022-07-29T09:48:47Z
             Version Sha: 3a8aa12eba357ed2de3192b15c99c717afdeb2b5+CHANGES

==> Vault server started! Log data will stream in below:

WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory
and starts unsealed with a single unseal key. The root token is already
authenticated to the CLI, so you can immediately begin using Vault.

You may need to set the following environment variable:

    $ export VAULT_ADDR='http://127.0.0.1:8200'

The unseal key and root token are displayed below in case you want to
seal/unseal the Vault or re-authenticate.

Unseal Key: G7zfTKLVRtRJzdWCSPmG3IKFTl3jQY4rpG/WCJsBDUc=
Root Token: hvs.FqHGU7sJXNyVVUhWXhtsASw0

Development mode should NOT be used in production installations!

Der Server ist im Development-Modus gestartet, mit einer Instanz, welche die Vault API auf dem Port 8200 zur Verfügung stellt. Alle Daten werden in Memory (inmem), also nicht persistent, gespeichert. Desweiteren startet der Dev-Server unsealed, was uns zum ersten Aspekt der Architektur von Vault führt. Bevor ich darauf eingehe, setzen wir die API-Adresse und den Root-Token als Environment-Variablen, um alle weiteren Befehle lesbarer zu gestalten:

1
2
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN="hvs.FqHGU7sJXNyVVUhWXhtsASw0" 

Alle Daten werden von Vault im Storage-Backend verschlüsselt gespeichert. Jede Kommunikation mit diesem Backend läuft verschlüsselt ab, der Key für diese Ver- und Entschlüsselung ist im “sealed"-Status verschlüsselt.

Sobald der Vault-Server gestartet wird, befindet dieser sich grundsätzlich im “sealed"-Status (ausser er wird als Dev-Server gestartet) und muss demnach zunächst unsealed werden, bevor Operationen möglich sind. Dies ist sowohl mit einem Encryption-Key möglich, welcher bei der Initialisierung vom Vault-Server generiert und mittels Shamirs Secret Sharing-Algorithmus in mehrere Teile aufgeteilt wird, als auch mittels einem Trusted Vault-Server, Cloud-Key-Management System oder einem Hardware Security Module.

In unserem Beispiel hat der Development-Server jedoch nur einen unseal-key und ist schon unsealed. Durch den Befehl vault status können wir den aktuellen Zustand unseres Servers sehen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
vault status
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.11.2
Build Date      2022-07-29T09:48:47Z
Storage Type    inmem
Cluster Name    vault-cluster-636af702
Cluster ID      cf682ea5-8497-6052-fb24-cecffad379ac
HA Enabled      false

Wie wir sehen ist unser Server unsealed und hat nur einen Secret-“Share”. Sobald der Vault im unsealed-Zustand ist, kann über die HTTP-API kommuniziert werden und Clients können sich authentifizieren. Zur Authentifizierung bietet Vault mehrere Möglichkeiten: Per Username und Passwort, public/private Keys, Tokens, GitHub, Cloud-Dienste, LDAP, OIDC etc.

Nachdem der Client sich authentifiziert hat, wird anhand von zuvor erstellter Policies ein Token mit einer bestimmten Leasingzeit ausgestellt, mit welchem der Client nun Zugriff auf die konfigurierten Bereiche hat. Vault erlaubt Zugriff auf Bereiche nur, wenn diese explizit mittels Policy gegeben wurden. Bei diesen Policies handelt es sich um ACL-Regeln.

In unserem Beispiel benutzen wir aktuell den Root-Token, um Zugriff auf Vault zu erhalten. Macht der Client nun einen Request für ein konkretes Secret, wird dieses von einer sogenannten Secret-Engine zur Verfügung gestellt und bekommt eine lease ID, mit welcher der Client das Secret annullieren und erneuern kann.

Mit dem Befehlt vault secrets list können wir die aktuell aktivierten Secret-Engines sehen:

1
2
3
4
5
6
7
vault secrets list
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_cc668177    per-token private secret storage
identity/     identity     identity_21538fc7     identity store
secret/       kv           kv_f4a50940           key/value secret storage
sys/          system       system_f2f87035       system endpoints used for control, policy and debugging

Beim Dev-Server ist die Key/Value-Secret-Engine per Default auf dem Pfad secret/ aktiviert. Im folgenden Abschnitt gehen wir ein wenig genauer auf Secret-Engines ein.

Secret-Engines

Wie wir schon gesehen haben bietet Vault im Bereich der Authentifizierung eine Vielzahl von Möglichkeiten. Dies gilt auch für die Secret-Engines, bei welchen es sich um die Backend-Lösungen für verschiedene Arten von Secrets handelt.

Für einfache Key-Value-Paare haben wir schon die KV Secrets Engine kennengelernt, welche sowohl nicht-versionierte als auch versionierte Key-Value-Paare beinhalten kann. Ein weiteres Beispiel wäre die Kubernetes Secrets Engine, welche Kubernetes Serviceaccount Tokens, Serviceaccounts, Rollen und Role-Bindings generieren kann.

Ein besonderes Feature ist die Transit Secret Engine, mit welcher sich Daten ‘in-transit’ verschlüsseln und entschlüsseln lassen können, welche nicht von Vault direkt gespeichert werden. Mit dieser cryptography/encription as a service lässt sich garantierte Datenbankverschlüsselung auf allen Datenbanken einfach realisieren, ohne die Entwickler in Verantwortung nehmen zu müssen.

Für fast jeden Anwendungsfall gibt es mittleitweile eine Secrets-Engine; ob für alle grösseren Public-Cloud-Anbieter, Active Directory, Datenbanken, als auch für Identity Management, Zertifikate und SSH. Was jede Secrets-Engine genau macht und welche Funktionen diese zur Verfügung stellt, lässt sich in der jeweiligen Dokumentation nachlesen, worauf HashiCorp auch gerne drauf hinweist:

1
2
3
4
5
6
vault secrets
Usage: vault secrets <subcommand> [options] [args]

  This command groups subcommands for interacting with Vault's secrets engines.
  Each secret engine behaves differently. Please see the documentation for
  more information.

Im Beispiel der Key-Value Secret-Engine lassen sich Secrets wie folgt speichern und wieder abrufen: vault kv put -mount=secret hello password=1234

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
vault kv put  -mount=secret hello password=1234
== Secret Path ==
secret/data/hello

======= Metadata =======
Key                Value
---                -----
created_time       2022-08-19T05:31:12.640385Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

Abrufen lässt sich das mit put gespeicherte Key/Value-Pair folgendermassen: vault kv get -mount=secret hello

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
vault kv get -mount=secret hello
== Secret Path ==
secret/data/hello

======= Metadata =======
Key                Value
---                -----
created_time       2022-08-19T05:31:12.640385Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

====== Data ======
Key         Value
---         -----
password    1234

Da wir den Root-Token nutzen, können wir ohne vorherige Konfiguration Secrets speichern und abrufen. Einem neuen Clienten müssten wir über eine Policy zunächst Schreib- und Lesezugriff auf den Pfad geben (in diesem Beispiel secret/), damit dieser dieselben Aktionen durchführen kann.

High-Availability

Da Vault als zentrales Secret-Management-System eine Infrastrukturkritische Position einnimmt, liegt die Frage nach High-Availablity und Skalierung auf der Hand.

Eine Möglichkeit um Ausfallsicherheit zu erhöhen ist, Vault mit mehreren Vault-Servern und einem Storage-Backend im HA-Modus laufen zu lassen. Hier übernimmt einer der Server die Rolle des aktiven Servers und beantwortet alle Requests, die anderen Instanzen sind im hot-standby und übernehmen Anfragen, sollte der erste ausfallen.

Da neugestartete Server immer, wie eingangs beschrieben, sealed sind, sollte man sich in diesem Kontext auf jeden Fall Gedanken um eine der “unseal-as-a-service"-Möglichkeiten machen. Vault unterstützt verschiedene Storage-Backends, um die Daten zu persistieren, wie z.B. Public-Cloud-Dienste, Zookeeper, S3, Etcd aber auch In-Memory und lokale speicherung.

Desweiteren steht seit Vault 1.4 ein direkt integriertes Storage-Backend zur Verfügung, welches in sich auf High-Availability ausgelegt ist. Dieses verwendet das RAFT-Consensus-Protokoll, auf welches wir hier nicht tiefer eingehen werden. Dieses braucht für ein HA-Setup mehrere Instanzen. Um eine Fehlertoleranz von einem Server zu haben, werden mindestens drei HA-Instanzen benötigt. Für eine Toleranz von zwei Servern, benötigt der Cluster fünf Instanzen.

Fazit

Wenn es um Secret-Management-Systeme geht, kann man bei HashiCorp Vault sicherlich mittlerweile von einer eierlegenden Wollmilchsau reden. Dabei konzentriert sich Vault durchgängig auf wesentliche Probleme, ohne unnötige Komplexität hinzuzufügen. Die Vielzahl an Secret Engines und Authentifizierungsmöglichkeiten lassen keine Wünsche offen. Möglichkeiten wie encryption-as-a-service setzen dem ganzen noch eine Krone auf. Wie alle anderen Produkte ist HashiCorp dabei Open Source und self-managed kostenlos verfügbar. Für mehr Informationen zu Vault kann ich die Dokumentation mit den von HashiCorp bereitgestellte Tutorials herzlichst empfehlen.

Ich hoffe, in diesem TechUp konnte ich dir einen guten ersten Überblick über HashiCorp Vault geben. Im nächsten Teil wird der Fokus auf Hands-On gelegt.