HashiCorp Nomad and Vault: Dynamic Secrets


Bicycle

In einer cloud-nativen Umgebung ist das Management von Geheimnissen ein kritischer Aspekt der Sicherheit. HashiCorp Vault ist ein beliebtes Tool zur Verwaltung von Geheimnissen und zum Schutz sensibler Daten. In Kombination mit HashiCorp Nomad, einem Orchestrierungstool zur Bereitstellung und Verwaltung von Anwendungen, kann eine leistungsstarke und sichere Plattform für die Ausführung von Arbeitslasten geschaffen werden.

Dynamische Geheimnisse in HashiCorp Vault

HashiCorp Vault bietet eine dynamische Geheimnis-Engine, die Geheimnisse bei Bedarf generiert. Diese Funktion ermöglicht die Erstellung kurzlebiger Anmeldedaten für Datenbanken, Cloud-Anbieter und andere Dienste. Durch die Verwendung dynamischer Geheimnisse kann das Risiko einer Offenlegung verringert und die Lebensdauer sensibler Daten begrenzt werden.

Unsere Beispielanwendung

In diesem Beispiel werden wir eine einfache Webanwendung mit HashiCorp Nomad bereitstellen. Die Anwendung benötigt Zugriff auf eine MySQL-Datenbank, und wir werden HashiCorp Vault verwenden, um dynamische Anmeldedaten für die Datenbank zu generieren, die Vault-Transit-Engine verwenden, um die Datenbankwerte im laufenden Betrieb zu verschlüsseln, und schließlich HashiCorp Consul für die Dienstentdeckung verwenden.

MySQL-Datenbank

Die eigentliche Bereitstellung der MySQL-Datenbank erfolgt mit der folgenden Nomad-Job-Datei. Die MySQL-Datenbank wird als Dienst gestartet und das Root-Passwort wird als Umgebungsvariable festgelegt.

 1job "mysql-server" {
 2  datacenters = ["dc1"]
 3  type        = "service"
 4  namespace   = "demo"
 5
 6  group "mysql-server" {
 7    count = 1
 8
 9    service {
10      name = "mysql-server"
11      port = "db"
12    }
13
14    restart {
15      attempts = 10
16      interval = "5m"
17      delay    = "25s"
18      mode     = "delay"
19    }
20
21    task "mysql-server" {
22      driver = "docker"
23
24      env = {
25        "MYSQL_ROOT_PASSWORD" = "super-duper-password"
26      }
27
28      config {
29        image = "docker.io/mysql:9"
30        ports = ["db"]
31      }
32
33      resources {
34        cpu    = 500
35        memory = 1024
36      }
37    }
38    network {
39      mode = "bridge"
40      port "db" {
41        static = 3306
42      }
43    }
44  }
45}

Hartecodierte Bereitstellung

Im ersten Schritt werden wir die Webanwendung mit hartcodierten Anmeldedaten bereitstellen. Dies könnte ein Workflow sein, mit dem Sie vertraut sind – beginnen Sie mit weniger sicheren Einstellungen und verbessern Sie sie schrittweise. Die Webanwendung ist eine einfache Python-Flask-Anwendung, die eine Verbindung zur MySQL-Datenbank herstellt und die Datenbankwerte auf einer Webseite anzeigt. Alternativ kann sie die unverschlüsselten Datenbankwerte anzeigen.

In der Konfigurationsdatei der Anwendung config.ini können Sie die hartcodierten Datenbankanmeldedaten sehen. Die Anwendung wird als Nomad-Job gestartet, und die Konfigurationsdatei wird in den Container eingebunden. Die Konfigurationsdatei verwendet auch die Consul-Template-Engine, um die Konfiguration der Datenbankinstanz aus der Consul-Dienstentdeckung mit der Anweisung {{ range service "mysql-server" }} abzurufen.

 1job "dynamic-app" {
 2  datacenters = ["dc1"]
 3  type        = "service"
 4  namespace   = "demo"
 5
 6  group "dynamic-app" {
 7    count = 1
 8
 9    restart {
10      attempts = 10
11      interval = "5m"
12      delay    = "25s"
13      mode     = "delay"
14    }
15
16    service {
17      name = "dynamic-app"
18      port = "web"
19
20      check {
21        type     = "http"
22        method   = "GET"
23        interval = "10s"
24        timeout  = "2s"
25        path     = "/health"
26      }
27    }
28
29    task "dynamic-app" {
30      driver = "docker"
31
32      config {
33        image = "ghcr.io/infralovers/nomad-vault-mysql:1.0.0"
34        volumes = [
35          "local/config.ini:/usr/src/app/config/config.ini"
36        ]
37
38        ports = ["web"]
39      }
40
41      template {
42        destination = "local/config.ini"
43        data        = <<EOF
44[DEFAULT]
45Port = 8080
46
47[DATABASE]
48{{ range service "mysql-server" }}
49Address = {{ .Address }}
50Port = {{ .Port }}
51{{end}}
52
53Database = my_app
54User = root
55Password = super-duper-password
56EOF
57      }
58      resources {
59        cpu    = 256
60        memory = 256
61      }
62    }
63    network {
64      port "web" {
65        to = 8080
66      }
67    }
68  }
69}

