Wstęp teoretyczny

Aby instancja Vault była gotowa do działania po jej zainstalowaniu, musimy wykonać dwie rzecz:

  • zaincjalizować Vaulta
  • wykonać unsealing, czyli brzydko tłumacząc odpieczętować Vaulta

Unsealing musimy wykonywać przy każdym uruchomieniu Vaulta. Jest tak dlatego, że Vault po uruchomieniu nie ma dostępu do pamięci, w której są zapisane wszystkie nasze sekrety. Dlatego konieczne jest dostarczenie do Vaulta klucz, za pomocą którego będzie on w stanie odszyfrować dane.

Inicjalizacja z kolei jest jednorazową akcją. Podczas incjalizacji Vault generuje następujące klucze:

  • unseal keys
  • encryption keys
  • root token

Jak łatwo się domyślić Unseal keys są używane do wykonania operacji Unseal.

Możemy wyróżnić trzy opcje Unsealowania Vaulta:

  • ręczny unsealing
  • Auto-unseal
  • Transit Unseal – de facto jedna z opcji Auto-unseal

Manualny unseal

Ręczny unsealing jest najprostszy i nie wymaga żadnej dodatkowej konfiguracji. Vault generuje root key (nie mylić z root tokenem) i wykorzystując algorytm Shamir’s Secret Sharing dzieli klucza na kawałki. Podczas inicjalizacji możemy określić na ile części ma zostać podzielony root key oraz ile części będzie wymagane do wykonania ręcznego unseala. Takie “częściowe klucze” rozdysponowuje się do różnych osób odpowiedzialnych za zarządzanie Vaultem. Zwiększa to bezpieczeństwo, gdyż pojedyncza osoba nie jest w posiadaniu root key i tym samym nie jest w stanie odszyfrować wszystkich danych. Ręczny unseal jest opcją niezależną od systemów zewnętrznych i bardzo elastyczną. Może jednak być bolesny w utrzymaniu, gdy mamy wiele klastrów Vaulta, wiele kluczy i wielu posiadaczy kluczy.

Vault Manual unseal

Auto-unseal

Automatyczny unseal zmniejsza złożoność operacyjną i czyni zarządzanie Vaultem mniej wymagającym. W tym podejściu delegujemy odpowiedzialność za zabezpieczenie root key do zewnętrznej usługi np. AWS KMS lub do fizycznego modułu szyfrującego np. HSM. Vault skonfigurowany do automatycznego unselowania, sam łączy się z serwisem/urządzeniem i wykonuje odpieczętowanie. Nie jest już wymagana żadna ręczna akcja.

Vault Auto-unseal

Transit Auto-unseal

Powiedzieliśmy sobie, że Transit Auto-unseal jest jedną z opcji Auto-unseal. To co wyróżnia to podejście, to fakt, że nie polegamy na zewnętrznej usłudze/urządzeniu, ale na innej instancji Vaulta (najczęściej takiej, nad którą również trzymamy piecze). W ten sposób możemy umieścić jednego centralnego Vaulta, który będzie odpowiedzialny za Auto-unseal innych instancji Vaulta.

Vault Transit Auto-unseal

Transit Auto-unseal setup

Czas na konkrety. W dalszej części pokaże Ci jak skonfigurować Vaulta, aby działał on w trybie Transit Auto-unseal. Będziemy działać oczywiście w Kubernetesie więc potrzebny będzie Ci klaster np. lokalny minikube bądź k3s.

Na początek w celu odizolowania środowisk tworzymy oddzielne namespace.

# namespace dla centralnego Vaulta, który będzie unselował inne Vaulty
kubectl create ns vault

# namespace dla Vault, którego będziemy unsealować
kubectl create ns vault-a 

Na pierwszy ogień idzie instalacja Vaulta centralnego. Dla ułatwienia posłużymy się Helmowymi Chartami. Poniżej konfiguracja Vaulta HA z dwoma replikami. Vault centralny będzie miał konfiguracje do unselowania ręcznego, ale równie dobrze mógłby być skonfigurowany unsealing automatyczny z użyciem AWS KMS.

server:
	affinity: ""
  ha:
    enabled: true
    replicas: 2
    raft: 
      enabled: true 
# przełącza namespace na Vaulta centralnego
kns vault

helm repo add hashicorp https://helm.releases.hashicorp.com
helm install vault hashicorp/vault -f vault-central-helm-values.yml 

Pora na incjalizacje Vaulta centralnego. Poniższą komendą zanicjujemy Vaulta i podzielimy root key na 4 części (unseal keys). Definijujemy takż, że 2 unseal keys będą wymagane, aby utworzyć root key w celu unselowania Vaulta, aby ten mógł utworzyć encrytpion key. To właśnie encryption key jest używany przez Vaulta do szyfrowanie i odszyfrowywanie sekretów.

