Enable Kubernetes auth on Hashicorp Vault

How to configure an Hashicorp Vault auth backend for Kubernetes service to auth against.
Jan 24, 2024 · 336 words · 2 minute read

Introduction 🔗

In this post I describe the steps taken to enable a Kubernetes authentication backend for Hashicorp Vault and configure a PKI role for cert-manager to use to issue certificate signing requests.

Process 🔗

Start by enabling the auth method.

vault auth enable --path=k8s-yourdomain-production kubernetes

You should see:

Success! Enabled kubernetes auth method at: k8s-yourdomain-production/

The cluster needs a ’token reviewer’ service account that Vault can use to validate tokens presented by clients that claim to have been authenticated by the cluster.

The YAML for this is as follows:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: token-reviewer
  namespace: cert-manager

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: vault-issuer-tokenreview-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
- kind: ServiceAccount
  name: token-reviewer
  namespace: cert-manager

---
apiVersion: v1
kind: Secret
metadata:
  name: token-reviewer
  namespace: cert-manager
  annotations:
    kubernetes.io/service-account.name: token-reviewer
type: kubernetes.io/service-account-token

This will generate a service account token in the token-reviewer secret.

And a ClusterIssuer that uses that service token to request certificates from a PKI role in Vault:

---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: vault
spec:
  vault:
    #server: https://vault.yourdomain.lan
    server: http://10.68.5.53:8200
    path: pki/sign/internal-certs
    auth:
      kubernetes:
        mountPath: /v1/auth/k8s-yourdomain-production
        role: cert-manager
        secretRef:
          name: token-reviewer
          key: token

Obtain the token and CA cert.

K8SURL=https://10.96.4.1:6443 # Or whatever
TOKEN=$(kubectl -n cert-manager get secret token-reviewer  -ojsonpath='{.data.token}' | base64 -d)
kubectl -n cert-manager get secret token-reviewer  -ojsonpath='{.data.ca\.crt}' | base64 -d >ca.crt

Create a policy that allows issuing certs:

cat <<EOF | vault policy write yourdomain-lan-certs /dev/stdin
path "pki/sign/internal-certs" {
    capabilities = ["create", "update"]
}
EOF

Also, create the PKI role that will sign the cert.

vault write pki/roles/internal-certs \
    allowed_domains="yourdomain.lan" \
    allow_subdomains=true \
    key_type=ec \
    max_ttl="2160h"

Configure the auth method:

vault write auth/k8s-yourdomain-production/config \
    token_reviewer_jwt="$TOKEN" \
    kubernetes_host="$K8SURL" \
    kubernetes_ca_cert="$(cat ca.crt)"

Create an authentication role to identify ‘cert-manager’ on the cluster by it’s presentation of the ’token-reviewer’ token:

vault write auth/k8s-yourdomain-production/role/cert-manager \
    bound_service_account_names=token-reviewer \
    bound_service_account_namespaces=cert-manager \
    policies=yourdomain-lan-certs \
    ttl=24h

If all’s well you should now see the ClusterIssuer showing as ‘ready’.

kubectl get clusterissuers

Output should look something like this:

NAME    READY   AGE
vault   True    10h