Key-Value-Geheimnis-Engine

Der nächste Schritt, um sicherere Bereitstellungen zu erreichen, ist die Verwendung der Key-Value-Geheimnis-Engine von HashiCorp Vault. Diese Engine ermöglicht das Speichern und Abrufen beliebiger Geheimnisse. In diesem Beispiel speichern wir die Datenbankanmeldedaten in Vault und rufen sie zur Laufzeit ab.

1$ vault secrets enable -path=dynamic-app/kv kv
2$ vault kv put dynamic-app/kv/database username=root password=super-duper-password

Um HashiCorp Nomad den Zugriff auf die Geheimnisse zu ermöglichen, müssen Sie eine Richtlinie erstellen, die Lesezugriff auf den Pfad dynamic-app/kv gewährt. Wir haben diese Richtlinie nomad-dynamic-app genannt.

1path "dynamic-app/kv/database" {
2  capabilities = ["read"]
3}

Auch die Anwendungsbereitstellung muss angepasst werden – die Konfigurationsdatei verwendet jetzt die Consul-Template-Engine, um die Datenbankanmeldedaten aus dem Vault Key-Value-Store durch die Anweisung {{ with secret "dynamic-app/kv/database" }} abzurufen.

 1job "dynamic-app" {
 2  datacenters = ["dc1"]
 3  type        = "service"
 4  namespace   = "demo"
 5
 6  group "dynamic-app" {
 7    count = 1
 8
 9    vault {
10      policies      = ["nomad-dynamic-app"]
11      change_mode   = "signal"
12      change_signal = "SIGINT"
13    }
14
15    service {
16      name = "dynamic-app"
17      port = "web"
18
19      check {
20        type     = "http"
21        method   = "GET"
22        interval = "10s"
23        timeout  = "2s"
24        path     = "/health"
25      }
26    }
27
28    restart {
29      attempts = 10
30      interval = "5m"
31      delay    = "25s"
32      mode     = "delay"
33    }
34
35    task "dynamic-app" {
36      driver = "docker"
37
38      config {
39        image = "ghcr.io/infralovers/nomad-vault-mysql:1.0.0"
40        volumes = [
41          "local/config.ini:/usr/src/app/config/config.ini"
42        ]
43
44        ports = ["web"]
45      }
46
47      template {
48        destination = "local/config.ini"
49        data        = <<EOF
50[DEFAULT]
51Port = 8080
52
53[DATABASE]
54{{ range service "mysql-server" }}
55Address = {{ .Address }}
56Port = {{ .Port }}
57{{end}}
58
59Database = my_app
60{{ with secret "dynamic-app/kv/database" }}
61User = {{ .Data.username }}
62Password = {{ .Data.password }}
63{{ end }}
64EOF
65      }
66      resources {
67        cpu    = 256
68        memory = 256
69      }
70    }
71    network {
72      mode = "bridge"
73      port "web" {
74        to = 8080
75      }
76    }
77  }
78}

Dynamische Geheimnis-Engine

Der vorletzte Schritt auf unserer Reise ist die Verwendung der dynamischen Geheimnis-Engine von HashiCorp Vault. Diese Engine generiert kurzlebige Anmeldedaten für Datenbanken, Cloud-Anbieter und andere Dienste. In diesem Beispiel verwenden wir die MySQL-Datenbank-Geheimnis-Engine, um dynamische Anmeldedaten für die Datenbank zu generieren.

 1$ vault secrets enable -path=dynamic-app/db database
 2$ vault write dynamic-app/db/config/mysql \
 3    plugin_name=mysql-database-plugin \
 4    connection_url="{{username}}:{{password}}@tcp(mysql-server.service.consul:3306)/" \
 5    allowed_roles="*" \
 6    username="root" \
 7    password="super-duper-password"
 8$ vault write -force dynamic-app/db/database/rotate-root/mysql
 9$ vault write dynamic-app/db/roles/my-role \
