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 VAULTJWT_PATH- setta il percorso del JSON Web Token fornito da KubernetesSERVICE_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.