Skip to content

alpha-unito/k8s-vault-signer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Kubernetes CSR Signer for Vault

HashCorp Vault can be used as a Certificate Authority manager for Kubernetes. However, the CA key cannot be extracted from Vault, preventing the control plane signer from properly handle Certificate Signing Request (CSR) objects in Kubernetes.

This repository implements a custom Kubernetes signer, called unito.it/vault-signer, to automatically handle the CSRs signing process in Kubernetes.

Usage

Configure the Vault PKI

Vault can be configured to serve as a Certificate Authority (CA) for Kubernetes through the PKI secret engine (see here). Let's assume that there exists a pki/ mount point that contains the Kubernetes CA, which should be used to sign Kubernetes CSRs.

The most secure way to sign CSRs in a controlled way is through the pki/sign/:role endpoint. Therefore, it is necessary to create a role for the Kubernetes signer

vault write pki/roles/kubernetes-signer \
  allow_any_name=true                   \
  allow_glob_domains=true               \
  enforce_hostnames=false               \
  key_bits=2048                         \
  key_type=any                          \
  no_store=true                         \
  ttl="0s"                              \
  max_ttl="87600h"

The Vault signer needs to read the role characteristics for CSR validation and to sign CSRs by sending requests to the pki/sign/kubernetes-signer endpoint. Therefore, it is necessary to create a Vault Policy with the proper permissions

vault policy write kubernetes-signer-policy - << EOF
path "pki/roles/kubernetes-signer" {
  capabilities = ["read"]
}

path "pki/sign/kubernetes-signer" {
  capabilities = ["create", "patch", "update"]
}
EOF

Authenticate with Vault

The Kubernetes Vault signer needs to authenticate with a Vault instance (or cluster) to delegate CSR signing. In detail, it supports two possible authentication methods: approle and kubernetes.

AppRole Authentication

The Vault AppRole authentication method allows machines or apps to authenticate with Vault-defined roles. This authentication method can be enabled through the following command

vault auth enable approle

Then, it is necessary to create a Vault role for the Kubernetes signer

vault write auth/approle/role/kubernetes-signer  \
  policies="kubernetes-signer-policy"            \
  token_max_ttl="10m"                            \
  token_ttl="60s"

To authenticate with the AppRole authentication method, a client needs to know the role id and the associated secret id. These ids can be obtained through the following commands

ROLE_ID=$(vault read auth/approle/role/kubernetes-signer/role-id)
SECRET_ID=$(vault write -f auth/approle/role/kubernetes-signer/secret-id)

Then, create an auth.conf configuration file for the Vault Kubernetes signer with the following syntax, substituting the ${ROLE_ID} and ${SECRET_ID} placeholders with the values returned by the previos commands

[Global]
auth-type = approle

[AppRole]
role-id = ${ROLE_ID}
secret-id = ${SECRET_ID}

Finally, create a secret from the previous file

kubectl create secret generic   \
    --namespace=vault-signer    \
    --from-file=auth.conf       \
    vault-auth-config

Kubernetes Authentication

The Vault Kubernetes authentication can be used to authenticate with Vault using a Kubernetes ServiceAccount token. This authentication method can be enabled through the following command

vault auth enable kubernetes

Then, it is necessary to register the ServiceAccount JWT token and the Kubernetes cluster to Vault using this command, where the value of the issuer field should be equal to the value of the --service-account-issuer option of the kube-apiserver command running on the Kubernetes cluster control plane

vault write auth/kubernetes/config                \
  kubernetes_host=<Kubernetes hostname and port>  \
  kubernetes_ca_cert=<PEM-encoded Kubernetes CA>  \
  issuer=<ServiceAccount issuer>

At this point, it is necessary to create a Vault role for the Kubernetes signer

vault write auth/kubernetes/role/kubernetes-signer  \
  bound_service_account_names="vault-signer"        \
  bound_service_account_namespaces="vault-signer"   \
  policies="kubernetes-signer-policy"               \
  token_max_ttl="10m"                               \
  token_ttl="60s"

Then, create an auth.conf configuration file for the Vault Kubernetes signer with the following syntax

[Global]
auth-type = kubernetes

[Kubernetes]
role-name = kubernetes-signer

Finally, create a secret from the previous file