10    db_name=mysql \
11    creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT ALL ON my_app.* TO '{{name}}'@'%';" \
12    default_ttl="1h" \
13    max_ttl="24h"

Natürlich müssen wir unsere Vault-Richtlinie für HashiCorp Nomad anpassen, um auf die dynamische Geheimnis-Engine zuzugreifen:

1path "dynamic-app/kv/database" {
2  capabilities = ["read"]
3}
4path "dynamic-app/db/creds/app" {
5  capabilities = ["read"]
6}

Schließlich muss auch die Anwendungsbereitstellung angepasst werden, aber dieses Mal verwenden wir die dynamische Geheimnis-Engine, um die Datenbankanmeldedaten aus dem Vault-Geheimnisspeicher mit der Anweisung {{ with secret "dynamic-app/db/creds/app" }} abzurufen

.

 1job "dynamic-app" {
 2  datacenters = ["dc1"]
 3  type        = "service"
 4  namespace   = "demo"
 5
 6  group "dynamic-app" {
 7    count = 1
 8
 9    vault {
10      policies      = ["nomad-dynamic-app"]
11      change_mode   = "signal"
12      change_signal = "SIGINT"
13    }
14
15    service {
16      name = "dynamic-app"
17      port = "web"
18
19      check {
20        type     = "http"
21        method   = "GET"
22        interval = "10s"
23        timeout  = "2s"
24        path     = "/health"
25      }
26    }
27
28    restart {
29      attempts = 10
30      interval = "5m"
31      delay    = "25s"
32      mode     = "delay"
33    }
34
35    task "dynamic-app" {
36      driver = "docker"
37
38      config {
39        image = "ghcr.io/infralovers/nomad-vault-mysql:1.0.0"
40        volumes = [
41          "local/config.ini:/usr/src/app/config/config.ini"
42        ]
43
44        ports = ["web"]
45      }
46
47      template {
48        destination = "local/config.ini"
49        data        = <<EOF
50[DEFAULT]
51Port = 8080
52
53[DATABASE]
54{{ range service "mysql-server" }}
55Address = {{ .Address }}
56Port = {{ .Port }}
57{{end}}
58
59Database = my_app
60{{ with secret "dynamic-app/db/creds/app" }}
61User = {{ .Data.username }}
62Password = {{ .Data.password }}
63{{ end }}
64EOF
65      }
66      resources {
67        cpu    = 256
68        memory = 256
69      }
70    }
71    network {
72      mode = "bridge"
73      port "web" {
74        to = 8080
75      }
76    }
77  }
78}

Sicher! Hier ist der abschließende Teil der Integration von HashiCorp Vault, Nomad und der dynamischen Geheimnis-Engine, um die gesamte Pipeline fertigzustellen.

Vault-Transit-Engine

Nachdem wir nun dynamische Geheimnisse zur Sicherung der Datenbankanmeldedaten verwenden, können wir als letzten Schritt die Vault-Transit-Engine implementieren, um sensible Daten während der Übertragung zu verschlüsseln. Die Transit-Engine wird verwendet, um Daten zu verschlüsseln und zu entschlüsseln, ohne dass Vault diese Daten tatsächlich speichert. Dies ist ideal für Anwendungsfälle wie das Verschlüsseln von Daten vor dem Speichern in einer Datenbank.

1$ vault secrets enable -path=dynamic-app/transit transit
2$ vault write  -f dynamic-app/transit/keys/app

Damit die Anwendung die Vault-Transit-Engine nutzen kann, müssen wir sicherstellen, dass Nomad Zugriff auf die Vault-Transit-Richtlinien hat. Die Vault-Richtlinie muss Lese- und Schreibrechte für die Transit-Engine enthalten.

 1path "dynamic-app/kv/database" {
 2  capabilities = ["read"]
 3}
 4path "dynamic-app/db/creds/app" {
 5  capabilities = ["read"]
 6}
 7path "dynamic-app/transit/encrypt/app" {
 8  capabilities = ["create", "update"]
 9}
10path "dynamic-app/transit/decrypt/app" {
11  capabilities = ["create", "update"]
12}

Diese Funktionalität stellt eine zusätzliche Sicherheitsebene dar, um die Datenbankwerte „on the fly“ zu verschlüsseln. Sie erfordert jedoch Code-Änderungen, damit Ihre Anwendung in der Lage ist, auf Code-Ebene mit der HashiCorp Vault Transit-Engine zu interagieren.

Die bisherigen Anpassungen an HashiCorp Vault betrafen nur Änderungen im Bereitstellungscode, ohne dass Änderungen an Ihrer Anwendung notwendig waren.

