HashiCorp Vault

Lo storaggio e l'uso di Secrets, che sono solo trascodificati Base64, è un potenziale problema di sicurezza.

Occorrerebbe accedere ai Secrets in modo più sicuro, autenticato e crittografato.

La ditta HashiCorp fornisce il prodotto Vault, un ambiente generico di storaggio ed accesso di informazioni private, incluso i Secrets di Kubernetes.

La versione Kubernetes del prodotto Vault è disponibile in versione base come chart di Helm.

Versioni più complete e complesse sono soggette a licenza e a pagamento.

Il seguente esempio illustra l'utilizzo di Vault per Kubernetes.

Installazione di Vault

Creare la directory di lavoro:

mkdir -p ~/vault
cd ~/vault

Aggiungere il repository helm della HashiCorp:

helm repo add hashicorp https://helm.releases.hashicorp.com

Compiere un upfate:

helm repo update

Ricercare i chart di Vault:

helm search repo hashicorp/vault

Generate un file di configurazione per Vault:

cat > helm-vault-raft-values.yml <<EOF
server:
   affinity: ""
   ha:
      enabled: true
      raft: 
         enabled: true
         setNodeId: true
         config: |
            cluster_name = "vault-integrated-storage"
            storage "raft" {
               path    = "/vault/data/"
            }

            listener "tcp" {
               address = "[::]:8200"
               cluster_address = "[::]:8201"
               tls_disable = "true"
            }
            service_registration "kubernetes" {}
EOF

Installare il chart di Vault:

helm install vault hashicorp/vault --values helm-vault-raft-values.yml

Listare i pods:

kubectl get pods

Occorre un tempo anche considerevole perchè la situazione si stabilizzi. Il risultato finale è:

NAME                                    READY   STATUS    RESTARTS   AGE
vault-0                                 0/1     Running   0          2m12s
vault-1                                 0/1     Running   0          2m12s
vault-2                                 0/1     Running   0          2m12s
vault-agent-injector-56b65c5cd4-k7lbt   1/1     Running   0          2m13s

Configurazione di Vault

Inizializzare vault-0 con un key share e un key threshold:

kubectl exec vault-0 -- vault operator init \
    -key-shares=1 \
    -key-threshold=1 \
    -format=json > cluster-keys.json

Il file cluster-keys.json contiene le chiavi di accesso.

Questo è solo un esempio, ed è un procedimento insicuro. Il file delle chiavi dovrebbe immediatamente essere crittografato.

Visualizzare la chiave di sblocco:

jq -r ".unseal_keys_b64[]" cluster-keys.json

Porla in una variabile:

VAULT_UNSEAL_KEY=$(jq -r ".unseal_keys_b64[]" cluster-keys.json)

Sbloccare il Vault sul pod vault-0:

kubectl exec vault-0 -- vault operator unseal $VAULT_UNSEAL_KEY

Unire vault-1 e vault-2 al cluster Raft.

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

Usare la chiave di sblocco, digitata a mano a richiesta, per sbloccare vault-1 e vault-2

kubectl exec -ti vault-1 -- vault operator unseal $VAULT_UNSEAL_KEY
kubectl exec -ti vault-2 -- vault operator unseal $VAULT_UNSEAL_KEY

Porre un Secret nel Vault

Visualizzare il root token:

jq -r ".root_token" cluster-keys.json

Collegarsi al pod vault-0:

kubectl exec --stdin=true --tty=true vault-0 -- /bin/sh

Login al Vault. Viene richiesto il root token:

vault login

Se ha successo, l'autenticazione è cached e non c'è più bisogno di compiere il login.

Abilitare il secrets engine di integrazione con Kubernetes:

vault secrets enable -path=secret kv-v2

Creare un Secret ad un dato percorso e porvi dei dati:

vault kv put secret/webapp/config username="static-user" password="static-password"

Verificare la registrazione del Secret:

vault kv get secret/webapp/config

Uscire da vault-0:

exit

Autenticazione da Kubernetes

Vault fornisce un metodo di autenticazione Kubernetes che permette ai client di autenticarsi con un Service Account Token di Kubernetes.

Collegarsi al pod vault-0:

kubectl exec --stdin=true --tty=true vault-0 -- /bin/sh

Abilitare l'autenticazione Kubernetes:

vault auth enable kubernetes

Configurare che il metodo di autenticazione Kubernetes usi la locazione dell'API Kubernetes:

vault write auth/kubernetes/config \
    kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"

Scrivere una policy di nome webapp che dia la possibilità read dei Secret al percorso secret/data/webapp/config.

vault policy write webapp - <<EOF
path "secret/data/webapp/config" {
  capabilities = ["read"]
}
EOF

Creare un Role chiamato webapp che connette il service account name vault di Kubernetes alla policy webapp:

vault write auth/kubernetes/role/webapp \
        bound_service_account_names=vault \
        bound_service_account_namespaces=default \
        policies=webapp \
        ttl=24h

Uscire dal pod vault-0:

exit

Applicativo che Usa il Vault

Deployment

Creare il deployment di un applicativo con il manifest deployment-01-webapp.yml:

vim deployment-01-webapp.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      serviceAccountName: vault
      containers:
        - name: app
          image: hashieducation/simple-vault-client:latest
          imagePullPolicy: Always
          env:
            - name: VAULT_ADDR
              value: 'http://vault:8200'
            - name: JWT_PATH
              value: '/var/run/secrets/kubernetes.io/serviceaccount/token'
            - name: SERVICE_PORT
              value: '8080'
  • VAULT_ADDR - indirizzo del servizio di VAULT
  • JWT_PATH - setta il percorso del JSON Web Token fornito da Kubernetes
  • SERVICE_PORT - per le richieste HTTP in arrivo

Sottomettere il nostro manifest:

kubectl apply --filename deployment-01-webapp.yml

Test

In un altro terminale compiere il port forward di tutte le richieste compiute a `` alla porta 80 del pod della webapp.

kubectl port-forward \
    $(kubectl get pod -l app=webapp -o jsonpath="{.items[0].metadata.name}") \
    8888:8080

Usiamo la porta 8888 per evitare collisioni con la porta 8080, possibilmente già in uso.

Nel terminale originale provare il collegamento:

curl http://localhost:8888
password:static-password username:static-user

Cosa Succede

L'applicativo che gira alla porta 8080 del pod webapp:

  • si autentica con il token del service account di Kubernetes
  • riceve un token da Vault che gli permette di leggere il percorso secret/data/webapp/config path
  • recupera il Secret dal percorso secret/data/webapp/config
  • ne compie il display in formato JSON

Pulizia Finale

Al termine compiere pulizia dell'esercizio.

Interrompere il Port Forwarding

Rimuovere l'applicativo tramite il suo Manifest

Disinstallare il chart di Vault

Questo ne rimuove tutti i pod e naturalmente i dati di configurazione e i Secrets storati vanno persi.

Ulteriori Possibilità

  • Settare Vault con storaggio integrato nel Google Kubernetes Engine.
  • Usare Annotations nell'applicativo per interagire con il servizio Vault Injector.
  • Persistere i dati di Vault su volumi.
  • Usare il servizio Vault fornito dalla stessa HashiCorp, a licenza.

Ulteriori dettagli documentativi si trovano sul sito Vault della Hashicorp.