HashiCorp Vault Secure Storage Backend im Apache APISIX-Ökosystem
API7.ai
January 24, 2022
Mit dem Aufstieg der mikroservicebasierten Architektur ist die Sicherung von Systemen viel schwieriger geworden als früher. Wir sind weit über den Punkt hinaus, an dem unsere 100 Instanzen von Backend-Servern mit einem einzigen statischen Geheimnis auf unseren Datenbankserver zugreifen, denn im Falle eines Geheimnislecks ist das gesamte System kompromittiert, und die Widerrufung dieses Geheimnisses führt zu einem massiven Dienstausfall (jetzt kann niemand mehr auf etwas zugreifen, es sei denn, die Instanzen werden neu konfiguriert). Wir können die Möglichkeit eines Sicherheitsverstoßes nicht ausschließen, da manchmal das Unerwartete passiert. Stattdessen liegt es ganz bei uns, den Schadensradius in solchen Situationen zu kontrollieren. Um Szenarien wie dieses zu bewältigen, kommt in einer Produktionsumgebung eine beliebte Lösung wie HashiCorp Vault ins Spiel, die als identitätsbasiertes Geheimnis- und Verschlüsselungsmanagementsystem fungiert. In diesem Artikel habe ich gezeigt, wie man Vault mit dem jwt-auth-Plugin von Apache APISIX (einem Cloud-nativen API-Gateway) integriert, um die Vorteile beider Welten effektiv zu nutzen.
Was ist Vault?
HashiCorp Vault wurde entwickelt, um Organisationen bei der Verwaltung des Zugriffs auf Geheimnisse und deren sicherer Übertragung innerhalb einer Organisation zu unterstützen. Geheimnisse sind definiert als jede Form sensibler Anmeldeinformationen, die streng kontrolliert und überwacht werden müssen und die verwendet werden können, um sensible Informationen freizuschalten. Geheimnisse können in Form von Passwörtern, API-Schlüsseln, SSH-Schlüsseln, RSA-Token oder OTP vorliegen. In der realen Welt, in der es sehr häufig vorkommt, dass Geheimnisse in Konfigurationsdateien oder als Variablen im eigentlichen Programmcode gespeichert werden und manchmal sogar in einem Versionskontrollsystem wie GitHub, BitBucket oder GitLab landen, stellt dies eine große Bedrohung für die Sicherheit dar. Vault löst dieses Problem, indem es Geheimnisse zentralisiert. Es bietet verschlüsselten Speicher für statische Geheimnisse, die Generierung dynamischer Geheimnisse mit einer TTL-Lease, die Authentifizierung von Benutzern (Maschinen oder Menschen), um sicherzustellen, dass sie berechtigt sind, auf ein bestimmtes Geheimnis zuzugreifen, und vieles mehr. So ist selbst im Falle eines Sicherheitsverstoßes der Schadensradius viel kleiner und eingegrenzt.
Vault macht es sehr einfach, den Zugriff zu kontrollieren und zu verwalten, indem es uns eine einheitliche Schnittstelle zur Verwaltung jedes Geheimnisses in Ihrer Infrastruktur bietet. Darüber hinaus bietet es die Flexibilität, detaillierte Audit-Protokolle zu erstellen und nachzuverfolgen, wer auf was zugegriffen hat.
Über das APISIX jwt-auth-Plugin
Es handelt sich um ein Authentifizierungs-Plugin, das an jede APISIX-Route angehängt werden kann, um eine JWT-Authentifizierung (JSON Web Token, mehr erfahren) durchzuführen, bevor die Anfrage an die Upstream-URI weitergeleitet wird. Kurz gesagt, es handelt sich um einen sicheren Authentifizierungsmechanismus, der zur Autorisierung für kritische Ressourcen führt. Typischerweise wird ein privater Schlüssel oder ein Textgeheimnis vom Aussteller verwendet, um den JWT zu signieren. Der Empfänger des JWT überprüft die Signatur, um sicherzustellen, dass das Token nach der Signierung durch den Aussteller nicht verändert wurde. Die gesamte Integrität des JWT-Mechanismus hängt vom Signiergeheimnis ab (sei es ein Textgeheimnis oder RSA-Schlüsselpaare). Dies macht es für nicht authentifizierte Quellen schwierig, den Signierschlüssel zu erraten und zu versuchen, die Ansprüche innerhalb des JWT zu ändern.
Daher ist die Speicherung dieser Schlüssel in einer sicheren Umgebung äußerst wichtig. Wenn sie in die falschen Hände geraten, könnte dies die Sicherheit der gesamten Infrastruktur gefährden. Obwohl wir von APISIX alle Mittel nutzen, um standardmäßige SecOps-Praktiken zu befolgen, ist es in der Produktionsumgebung ganz natürlich, eine zentralisierte Schlüsselverwaltungslösung wie HashiCorp Vault zu haben, um detaillierte Audit-Trails, periodische Schlüsselrotation, Schlüsselwiderruf usw. zu ermöglichen. Und es wäre ein ziemlich problematisches Problem, wenn Sie jedes Mal die Apache-APISIX-Konfiguration aktualisieren müssten, sobald eine Schlüsselrotation in der gesamten Infrastruktur stattfindet.
Schritte zur Verwendung von Vault mit Apache APISIX
Für die Integration mit Vault muss Apache APISIX mit der Vault-Konfiguration in der config.yaml geladen werden.
Intern kommuniziert APISIX mit dem Vault-Server über die KV Secret Engine v1 HTTP APIs. Da die meisten Unternehmenslösungen in ihrer Produktionsumgebung an der KV Secrets Engine - Version 1 festhalten, haben wir in der Anfangsphase der APISIX-Vault-Unterstützung nur Version 1 unterstützt. In späteren Releases werden wir die Unterstützung für K/V Version 2 hinzufügen.
Die Hauptidee hinter der Verwendung von Vault anstelle des APISIX-etcd-Backends ist das Sicherheitsbedenken in einer Umgebung mit geringem Vertrauen. Wir, die APISIX-Entwickler, nehmen Ihre Prioritäten ernst. Deshalb empfehlen wir die Verwendung von Vault-Zugriffstoken, die kurzlebig sind und dem APISIX-Server eingeschränkten Zugriff gewähren können.
Vault konfigurieren
Wenn Sie bereits eine Vault-Instanz mit den notwendigen Berechtigungen laufen haben, können Sie diesen Abschnitt überspringen. Dieser Abschnitt teilt die Best Practices zur Verwendung von Vault innerhalb des Apache-APISIX-Ökosystems. Bitte befolgen Sie die unten genannten Schritte.
Schritt 1: Starten Sie einen Vault-Server
Hier haben Sie mehrere Optionen, Sie können zwischen Docker, vorkompilierten Binärdateien oder dem Bauen aus dem Quellcode wählen. Da Sie zur Kommunikation mit dem Vault-Server einen Vault-CLI-Client benötigen, würde ich den Weg mit vorkompilierten Binärdateien dem Docker-Ansatz vorziehen. Wie auch immer, es liegt ganz bei Ihnen (konsultieren Sie gerne die offiziellen Installationsdokumente von Vault). Um einen Entwicklungsserver zu starten, führen Sie den folgenden Befehl aus.
$ vault server -dev -dev-root-token-id=root
…
WARNUNG! Der Entwicklungsmodus ist aktiviert! In diesem Modus läuft Vault vollständig im Speicher
und startet entsiegelt mit einem einzigen Entsiegelschlüssel. Das Root-Token ist bereits
für die CLI authentifiziert, sodass Sie sofort mit der Verwendung von Vault beginnen können.
Möglicherweise müssen Sie die folgende Umgebungsvariable setzen:
export VAULT_ADDR='http://127.0.0.1:8200'
Der Entsiegelschlüssel und das Root-Token werden unten angezeigt, falls Sie
Vault siegeln/entsiegeln oder erneut authentifizieren möchten.
Entsiegelschlüssel: 12hURx2eDPKK1tzK+8TkgH9pPhPNJFpyfc/imCLgJKY=
Root-Token: root
Der Entwicklungsmodus sollte NICHT in Produktionsinstallationen verwendet werden!
Setzen Sie Ihre aktuelle CLI mit den richtigen Umgebungsvariablen.
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root'
Aktivieren Sie die Vault-K/V-Version-1-Geheimnis-Engine-Backend mit einem geeigneten Pfadpräfix. In dieser Demo wählen wir den Pfad kv
, damit es keine Kollision mit dem standardmäßigen Geheimnispfad für K/V Version 2 gibt.
$ vault secrets enable -path=kv -version=1 kv
Erfolg! Die kv-Geheimnis-Engine wurde aktiviert unter: kv/
# Um den Status zu bestätigen, führen Sie aus
$ vault secrets list
Pfad Typ Zugriff Beschreibung
---- ---- -------- -----------
cubbyhole/ cubbyhole cubbyhole_4eeb394c privater Geheimnisspeicher pro Token
identity/ identity identity_5ca6201e Identitätsspeicher
kv/ kv kv_92cd6d37 n/a
secret/ kv kv_6dd46a53 Schlüssel/Wert-Geheimnisspeicher
sys/ system system_2045ddb1 Systemendpunkte, die für Kontrolle, Richtlinien und Debugging verwendet werden
Schritt 2: Generieren Sie ein Vault-Zugriffstoken für APISIX
Dieser Artikel befasst sich mit der Verwendung von Vault aus der Perspektive des jwt-auth
-Plugins. Für einen APISIX-Consumer (wenn Sie mit Consumern im APISIX-Ökosystem nicht vertraut sind, lesen Sie bitte das Dokument über Apache APISIX Consumer) mit dem Benutzernamen jack
sucht das jwt-auth
-Plugin (wenn es mit der Vault-Konfiguration aktiviert ist) nach Geheimnissen unter <vault.prefix in config.yaml>/consumer/<consumer.username>/jwt-auth
im Vault-KV-Speicher. In diesem Kontext, wenn Sie den kv/apisix
-Namespace (Vault-Pfad) als vault.prefix
in der config.yaml für alle APISIX-bezogenen Datenabrufe zuweisen, empfehlen wir Ihnen, eine Richtlinie für den Pfad kv/apisix/consumer/
. zu erstellen. Das zusätzliche Sternchen () am Ende stellt sicher, dass die Richtlinie den Lesezugriff für jeden Pfad mit dem Präfix kv/apisix/consumer
erlaubt.
Erstellen Sie eine Richtliniendatei in HashiCorp Configuration Language (HCL).
$ tee apisix-policy.hcl << EOF
path "kv/apisix/consumer/*" {
capabilities = ["read"]
}
EOF
Wenden Sie die Richtlinie auf die Vault-Instanz an.
$ vault policy write apisix-policy apisix-policy.hcl
Erfolg! Hochgeladene Richtlinie: apisix-policy
Generieren Sie ein Token mit der neu definierten Richtlinie, die mit einem kleinen Zugriffsbereich konfiguriert wurde.
$ vault token create -policy="apisix-policy"
Schlüssel Wert
--- -----
Token s.KUWFVhIXgoRuQbbp3j1eMVGa
Token-Zugriff nPXT3q0mfZkLmhshfioOyx8L
Token-Dauer 768h
Token-erneuerbar true
Token-Richtlinien ["apisix-policy" "default"]
Identitätsrichtlinien []
Richtlinien ["apisix-policy" "default"]
In dieser Demonstration ist s.KUWFVhIXgoRuQbbp3j1eMVGa
Ihr Zugriffstoken.
Vault-Konfiguration in Apache APISIX hinzufügen
Wie bereits erwähnt, kommuniziert Apache APISIX über die Vault-HTTP-APIs mit der Vault-Instanz. Die notwendige Konfiguration muss in der config.yaml hinzugefügt werden. Hier ist die kurze Information über die verschiedenen Felder, die Sie verwenden können:
- host: Die Host-Adresse, unter der der Vault-Server läuft.
- timeout: HTTP-Timeout für jede Anfrage.
- token: Das generierte Token von der Vault-Instanz, das den Zugriff zum Lesen von Daten aus dem Vault gewährt.
- prefix: Das Aktivieren eines Präfixes ermöglicht es Ihnen, Richtlinien besser durchzusetzen, begrenzte Tokens zu generieren und die Daten, auf die von APISIX zugegriffen werden kann, eng zu kontrollieren. Gültige Präfixe sind (
kv/apisix
,secret
usw.)
vault:
host: 'http://0.0.0.0:8200'
timeout: 10
token: 's.KUWFVhIXgoRuQbbp3j1eMVGa'
prefix: 'kv/apisix'
Einen APISIX-Consumer erstellen
APISIX verfügt über eine Consumer-Ebene, die parallel zu Authentifizierungsszenarien verläuft. Um die Authentifizierung für eine APISIX-Route zu aktivieren, wird ein Consumer mit einer geeigneten Konfiguration für diesen spezifischen Authentifizierungsdienst benötigt. Nur dann kann APISIX die Anfrage an die Upstream-URI weiterleiten, indem es die Authentifizierung in Bezug auf die Consumer-Konfiguration erfolgreich durchführt. Ein APISIX-Consumer hat zwei Felder - eines ist username
(erforderlich), um einen Consumer von den anderen zu unterscheiden, und das andere ist plugins
, das die pluginspezifischen Konfigurationen des Consumers enthält.
Hier in diesem Artikel werden wir einen Consumer mit dem jwt-auth
-Plugin erstellen. Es führt eine JWT-Authentifizierung für die jeweilige(n) Route(n) oder Service(s) durch.
Um jwt-auth
mit der Vault-Konfiguration zu aktivieren, senden Sie eine Anfrage an:
$ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"username": "jack",
"plugins": {
"jwt-auth": {
"key": "test-key",
"vault": {}
}
}
}'
Hier sucht das Plugin nach dem Schlüsselgeheimnis im Vault-Pfad (<vault.prefix aus conf.yaml>/consumer/jack/jwt-auth
) für den Consumer jack
, der in der Consumer-Konfiguration angegeben ist, und verwendet es für die nachfolgende Signierung und JWT-Überprüfung. Wenn der Schlüssel nicht im selben Pfad gefunden wird, protokolliert das Plugin einen Fehler und kann die JWT-Authentifizierung nicht durchführen.
Test-Upstream-Server einrichten
Um das Verhalten zu testen, können Sie eine Route für einen Upstream (einen einfachen Ping-Handler, der Pong zurückgibt) erstellen. Sie können ihn mit einem einfachen Go-HTTP-Server einrichten.
// einfacher Upstream-Server
package main
import "net/http"
func ping(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("secure/pong\n"))
}
func main() {
http.HandleFunc("/secure/ping", ping)
http.ListenAndServe(":9999", nil)
}
Eine APISIX-Route mit aktivierter Authentifizierung erstellen
Erstellen Sie eine APISIX-Route mit diesem sicheren Ping-HTTP-Server und aktiviertem jwt-auth
-Authentifizierungs-Plugin.
$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"plugins": {
"jwt-auth": {}
},
"upstream": {
"nodes": {
"127.0.0.1:9999": 1
},
"type": "roundrobin"
},
"uri": "/secure/ping"
}'
Token vom jwt-auth-Plugin generieren
Jetzt signieren Sie ein JWT-Geheimnis von APISIX, das verwendet und übergeben werden kann, um Anfragen an die [http://localhost:9080/secure/ping](http://localhost:9080/secure/ping)
-Proxy-Route an den APISIX-Server zu stellen.
$ curl http://127.0.0.1:9080/apisix/plugin/jwt/sign\?key\=test-key -i
HTTP/1.1 200 OK
Datum: Di, 18 Jan 2022 07:50:57 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Verbindung: keep-alive
Server: APISIX/2.11.0
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ0ZXN0LWtleSIsImV4cCI6MTY0MjU3ODY1N30.nkyev1_KUapVgY_QVYETsSApA6gEkDWS8tsHFV1EpD8
Im vorherigen Schritt, wenn Sie etwas wie die Nachricht failed to sign jwt
sehen, stellen Sie sicher, dass Sie einen geheimen Schlüssel im Vault-Pfad kv/apisix/consumers/jack/jwt-auth
gespeichert haben.
# Beispiel
$ vault kv put kv/apisix/consumer/jack/jwt-auth secret=$ecr3t-c0d3
Erfolg! Daten geschrieben nach: kv/apisix/consumer/jack/jwt-auth
Anfrage an den APISIX-Server stellen
Stellen Sie jetzt eine Anfrage an den APISIX-Proxy für die Route /secure/ping
. Bei erfolgreicher Validierung wird die Anfrage an unseren Go-HTTP-Server weitergeleitet.
$ curl http://127.0.0.1:9080/secure/ping -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ0ZXN0LWtleSIsImV4cCI6MTY0MjU3ODU5M30.IYudBr7FTgRme70u4rEBoYNtGmGByzgfGlt8hctI__Q' -i
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 12
Verbindung: keep-alive
Datum: Di, 18 Jan 2022 08:00:04 GMT
Server: APISIX/2.11.0
secure/pong
Jede Anfrage ohne gültiges JWT führt zu einem HTTP 401 Unauthorized
-Fehler.
$ curl http://127.0.0.1:9080/secure/ping -i
HTTP/1.1 401 Unauthorized
Datum: Di, 18 Jan 2022 08:00:33 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Verbindung: keep-alive
Server: APISIX/2.11.0
{"message":"Fehlendes JWT-Token in der Anfrage"}
Verschiedene Anwendungsfälle, in denen Vault mit dem APISIX jwt-auth-Plugin integriert werden kann
Das Apache-APISIX-jwt-auth
-Plugin kann so konfiguriert werden, dass es einfache Textgeheimnisse sowie RS256-Public-Private-Schlüsselpaare aus dem Vault-Speicher abruft.
:::note Für die frühe Version dieser Integrationsunterstützung erwartet das Plugin, dass der Schlüsselname der im Vault-Pfad gespeicherten Geheimnisse unter [ secret
, public_key
, private_key
] liegt, um den Schlüssel erfolgreich zu verwenden. In zukünftigen Releases werden wir die Unterstützung für benutzerdefinierte Schlüsselnamen hinzufügen. :::
-
Sie haben ein HS256-Signiergeheimnis im Vault gespeichert und möchten es für die JWT-Signierung und -Überprüfung verwenden.
$ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "username": "jack", "plugins": { "jwt-auth": { "key": "key-1", "vault": {} } } }'
Hier sucht das Plugin nach dem Schlüssel
secret
im Vault-Pfad (<vault.prefix aus conf.yaml>/consumer/jack/jwt-auth
) für den Consumerjack
, der in der Consumer-Konfiguration angegeben ist, und verwendet es für die nachfolgende Signierung und JWT-Überprüfung. Wenn der Schlüssel nicht im selben Pfad gefunden wird, protokolliert das Plugin einen Fehler und kann die JWT-Authentifizierung nicht durchführen. -
RS256-RSA-Schlüsselpaare, sowohl öffentliche als auch private Schlüssel, sind im Vault gespeichert.
$ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "username": "jim", "plugins": { "jwt-auth": { "key": "rsa-keypair", "algorithm": "RS256", "vault": {} } } }'
Das Plugin sucht nach den Schlüsseln
public_key
undprivate_key
im Vault-KV-Pfad (<vault.prefix aus conf.yaml>/consumer/jim/jwt-auth
) fürjim
, der in der Plugin-Vault-Konfiguration angegeben ist. Wenn sie nicht gefunden werden, schlägt die Authentifizierung fehl.Wenn Sie unsicher sind, wie Sie öffentliche und private Schlüssel im Vault-KV-Speicher speichern können, verwenden Sie diesen Befehl:
# vorausgesetzt, Ihr aktuelles Verzeichnis enthält die Dateien "public.pem" und "private.pem" $ vault kv put kv/apisix/consumer/jim/jwt-auth public_key=@public.pem private_key=@private.pem Erfolg! Daten geschrieben nach: kv/apisix/consumer/jim/jwt-auth
-
Der öffentliche Schlüssel befindet sich in der Consumer-Konfiguration, während der private Schlüssel im Vault gespeichert ist.
$ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "username": "john", "plugins": { "jwt-auth": { "key": "user-key", "algorithm": "RS256", "public_key": "-----BEGIN PUBLIC KEY-----\n……\n-----END PUBLIC KEY-----" "vault": {} } } }'
Dieses Plugin verwendet den RSA-öffentlichen Schlüssel aus der Consumer-Konfiguration und verwendet den privaten Schlüssel, der direkt aus dem Vault abgerufen wird.
Vault im Plugin deaktivieren
Um die Vault-Suche im jwt-auth
-Plugin zu deaktivieren, entfernen Sie einfach das leere Vault-Objekt aus der Consumer-Plugin-Konfiguration (in diesem Fall jack
). Dadurch sucht das JWT-Plugin nach Signiergeheimnissen (sowohl HS256/HS512 als auch RS512-Schlüsselpaare) in der Plugin-Konfiguration für nachfolgende Anfragen an die URI-Route, für die die jwt-auth
-Konfiguration aktiviert wurde. Selbst wenn Sie die Vault-Konfiguration in der APISIX-config.yaml
aktiviert haben, wird keine Anfrage an den Vault-Server gesendet.
APISIX-Plugins werden hot-reloaded, daher ist kein Neustart von APISIX erforderlich.
$ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"username": "jack",
"plugins": {
"jwt-auth": {
"key": "test-key",
"secret": "my-secret-key"
}
}
}'
Zusammenfassung
Dieser Artikel bringt Ihnen die kommende Veröffentlichung der Vault-Apache-APISIX-Integration und die damit verbundenen Details.
Fühlen Sie sich frei, eine Diskussion in GitHub Discussions zu starten oder über die Mailingliste zu kommunizieren.