kubectl create secret generic   \
    --namespace=vault-signer    \
    --from-file=auth.conf       \
    vault-auth-config

Deploy the Vault Signer

The Kubernetes Vault signer can be deployed using the Helm Chart provided in the helm folder of this repository. First, create a vaules.yml file

replicaCount: 3
vault:
  address:
    scheme: http
    hostname: <Your Vault Hostname>
    port: 8200
  auth:
    secretName: vault-auth-config
    secretKey: auth.conf
  pki: pki
  role: kubernetes-signer

Then, deploy a Helm release with the following command

helm install --namespace vault-signer --values values.yaml vault-signer ./helm

Sign Kubernetes CSRs

The Vault signer handles CSRs that specify a signerName equal to unito.it/vault-signer. To test that everything works properly, create a csr.yaml file with the following content

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: vault-test-csr
spec:
  request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZqQ0NBVDRDQVFBd0VURVBNQTBHQTFVRUF3d0dZVzVuWld4aE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRgpBQU9DQVE4QU1JSUJDZ0tDQVFFQTByczhJTHRHdTYxakx2dHhWTTJSVlRWMDNHWlJTWWw0dWluVWo4RElaWjBOCnR2MUZtRVFSd3VoaUZsOFEzcWl0Qm0wMUFSMkNJVXBGd2ZzSjZ4MXF3ckJzVkhZbGlBNVhwRVpZM3ExcGswSDQKM3Z3aGJlK1o2MVNrVHF5SVBYUUwrTWM5T1Nsbm0xb0R2N0NtSkZNMUlMRVI3QTVGZnZKOEdFRjJ6dHBoaUlFMwpub1dtdHNZb3JuT2wzc2lHQ2ZGZzR4Zmd4eW8ybmlneFNVekl1bXNnVm9PM2ttT0x1RVF6cXpkakJ3TFJXbWlECklmMXBMWnoyalVnald4UkhCM1gyWnVVV1d1T09PZnpXM01LaE8ybHEvZi9DdS8wYk83c0x0MCt3U2ZMSU91TFcKcW90blZtRmxMMytqTy82WDNDKzBERHk5aUtwbXJjVDBnWGZLemE1dHJRSURBUUFCb0FBd0RRWUpLb1pJaHZjTgpBUUVMQlFBRGdnRUJBR05WdmVIOGR4ZzNvK21VeVRkbmFjVmQ1N24zSkExdnZEU1JWREkyQTZ1eXN3ZFp1L1BVCkkwZXpZWFV0RVNnSk1IRmQycVVNMjNuNVJsSXJ3R0xuUXFISUh5VStWWHhsdnZsRnpNOVpEWllSTmU3QlJvYXgKQVlEdUI5STZXT3FYbkFvczFqRmxNUG5NbFpqdU5kSGxpT1BjTU1oNndLaTZzZFhpVStHYTJ2RUVLY01jSVUyRgpvU2djUWdMYTk0aEpacGk3ZnNMdm1OQUxoT045UHdNMGM1dVJVejV4T0dGMUtCbWRSeEgvbUNOS2JKYjFRQm1HCkkwYitEUEdaTktXTU0xMzhIQXdoV0tkNjVoVHdYOWl4V3ZHMkh4TG1WQzg0L1BHT0tWQW9FNkpsYWFHdTlQVmkKdjlOSjVaZlZrcXdCd0hKbzZXdk9xVlA3SVFjZmg3d0drWm89Ci0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo=
  signerName: unito.it/vault-signer
  expirationSeconds: 86400
  usages:
    - client auth

Then, create the Kubernetes CSR object and approve it using the following commands

kubectl apply -f csr.yaml
kubectl certificate approve vault-test-csr

At this point, the CSR should be in the Approved,Issued status when inspected using the following command

kubectl get csr vault-test-csr

Plus, the following command should display a valid X509 certificate

kubectl get csr vault-test-csr --output=json |  \
  jq .status.certificate |                      \
  tr -d '"' |                                   \
  base64 -d |                                   \
  openssl x509 -text -noout

Acknowledgment

The development of the Kubernetes Vault signer has been partially supported by the HaMMon project, "Hazard Mapping and Vulnerability Monitoring", funded by the Italian Research Center in High-Performance Computing, Big Data, and Quantum Computing (ICSC).