6 minutes
Avoir son authorité de certification dans Kubernetes via Vault
Dans cet article je vais détailler comment utiliser Vault pour générer des certificats et les utiliser dans Kubernetes.
Je pars du principe que :
- Vous avez un cluster Kubernetes opérationnel.
- Cert-manager est déployé dans le cluster.
- Vault est installé et opérationel.
Nous allons commencer par installer un CA dans Vault et préparer la configuration.
Création d’une CA dans Vault et configuration
Configuration de la CA
Je pars du principe que vous avez déjà une rootCA et que vous voulez créer une intermediate dans Vault.
Il y a plusieurs façon de faire voici par exemple la config terraform pour créer un backend PKI sur le path /pki.
Cela va aussi générer une CSR et va l’output sur le terminal après avoir lancé la commande terraform apply
resource "vault_mount" "pki" {
path = "pki"
type = "pki"
description = "Kubernetes CA"
# 1 years
default_lease_ttl_seconds = 31536000
# 5 years
max_lease_ttl_seconds = 157680000
}
resource "vault_pki_secret_backend_intermediate_cert_request" "vault_CA" {
depends_on = [ vault_mount.pki ]
backend = vault_mount.pki.path
type = "internal"
common_name = "Vault CA"
country = "FR"
province = "Lorraine"
organization = "McTH"
ou = "kube"
format = "pem"
private_key_format = "der"
key_type = "rsa"
key_bits = "4096"
}
output "ca_csr" {
value = vault_pki_secret_backend_intermediate_cert_request.vault_CA.csr
}
Une fois que vous avez personnalisé cette configuration vous pouvez lancer :
terraform plan -out plan
Puis :
terraform apply plan
Petit rappel : pour Terraform avec Vault comme backend : il faut setter les variables d’environnement correspondante à votre Vault pour que ce dernier s’y connecte. Assurez-vous d’avoir les variables
VAULT_TOKEN
etVAULT_ADDR
qui pointent sur votre Vault avec un utilisateur ayant suffisamment de droits.
Une fois que vous avez la CSR vous pouvez la signer avec votre Root certificate.
Il faut absolument ajouter l’option : authorityKeyIdentifier=keyid
lors de la signature de votre CA.
Une fois votre CSR signée vous avez donc votre certiticat qu’il faut importer :
data "local_file" "kube_cert" {
filename = "${path.module}/kube.pem"
}
resource "vault_pki_secret_backend_intermediate_set_signed" "kube_ca" {
backend = vault_mount.pki.path
certificate = data.local_file.kube_cert.content
}
resource "vault_pki_secret_backend_config_urls" "config_urls" {
backend = vault_mount.pki.path
issuing_certificates = ["https://vault.security.svc:8200/v1/pki/ca", "https://vault.domain.tld/v1/pki/ca"]
crl_distribution_points = ["https://vault.security.svc:8200/v1/pki/crl","https://vault.domain.tld/v1/pki/crl"]
}
Avec cela vous avez une authorité de certification dans votre Vault fonctionnelle. Mais vous ne pouvez pas encore générer de certificat.
Il faut maintenant créer des roles afin de permettre cela.
Configuration des roles
Continuons cela avec Terraform.
resource "vault_pki_secret_backend_role" "cert-generator" {
backend = vault_mount.pki.path
name = "server-cert-for-kube"
allowed_domains = [ "*.domain.tld" ]
allow_subdomains = true
allow_glob_domains = true
allow_any_name = false
enforce_hostnames = true
allow_ip_sans = true
server_flag = true
client_flag = false
country = ["FR"]
province = ["Lorraine"]
organization = ["McTh"]
ou = ["kube"]
key_usage = ["DigitalSignature", "KeyAgreement", "KeyEncipherment"]
# 2 years
max_ttl = 63113904
# 30 days
ttl = 2592000
no_store = true
}
Cela va permettre de créer des certificats de type server. Je ne vais pas entrer dans les détails des options mais par défaut vos certificats auront une durée de vie de 30 jours et vous pourrez au max demander 2ans.
Il faut prévoir une dernière chose avant de passer à la configuration Kubernetes : une policy (nommée kube-cert-generator) afin d’autoriser un compte la génération de certificat dans Vault.
path "pki/issue/server-cert-for-kube" {
capabilities = ["create", "update"]
}
path "pki/sign/server-cert-for-kube" {
capabilities = ["create", "update"]
}
Cela permettra à un compte de service de générer et de signer des certificats dans Vault.
Configuration de Kubernetes dans Vault
Pour que Kubernetes puisse communiquer directement avec Vault via des service accounts il faut également activer le backend kubernetes.
Avant de se lancer directement dans la configuration Terraform il faut executer quelques commandes afin de récupérer certaines informations dans votre cluster Kubernetes :
NS="security" ## Namespace de Vault de préférence
export VAULT_SA_NAME=$(kubectl get sa vault-auth -n $NS -o jsonpath="{.secrets[*]['name']}")
export SA_JWT_TOKEN=$(kubectl get secret $VAULT_SA_NAME -n $NS -o jsonpath="{.data.token}" | base64 --decode; echo)
export SA_CA_CRT=$(kubectl get secret $VAULT_SA_NAME -n $NS -o jsonpath="{.data['ca\.crt']}" | base64 --decode; echo)
export K8S_HOST="kubernetes.default.svc"
Cela implique que votre cluster contient un service account pour vault appelé vault-auth. Si ce n’est pas le cas voici :
cat <<EOF | kubectl apply -f -n security -
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-auth
EOF
Vous pouvez directement lancer la commande Vault afin d’ajouter le backend d’auth plus facilement :
vault write auth/kubernetes/config \
token_reviewer_jwt="$SA_JWT_TOKEN" \
kubernetes_host="https://$K8S_HOST:443" \
kubernetes_ca_cert="$SA_CA_CRT"
Ou si vous souhaitez continuer avec Terraform voici la configuration :
resource "vault_auth_backend" "kubernetes" {
type = "kubernetes"
}
resource "vault_kubernetes_auth_backend_config" "config" {
backend = vault_auth_backend.kubernetes.path
kubernetes_host = "https://${K8S_HOST}:443"
kubernetes_ca_cert = "${SA_CA_CRT}"
token_reviewer_jwt = "${SA_JWT_TOKEN}"
issuer = "api"
disable_iss_validation = "true"
}
Pour finir il faut ajouter un role dans l’auth kubernetes afin d’autoriser un compte de service à se connecter à Vault :
resource "vault_kubernetes_auth_backend_role" "vault-issuer" {
backend = vault_auth_backend.kubernetes.path
role_name = "vault-issuer"
bound_service_account_names = ["vault-issuer"]
bound_service_account_namespaces = ["kube-system"]
token_ttl = 3600
token_policies = ["kube-cert-generator"]
audience = "vault"
}
Avec tout cela Vault est complètement prêt pour générer des certificats signés par votre CA pour votre cluster.
Kubernetes
Je pars du principe que le cert-manager est installée et que vous avez un ingress controller d’installé (qui utilise les secrets pour les certificats).
Dans le namespace de votre ingress controller (kube-system de mon côté) vous devez créer un service account qui pourra se connecter à Vault (vault-issuer que nous avons créé précédemment) :
cat <<EOF | kubectl apply -f -n kube-system -
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-issuer
EOF
Ensuite il faut créer un issuer dans cert-manager :
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: kube-issuer
namespace: istio-system
spec:
vault:
path: pki/sign/server-cert-for-kube-dev
server: https://vault.security.svc:8200
caBundle: CAbase64
auth:
kubernetes:
role: vault-issuer
mountPath: /v1/auth/kubernetes
secretRef:
name: vault-issuer-token-ftdxh
key: token
Ici je pars du principe que votre Vault est sécurisé en interne. S’il ne l’est pas vous pouvez enlever.
Si vous souhaitez par exemple ensuite générer un certificat pour test.domain.tld :
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: test.domain.tld
namespace: kube-system
spec:
commonName: "test.domain.tld"
dnsNames:
- "domain.tld"
duration: 1440h
renewBefore: 72h
issuerRef:
kind: Issuer
name: kube-issuer
secretName: test-domain
usages:
- digital signature
- key encipherment
- server auth
Ceci va générer un certificat dans cert-manager et ajouter le ca le cert et la key dans un secret test-domain.
Vous pourrez utiliser ce dernier dans votre ingress controller afin d’avoir un certificat valide.
En cas de problème avec votre certificat vous pouvez valider que tout est ok côté cert-manager en regardant les status dans kubernetes :
$ kubectl get issuer -n kube-system
NAME READY AGE
kube-issuer True 12d
Ou encore :
kubectl get certificate -n kube-system
NAME READY SECRET AGE
test.domain.tld True test.domain 11d
Ce n’est pas si simple d’utiliser Vault avec Kubernetes mais au final la génération de certificat automatique avec auto renew est vraiment une fonctionnalité intéressante et permet de simplifier ce process qui peut être assez lourd dans beaucoup d’organisations.