kubectl exec vault-0 -- vault operator init \
    -key-shares=4 \
    -key-threshold=2 \
    -format=json > vault-central-keys.json 

Vault wygeneruje 4 unseal key zakodowane w base64 i hex oraz root token odpowiedzialny za autoryzację w Vault. Root token posłuży nam jeszcze później.

{
  "unseal_keys_b64": [
    "4Wm5BYsNal+zMbsb3ewNbi6zLtKIOXz3L+NFX7jw0/3T",
    "miasg31FmPJqx9LrnPaVEuG639fvjAqZF3gp4ZlKw+wK",
    "EyVw9nQH/T+3zsa4HbPJ2s15l6B5MizMKQlKqs9taFzX",
    "zc7eU9MEvy9AaV4FPSQe7Jla2LcqSjS8KNPFDlQs0Rcg"
  ],
  "unseal_keys_hex": [
    "e169b9058b0d6a5fb331bb1bddec0d6e2eb32ed288397cf72fe3455fb8f0d3fdd3",
    "9a26ac837d4598f26ac7d2eb9cf69512e1badfd7ef8c0a99177829e1994ac3ec0a",
    "132570f67407fd3fb7cec6b81db3c9dacd7997a079322ccc29094aaacf6d685cd7",
    "cdcede53d304bf2f40695e053d241eec995ad8b72a4a34bc28d3c50e542cd11720"
  ],
  "unseal_shares": 4,
  "unseal_threshold": 2,
  "recovery_keys_b64": [],
  "recovery_keys_hex": [],
  "recovery_keys_shares": 0,
  "recovery_keys_threshold": 0,
  "root_token": "hvs.NbXRWfYNI4PmA860aBlC4onU" 

Gdy mamy już unseal keys czas na unselowania centralnego Vaulta. Mamy uruchomione dwie repliki Vaulta i obie z nich musimy ręcznie unselować. W tym celu do każdego poda musimy przekazać conajmniej 2 unseal_keys_b64 zgodnie z progiem, jaki skonfigurowaliśmy podczas incjalizacji.

kubectl exec vault-0 -- vault operator unseal 4Wm5BYsNal+zMbsb3ewNbi6zLtKIOXz3L+NFX7jw0/3T
kubectl exec vault-0 -- vault operator unseal miasg31FmPJqx9LrnPaVEuG639fvjAqZF3gp4ZlKw+wK 

Powtarzamy krok dla drugiego poda ale wcześniej musimy podłączyć drugiego Vaulta do klastra Raft, gdyż taki typ storage jest domyślnie skonfigurowany.

kubectl exec -ti vault-1 -- vault operator raft join http://vault-0.vault-internal:8200

kubectl exec vault-1 -- vault operator unseal 4Wm5BYsNal+zMbsb3ewNbi6zLtKIOXz3L+NFX7jw0/3T
kubectl exec vault-1 -- vault operator unseal miasg31FmPJqx9LrnPaVEuG639fvjAqZF3gp4ZlKw+wK 

Czas na skonfigurowanie kluczowego elementu odpowiedzialnego za ochronę i przekazywanie root key.Transit Secret Engine. Transit Secret Engine jest jednym z typów sekretów jaki oferuje Vault. Można go określi mianem “encryption as a service” i porównać do usługi AWS KMS.

# zestaw tunel do Vaulta w oddzielnym oknie terminala 
kubectl port-forward vault-0 -n vault 8200:8200

# ustaw adres Vaulta aby używać lokalnie Vault CLI
export VAULT_ADDR=http://127.0.0.1:8200

# użyj 'root_token' wygenerowanego podczas incjalizacji do autryzacji w Vault
vault login

# utwórz transit secret engine
vault secrets enable transit
vault write -f transit/keys/autounseal 

Utworzymy teraz token autoryzujący wszystkie Vaulty do Vaulta centralnego i jego Transit Secret Engine. Do tego potrzebna jest nam również powiązana polityka. Zapisz poniższą politykę do pliku autounseal-policy.hcl.

path "transit/encrypt/autounseal" {
   capabilities = [ "update" ]
}

path "transit/decrypt/autounseal" {
   capabilities = [ "update" ]
} 
# utwórz politykę
vault policy write autounseal autounseal-policy.hcl

# utwórz Vault token
$ vault token create -orphan -policy=autounseal -period=24h

Key                  Value
---                  -----
token                hvs.CAESIP_A7TaC9kt4yUeqg5_bJNiOJElb4UbA01xoV9Rk4ei6Gh4KHGh2cy5zVXpaa3A1MG9uOEZrNXN2a3J0TGl0cHU
token_accessor       wkTM4nsF0ehkRvIuBD9cedHC
token_duration       24h
token_renewable      true
token_policies       ["autounseal" "default"]
identity_policies    []
policies             ["autounseal" "default"] 

Token utworzony. To właśnie za jego pomocą będziemy mogli autoryzować inne Vaulty w Vaulcie centralnym, który będzie służył do unselowania. Zauważ, że token ten jest orphan co oznacza, że nie ma tokenu nadrzędnego (parent tokenu) a więc nie będzie on usuwany wraz z przodkiem. Co warto zaznaczyć, auto-unseal token domyślnie odnawiany będzie automatycznie.

Czas ma przygotowanie Helm Chartu dla drugiego Vaulta. Jak widzisz poniżej skonfigurowany został transit unsealing. Konfiguracja zawiera adres Vaulta centralnego, jak i token wygenerowany w poprzednim kroku.

server:
  standalone:
    enabled: true
    config: |
      disable_mlock = true
      ui=true

      storage "file" {
        path = "/vault/data"
      }

      listener "tcp" {
        address     = "127.0.0.1:8200"
        tls_disable = "true"
      }

      seal "transit" {
        address = "http://vault.vault:8200"
        token = "hvs.CAESIP_A7TaC9kt4yUeqg5_bJNiOJElb4UbA01xoV9Rk4ei6Gh4KHGh2cy5zVXpaa3A1MG9uOEZrNXN2a3J0TGl0cHU"
        disable_renewal = "false"
        key_name = "autounseal"
        mount_path = "transit/"
        tls_skip_verify = "true"
      } 

Zainstaluj Vaulta w dedykowanym namespace.

kns vault-a
helm install vault hashicorp/vault -f vault-auto-unseal-helm-values.yml 

Ostatnim krokiem jest podobnie jak w przypadku Vaulta centralnego incjalizacja.

kubectl exec -it vault-0 -- vault operator init

Recovery Key 1: FFMLznSZq9wh/0CJwKLJWKkI9BrK/hjF6ySDYl9a19Ie
Recovery Key 2: qRfrdpkuEcXsF+dFh1Geru8VHkiL/hWUW+vY25twlwT1
Recovery Key 3: dX8sed7Dv8kI8kfFuYWDeQlagoikEVBpV5lZqH4ORnEh
Recovery Key 4: TCCplv+KvZHEOlICQU6eb67hGccufiqcZGkSiGlpQPkx
Recovery Key 5: ictL+c9czgMO+ME8qoTcGpgsvymEcORN7MkrpDE28x4a

Initial Root Token: hvs.6umGyyta9xrjq0q7Cv09Hr8X

Success! Vault is initialized 

Możesz teraz zweryfikować czy Vault poprawnie przeprowadził proces automatycznego unselowania.

kubectl exec -it vault-a -- vault status

Key                      Value
---                      -----
Recovery Seal Type       shamir
Initialized              true
Sealed                   false
Total Recovery Shares    5
Threshold                3
Version                  1.12.0
Build Date               2022-10-10T18:14:33Z
Storage Type             file
Cluster Name             vault-cluster-7a11a0ae
Cluster ID               a883d977-e70a-6367-3148-9c7a2c246897
HA Enabled               false 

Zauważ różnicę, że Vault z automatycznym unselowaniem podczas incjalizacji generuje Recovery keys zamiast Unseal Keys. Kluczy odzyskiwania nie można użyć do unselowania Vaulta. Pełnią one jedynie funkcje autoryzacyjne. Pozwalają np. wygenerować nowy root token.

Podsumowanie

Jedną z praktyk metodyki DevOps jest automatyzacja. Chcemy automatyzować procesy, aby zredukować ilość manualnych akcji. Benefitem jest nie tylko zaoszczędzony czas, ale również zmniejszenie podatności na błąd ludzki. Niewątpliwie Auto-unseal Vaulta jest opcją, która świetnie wpisuje się w te założenia. Jednakże z punktu widzenia bezpieczeństwa, czasami wprowadzenie manualnego kroku wymagającego interwencji człowieka jest przydatne, a niekiedy konieczne. W powyższej konfiguracji Transit Auto-unseal mamy połączenie dwóch podejść. Manualny proces unselowania Vaulta centralnego oraz automatyczny proces unselowania Vaultów zależnych. Warto również zaznaczyć, że nasze rozwiązanie jest cloud-agnostic. Nie opieramy się tutaj na żadnej zewnętrznej usłudze szyfrującej. Nasze rozwiązanie jest więc łatwiej przenaszalne i mamy nad nim większą kontrolę. Słabym punktem tego setupu jest jednak wprowadzenie elementu krytycznego w całej tej układance. Taki elementem jest właśnie Vault centralny. Jakkolwiek awaria tego komponentu może przysporzyć sporo problemów. Pytaniem, które należy sobie tutaj postawić jest, jak zapewnić wysoką dostępność i odporność na błędy dla krytycznego Vaulta. To jednak temat na odrębne przemyślenia i post.

Close Menu