Auch die Bereitstellung der Anwendung muss angepasst werden. Dieses Mal müssen wir jedoch den Konfigurationsabschnitt [VAULT] hinzufügen, um die Transit-Engine zur Verschlüsselung der Datenbankwerte zu verwenden, indem die Anweisungen KeyPath = dynamic-app/transit und KeyName = app gesetzt werden.

 1job "dynamic-app" {
 2  datacenters = ["core"]
 3  type        = "service"
 4  namespace   = "demo"
 5
 6  group "dynamic-app" {
 7    count = 1
 8
 9    vault {
10      policies      = ["nomad-dynamic-app"]
11      change_mode   = "signal"
12      change_signal = "SIGINT"
13    }
14
15    service {
16      name = "dynamic-app"
17      port = "web"
18
19      check {
20        type     = "http"
21        method   = "GET"
22        interval = "10s"
23        timeout  = "2s"
24        path     = "/health"
25      }
26    }
27
28    restart {
29      attempts = 10
30      interval = "5m"
31      delay    = "25s"
32      mode     = "delay"
33    }
34
35    task "dynamic-app" {
36      driver = "docker"
37
38      config {
39        image = "ghcr.io/infralovers/nomad-vault-mysql:1.0.0"
40        volumes = [
41          "local/config.ini:/usr/src/app/config/config.ini"
42        ]
43
44        ports = ["web"]
45      }
46
47      template {
48        destination = "local/config.ini"
49        data        = <<EOF
50[DEFAULT]
51Port = 8080
52
53[DATABASE]
54{{ range service "mysql-server" }}
55Address = {{ .Address }}
56Port = {{ .Port }}
57{{end}}
58
59Database = my_app
60{{ with secret "dynamic-app/db/creds/app" }}
61User = {{ .Data.username }}
62Password = {{ .Data.password }}
63{{ end }}
64
65[VAULT]
66Enabled = True
67InjectToken = True
68Namespace =
69Address = {{ env "VAULT_ADDR" }}
70KeyPath = dynamic-app/transit
71KeyName = app
72EOF
73      }
74      resources {
75        cpu    = 256
76        memory = 256
77      }
78    }
79    network {
80      port "web" {
81        to = 8080
82      }
83    }
84  }
85}

Zusammenfassung

In diesem Artikel haben wir demonstriert, wie man HashiCorp Vault und Nomad verwendet, um eine sichere Webanwendung mit dynamischen Geheimnissen bereitzustellen. Durch die Verwendung dynamischer Geheimnisse können Sie das Risiko einer Offenlegung reduzieren und die Lebensdauer sensibler Daten begrenzen. Dieser Ansatz bietet eine sichere und skalierbare Plattform für den Betrieb Ihrer Workloads in einer cloud-nativen Umgebung.

Selbst wenn Sie Ihre Bereitstellungen durch die Verwendung der Key-Value-Secret-Engine transformieren möchten, haben Sie einen guten Ausgangspunkt, um Ihre Sicherheitseinstellungen schrittweise zu verbessern. Der nächste Schritt besteht darin, die dynamischen Geheimnisse zu verwenden, um kurzlebige Anmeldeinformationen für Ihre Dienste zu generieren. Wie im Beispiel gezeigt, ist es nur eine geringfügige Änderung in Ihrem aktuellen Bereitstellungscode erforderlich, um die dynamische Geheimnisse-Engine zu nutzen.

Alle Codebeispiele finden Sie im GitHub-Repository, um das Beispiel selbst nachzustellen. Als Ausgangspunkt können Sie die Anweisungen zur Bereitstellung eines HashiCorp Nomad-Clusters in der Cloud verwenden, um einen eigenen Nomad-Cluster bereitzustellen, der mit einem Consul-Cluster und einem Vault-Cluster ausgestattet ist.

Wenn Sie mehr über HashiCorp Vault und Nomad erfahren möchten, werfen Sie einen Blick auf unsere Kurse Cloud Native Essentials und HashiCorp Vault Enterprise. Diese Kurse helfen Ihnen, die Werkzeuge und Techniken zu meistern, die benötigt werden, um sichere und skalierbare cloud-native Anwendungen zu erstellen.

Zurück Unsere Trainings entdecken

Wir sind für Sie da

Sie interessieren sich für unsere Trainings oder haben einfach eine Frage, die beantwortet werden muss? Sie können uns jederzeit kontaktieren! Wir werden unser Bestes tun, um alle Ihre Fragen zu beantworten.

Hier kontaktieren