Instalacja HashiCorp Vault na NSIS Magnum
W Kubernetes Secret jest obiektem zawierającym hasła, tokeny, klucze lub inne dane o niewielkich rozmiarach. Korzystanie z Secrets zapewnia, że prawdopodobieństwo ujawnienia poufnych danych podczas tworzenia, uruchamiania i edytowania podów jest znacznie mniejsze. Głównym problemem jest to, że Secrets są przechowywane w postaci niezaszyfrowanej w etcd, więc każdy, kto ma dostęp do Secrets, może je odczytać przez
API, jak również każdy, kto
może utworzyć poda lub wdrożenie w przestrzeni nazw
może również pobierać lub modyfikować sekret.
W celu zwiększenia bezpieczeństwa klastra można zastosować szereg strategii lub zainstalować specjalistyczne rozwiązanie, takie jak HashiCorp Vault. Oferuje ono następujące funkcje:
bezpieczne przechowywanie wszelkiego rodzaju sekretów - haseł, certyfikatów TLS, poświadczeń bazy danych, kluczy szyfrowania API i innych,
szyfrowanie wszystkich danych,
dynamiczna obsługa poświadczeń,
szczegółowe zasady dostępu dla użytkowników, aplikacji i usług,
rejestrowanie i audyt wykorzystania danych,
odwoływanie lub usuwanie dowolnych kluczy lub sekretów,
ustawianie automatycznej rotacji sekretów - zarówno dla administratorów, jak i użytkowników.
W tym artykule zainstalujemy HashiCorp Vault w klastrze Magnum Kubernetes w chmurze NSIS.
Co zostanie omówione?
Instalacja samodzielnie podpisanych certyfikatów TLS za pomocą CFSSL
Generowanie certyfikatów umożliwiających szyfrowanie ruchu za pomocą Vault
Instalacja backendu pamięci masowej Consul dla zapewnienia wysokiej dostępności
Instalacja Vault
Pięczętowanie i rozpieczętowywanie Vault
Rozpieczętowywanie Vault
Uruchamianie interfejsu użytkownika Vault
Przywracanie livenessProbe do wartości produkcyjnej
Rozwiązywanie problemów
Wymagania wstępne
Nr 1 Konto
Jest wymagane konto hostingowe NSIS z dostępem do interfejsu Horizon: https://horizon.cloudferro.com.
Nr 2 Znajomość kubectl
Musisz mieć uruchomiony odpowiedni klaster Kubernetes z wskazującym na niego kubectl, patrz artykuł Jak uzyskać dostęp do klastra Kubernetes po wdrożeniu przy użyciu Kubectl na NSIS OpenStack Magnum?.
Nr 3 Znajomość obsługi wykresów Helm
Charty Helm na platformie Kubernetes są przedstawione w artykule
Wdrażanie Helm Charts na klastrach Magnum Kubernetes w chmurze NSIS
Krok 1 Instalacja CFSSL
Aby zapewnić, by komunikacja Vault z klastrem była szyfrowana, musimy dostarczyć certyfikaty TLS.
Użyjemy samopodpisanych certyfikatów TLS wydanych przez prywatny urząd certyfikacji. Do ich wygenerowania użyjemy narzędzi CFSSL cfssl i cfssljson.
cfssl jest narzędziem CLI. cfssljson pobiera dane wyjściowe JSON z cfssl i zapisuje certyfikaty, klucze i CSR (żądania podpisania certyfikatu).
Musimy pobrać pliki binarne obu narzędzi cfssl i cfssljson ze strony https://github.com/cloudflare/cfssl i oznaczyć je jako wykonywalne:
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.6.3/cfssl_1.6.3_linux_amd64 -o cfssl
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssljson_1.6.1_linux_amd64 -o cfssljson
chmod +x cfssl
chmod +x cfssljson
Następnie musimy dodać je do naszej ścieżki:
sudo mv cfssl cfssljson /usr/local/bin
Krok 2 Generowanie certyfikatów TLS
Zanim zaczniemy, utwórzmy dedykowaną przestrzeń nazw, w której będą znajdować się wszystkie zasoby Kubernetes związane z Vault:
kubectl create namespace vault
Będziemy musieli wydać dwa zestawy certyfikatów. Pierwszy zestaw będzie certyfikatem głównym dla urzędu certyfikacji. Drugi będzie odwoływał się do certyfikatu CA i utworzy rzeczywisty certyfikat Vault.
Aby utworzyć żądanie klucza dla CA, oprzemy je na pliku JSON ca-csr.json. Utwórz ten plik w swoim ulubionym edytorze i jeśli chcesz, zastąp szczegółowe dane certyfikatu zgodnie z własnym przypadkiem:
ca-csr.json
{
"hosts": [
"cluster.local"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "Poland",
"L": "Warsaw",
"O": "MyOrganization"
}
]
}
Następnie należy wydać polecenie wygenerowania samopodpisanego certyfikatu głównego urzędu certyfikacji.
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
Powinny zostać wyświetlone dane wyjściowe podobne do poniższych:
2023/01/02 15:27:36 [INFO] generating a new CA key and certificate from CSR
2023/01/02 15:27:36 [INFO] generate received request
2023/01/02 15:27:36 [INFO] received CSR
2023/01/02 15:27:36 [INFO] generating key: rsa-2048
2023/01/02 15:27:36 [INFO] encoded CSR
2023/01/02 15:27:36 [INFO] signed certificate with serial number 472447709029717049436439292623827313295747809061
W rezultacie generowane są trzy elementy:
klucz prywatny,
CSR oraz
certyfikat z podpisem własnym (ca.pem, ca.csr, ca-key.pem).
Następnym krokiem jest utworzenie certyfikatów Vault, które odwołują się do prywatnego urzędu certyfikacji. W tym celu należy najpierw utworzyć plik konfiguracyjny ca-config.json, aby zastąpić domyślną konfigurację. Jest to przydatne zwłaszcza w przypadku zmiany ważności certyfikatu:
ca-config.json
{
"signing": {
"default": {
"expiry": "17520h"
},
"profiles": {
"default": {
"usages": ["signing", "key encipherment", "server auth", "client auth"],
"expiry": "17520h"
}
}
}
}
Następnie wygeneruj klucze Vault, odwołując się do tego pliku i kluczy CA:
cfssl gencert \
-ca ./ca.pem \
-ca-key ./ca-key.pem \
-config ca-config.json \
-profile default \
-hostname="vault,vault.vault.svc.cluster.local,localhost,127.0.0.1" \
ca-csr.json | cfssljson -bare vault
Wynik będzie następujący:
2023/01/02 16:19:52 [INFO] generate received request
2023/01/02 16:19:52 [INFO] received CSR
2023/01/02 16:19:52 [INFO] generating key: rsa-2048
2023/01/02 16:19:52 [INFO] encoded CSR
2023/01/02 16:19:52 [INFO] signed certificate with serial number 709743788174272015258726707100830785425213226283
Ponadto w katalogu roboczym zostaną utworzone kolejne trzy pliki: vault.pem, vault.csr oraz vault-key.pem.
Ostatnim krokiem jest zapisanie wygenerowanych kluczy jako sekretów Kubernetes TLS w naszym klastrze:
kubectl -n vault create secret tls tls-ca --cert ./ca.pem --key ./ca-key.pem -n vault
kubectl -n vault create secret tls tls-server --cert ./vault.pem --key ./vault-key.pem -n vault
Nazwy tych sekretów odzwierciedlają domyślne nazwy wykresu Helm Vault.
Krok 3 Instalacja chart Helm Consul
Backend Consul zapewni wysoką dostępność naszej instalacji Vault. Consul będzie działać w przestrzeni nazw vault, którą już utworzyliśmy.
Oto plik konfiguracyjny zastępujący chart Helm Consul: consul-values.yaml.
consul-values.yaml
global:
datacenter: vault-kubernetes-guide
client:
enabled: true
server:
replicas: 1
bootstrapExpect: 1
disruptionBudget:
maxUnavailable: 0
Teraz zainstaluj repozytorium hashicorp chartów Helm i sprawdź, czy znajduje się w nim vault:
helm repo add hashicorp https://helm.releases.hashicorp.com
helm search repo hashicorp/vault
W ostatnim kroku zainstaluj chart Consul:
helm install consul hashicorp/consul -f consul-values.yaml -n vault
Raport o powodzeniu instalacji wygląda następująco:
NAME: consul
LAST DEPLOYED: Thu Feb 9 18:52:58 2023
NAMESPACE: vault
STATUS: deployed
REVISION: 1
NOTES:
Thank you for installing HashiCorp Consul!
Your release is named consul.
Wkrótce kilka podów Consul zostanie wdrożonych w przestrzeni nazw vault. Aby to sprawdzić, uruchom następujące polecenie:
kubectl get pods -n vault
Poczekaj, aż wszystkie pody będą w stanie Running, a następnie przejdź do następnego kroku.
Krok 4 Instalacja chart Helm Vault
Jesteśmy teraz gotowi, by zainstalować Vault.
Najpierw dostarczymy plik vault-values.yaml, który zastąpi plik konfiguracyjny wykresu chart Helm. Te zmienione parametry zapewniają włączenie szyfrowania, wysokiej dostępności, ustawienie większego czasu dla readinessProbe i udostępnienie interfejsu użytkownika jako typu usługi LoadBalancer:
vault-values.yaml
# Vault Helm Chart Value Overrides
global:
enabled: true
tlsDisable: false
injector:
enabled: true
image:
repository: "hashicorp/vault-k8s"
tag: "0.14.1"
resources:
requests:
memory: 500Mi
cpu: 500m
limits:
memory: 1000Mi
cpu: 1000m
server:
# These Resource Limits are in line with node requirements in the
# Vault Reference Architecture for a Small Cluster
image:
repository: "hashicorp/vault"
tag: "1.9.2"
# For HA configuration and because we need to manually init the vault,
# we need to define custom readiness/liveness Probe settings
readinessProbe:
enabled: true
path: "/v1/sys/health?standbyok=true&sealedcode=204&uninitcode=204"
livenessProbe:
enabled: true
path: "/v1/sys/health?standbyok=true"
initialDelaySeconds: 360
extraEnvironmentVars:
VAULT_CACERT: /vault/userconfig/tls-ca/tls.crt
# extraVolumes is a list of extra volumes to mount. These will be exposed
# to Vault in the path `/vault/userconfig/<name>/`.
# These reflect the Kubernetes vault and ca secrets created
extraVolumes:
- type: secret
name: tls-server
- type: secret
name: tls-ca
standalone:
enabled: false
# Run Vault in "HA" mode.
ha:
enabled: true
replicas: 3
config: |
ui = true
listener "tcp" {
tls_disable = 0
address = "0.0.0.0:8200"
tls_cert_file = "/vault/userconfig/tls-server/tls.crt"
tls_key_file = "/vault/userconfig/tls-server/tls.key"
tls_min_version = "tls12"
}
storage "consul" {
path = "vault"
address = "consul-consul-server:8500"
}
# Vault UI
ui:
enabled: true
serviceType: "LoadBalancer"
serviceNodePort: null
externalPort: 8200
Następnie uruchom instalację:
helm install vault hashicorp/vault -n vault -f vault-values.yaml
W rezultacie zostanie utworzonych kilka podów:
kubectl get pods -n vault
NAME READY STATUS RESTARTS AGE
consul-consul-client-655fq 1/1 Running 0 104s
consul-consul-client-dkngt 1/1 Running 0 104s
consul-consul-client-nnbnl 1/1 Running 0 104s
consul-consul-connect-injector-8447d8d97b-8hkj8 1/1 Running 0 104s
consul-consul-server-0 1/1 Running 0 104s
consul-consul-webhook-cert-manager-7c4ccbdd4c-d89bw 1/1 Running 0 104s
vault-0 1/1 Running 0 23s
vault-1 1/1 Running 0 23s
vault-2 1/1 Running 0 23s
vault-agent-injector-6c7cfc768-kv968 1/1 Running 0 23s
Pięczętowanie i rozpieczętowywanie Vault
Zaraz po instalacji serwer Vault uruchamia się w stanie sealed. Serwer wie, gdzie i jak uzyskać dostęp do fizycznej pamięci masowej, ale z założenia nie ma klucza do jej odszyfrowania. Jedyne operacje, które można wykonać, gdy Vault jest zapieczętowany, to:
odpieczętowanie Vault i
sprawdzenie stanu zapieczętowania.
Proces odwrotny, czyli odpieczętowanie, polega na utworzeniu klucza głównego w formacje tekstowym niezbędnego do odczytania klucza deszyfrującego.
W rzeczywistości istniałby administrator, który mógłby najpierw wygenerować tak zwane key shares lub unseal keys, czyli zestaw dokładnie pięciu ciągów tekstowych. Następnie rozdzieliłby te klucze między dwie lub więcej osób, tak aby sekrety były trudne do zdobycia dla potencjalnego atakującego. Aby przeprowadzić odpieczętowanie, należy podać w Vault co najmniej trzy z tych pięciu ciągów (w dowolnej kolejności).
W tym artykule jesteś jednak zarówno administratorem, jak i użytkownikiem, zatem możesz skonfigurować wszystko tak, jak chcesz. Najpierw:
wygeneruj klucze i zachowaj je w dostępnym miejscu, a następnie
wprowadź trzy z tych pięciu ciągów znaków z powrotem do systemu.
Będziesz mieć ograniczoną, ale wystarczającą ilość czasu na wprowadzenie kluczy – wartość livenessProbe w pliku vault-values.yaml wynosi 360 sekund, co powinni zapewnić wystarczająco dużo czasu na wprowadzenie kluczy.
Na końcu artykułu pokażemy, jak interaktywnie ustawić ten czas na 60 sekund, aby klaster mógł częściej sprawdzać stan podów.
Krok 5 Rozpieczętowywanie Vault
Trzy węzły w klastrze Kubernetes reprezentują Vault i noszą nazwy vault-0, vault-1, vault-2. Aby Vault był funkcjonalny, należy odpieczętować wszystkie trzy węzły.
Aby rozpocząć, wejdź do kontenera w vault-0:
kubectl -n vault exec -it vault-0 -- sh
Następnie uzyskaj z poda klucze:
vault operator init
Wynik będzie następujący: otrzymasz 5 kluczy odpieczętowujących i token root. Zapisz te klucze w Notatniku, aby mieć do nich później wygodny dostęp.
Unseal Key 1: jcJj2ukVBNG5K01PX3UkskPotc+tGAvalG5CqBveS6LN
Unseal Key 2: OBzqfTYL9lmmvuewk85kPxpgc0D/CDVXrY9cdBElA3hJ
Unseal Key 3: M6QysiGixui4SlqB7Jdgv0jaHn8m45V91iabrxRvNo6v
Unseal Key 4: H7T5BHR2isbBSHfu2q4aKG0hvvA13uXlT9799whxmuL+
Unseal Key 5: rtbXv3TqdUeN3luelJa8OOI/CKlILANXxFVkyE/SKv4c
Initial Root Token: s.Pt7xVk5rShSuIJqRPqBFWY5H
Następnie z poziomu poda vault-0 odpieczętuj go, wpisując polecenie:
vault operator unseal
Zostanie wyświetlony monit o podanie klucza. Następnie wklej klucz 1 z Notatnika. Powtórz operację 3 razy w podzie vault-0, za każdym razem podając inny klucz z uprzednio wygenerowanych pięciu.
Cały proces wygląda następująco:
Po trzeciej operacji wartość Initialized zmienia się na true, a sealed na false:
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
... ...
Pod jest odpieczętowany.
Teraz powtórz tę samą operację dla **podów ** vault-1 i vault-2 **.
Aby przestać korzystać z konsoli w vault-0, naciśnij na klawiaturze kombinację klawiszy Ctrl-D. Następnie przejdź do poda vault-1 za pomocą polecenia
kubectl -n vault exec -it vault-1 -- sh
i odblokuj go, wprowadzając co najmniej trzy klucze. Następnie wykonaj podobną procedurę dla poda vault-2. Dopiero po odblokowaniu wszystkich trzech podów Vault stanie się aktywny.
Krok 6 Uruchomienie interfejsu użytkownika Vault
W naszej konfiguracji interfejs użytkownika Vault jest udostępniony na porcie 8200 dedykowanego load balancera, który został utworzony.
Aby sprawdzić, czy load balancer działa, uruchom polecenie
kubectl -n vault get svc
Sprawdź zewnętrzny adres IP load balancera (może to potrwać kilka minut, aż zewnętrzny adres IP będzie dostępny):
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
...
vault-ui LoadBalancer 10.254.212.213 46.60.17.201 8200:32091/TCP 143m
Wpisz w przeglądarce zewnętrzny adres IP, określając protokół HTTPS i port 8200. Witryna może poprosić o certyfikat i może zgłosić, że kontynuowanie może być niebezpieczne. Należy zaakceptować wszystkie informacje o zagrożeniach, interfejs użytkownika Vault powinien być wtedy dostępny, podobnie jak na poniższej ilustracji. Aby się zalogować, należy podać uzyskany wcześniej token:
Możesz teraz rozpocząć korzystanie z Vault.
Przywracanie livenessProbe do wartości produkcyjnej
W Kubernetes livenessProbe to czas, przez który system sprawdza stan węzłów. W normalnych okolicznościach nie byłoby to problemem, ale jeśli w tym czasie nie odpieczętujesz Vault, odpieczętowanie nie zadziała. W normalnych okolicznościach ta wartość wynosiłaby 60 sekund, więc w przypadku jakichkolwiek zakłóceń system zareagowałby w ciągu jednej minuty zamiast sześciu. Jednak skopiowanie i wprowadzenie trzech ciągów w czasie poniżej jednej minuty byłoby trudne, gdyby w pliku vault-values.yaml była obecna wartość 60. Prawie na pewno zostałby zgłoszony błąd Kubernetes 137, co oznacza, że wymagane operacje nie zostały wykonane na czas.
W pliku vault-values.yaml czas aktywacji livenessProbe równy 360 sekund jest zdefiniowany w poniższej sekcji:
livenessProbe:
enabled: true
path: "/v1/sys/health?standbyok=true"
initialDelaySeconds: 360
Aby przywrócić dla livenessProbe wartość 60, wykonaj polecenie:
kubectl edit statefulset vault -n vault
Możesz teraz uzyskać dostęp do odpowiednika pliku vault-values.yaml w klastrze Kubernetes. Polecenie automatycznie wywoła edytor taki jak Vim, więc naciśnij klawisz O na klawiaturze, aby móc zmienić w nim wartość:
Kiedy zakończysz, zapisz plik i zamknij Vim za pomocą standardowych poleceń :w i :q.
Rozwiązywanie problemów
Sprawdź zdarzenia, które mogą wskazać, co należy poprawić:
kubectl get events -n vault
Jeśli wystąpią błędy i chcesz usunąć instalację Vault, aby powtórzyć proces od nowa, pamiętaj, że obiekt MutatingWebhookConfiguration może pozostać w domyślnej przestrzeni nazw. Usuń go przed ponowną próbą:
kubectl get MutatingWebhookConfiguration
kubectl delete MutatingWebhookConfiguration consul-consul-connect-injector
kubectl delete MutatingWebhookConfiguration vault-agent-injector-cfg
Co można zrobić dalej?
Teraz serwer Vault jest częścią klastra i można z niego korzystać z adresu IP, pod którym został zainstalowany.
Innym sposobem na zwiększenie bezpieczeństwa Kubernetes jest zabezpieczenie aplikacji za pomocą protokołu HTTPS za pomocą ingress: