Skip to main content

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:

../_images/unsealing_the_pod1.png

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:

../_images/vault_created_nsis_pl1.png

Możesz teraz rozpocząć korzystanie z Vault.

../_images/start_using_vault1.png

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ść:

../_images/vim_editor_change1.png

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: