This guide will walk you through how to configure Vault running on a Kubernetes cluster to exchange service accounts for a scoped client vault token. This can be useful when you want your services running on a kubernetes cluster to self auth against vault and not require the need to pass around vault credentials.

Auth Delgators

The first thing we want to setup is a ClusterRoleBinding that has a roleRef which uses system:auth-delagator

This role allows delegated authentication and authorization checks. This is commonly used by add-on API servers for unidified authentication and authorization.


apiVersion: rbac.authorization.k8s.io/v1beta1  
kind: ClusterRoleBinding  
metadata:  
  name: role-tokenreview-binding  
  namespace: default  
roleRef:  
  apiGroup: rbac.authorization.k8s.io     
  kind: ClusterRole  
  name: system:auth-delegator  
subjects:  
- kind: ServiceAccount     
  name: vault-auth     
  namespace: default

Note change the namespace to something more appropriate than default

Next we can create a service account which will get bound to this ClusterRoleBinding. Ensure the namespaces for the SA specified in the ClusterRoleBinding and the SA match.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: vault-auth
  namespace: default

Required information

Before we continue to setup vault we need to extra some data from our cluster and newly created service account.

Service Account Token

export VAULT_SA_NAME=$(kubectl get sa vault-auth --output jsonpath="{.secrets[*]['name']}")  

Service Account JWT

export SA_JWT_TOKEN=$(kubectl get secret $VAULT_SA_NAME --output 'go-template={{ .data.token }}'| base64 --decode)  

Service Account CA CRT

export SA_CA_CRT=$(kubectl config view --raw --minify --flatten --output 'jsonpath={.clusters[].cluster.certificate-authority-data}' | base64 --decode)  

K8 Hostname

export K8S_HOST=$(kubectl config view --raw --minify --flatten --output 'jsonpath={.clusters[].cluster.server}')  

JWT Issuer

If the cluster is on K8 1.21+ you must pass in the following

https://kubernetes.default.svc

This is due to changes from 1.21 onward using a different auth method.

Vault Setup

Now we can setup our Kubernetes auth on vault.

In the Access Section click on enable new method this will take you to a selection screen. From here click on Kubernetes.

The path that you should use here is kubernetes. (This is the default path no need to change it unless we want to add different k8 auth engines).

The next page you will see is the configuration page there are only three fields that need to be filled out

  • Kubernetes Host : equal to echo ${K8S_HOST}
  • Kubernetes SA CA Certificate : equal to echo ${SA_CA_CRT}
  • Token Reviewer JWT : equal to echo ${SA_JWT_TOKEN} (this is in the Kubernetes Options option)

With this configured there is now a accessible v1/auth/kubernetes/login endpoint. This is what our services will have to send requests to get back a client token.

Role Setup

This will be on a per service basis as we will need to create policies and roles accordingly.

Here is an example flow of setting up getting a service a scoped token.

Policy creation

Go to the Policies section of vault and click create ACL Policy give it a name and a policy. Here is a example policy for a path that would already exist on vault. Note that these paths must be created as new engines.

path "my-app/*" {  
  capabilities = ["read", "list"]  
}  

Service Account

While every namespace has a default service account that gets attached to a pod if you do not provide one, we will create one for the sake of this guide and the want/need to scope pods to Vault ACLs.

kubectl create sa {MY SA NAME} -n {NAMESPACE}

And lets pretend that this SA gets assigned to our pod.

Now over at Vault when we click view configuration on our Kubernetes auth in access we get taken to a page where we see two tabs one of which being roles.

If you click on roles -> create role we have a form that requires to be filled out. The fields we need to fill out are

  • Name : name of this role. Let’s name it after the service that uses it
  • Bound service account names : This will be the service account name
  • Bound service account namespace : Namespace that the service account lives in
  • Generated Tokens Policies : Name of the policy to use

Service integration

Now, our services can have scoped identities and no longer require the root client token be passed to them.

Instead we will have to setup the services to initial called /v1/auth/kubernetes/login with a json body that consists of role and jwt

  • Role : name of role on vault
  • JWT : this is the service account token that is mounted to the pod

Upon successfully validation Vault returns a json body that describes your auth status that includes your scoped client token among other things.