Create a ServiceAccount Token for a ServiceAccount with Role & RoleBinding¶
Tip
This guide will walk you through setting up kind
and Vault
and its Kubernetes Secret Engine to create a Service Account Token for a pre-existing ServiceAccount with Role & RoleBinding
Prerequisites¶
You will need the following tools to be installed:
Setup kind
¶
cat <<EOF >>kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
apiServerAddress: "127.0.0.1"
apiServerPort: 6443
EOF
kind create cluster --config=kind-config.yaml
you should now be able to run kubectl
commands:
> kubectl get ns
NAME STATUS AGE
default Active 64m
kube-node-lease Active 64m
kube-public Active 64m
kube-system Active 64m
local-path-storage Active 63m
Configure Vault
access¶
The following manifest, creates a ServiceAccount vault-auth
and assigns it the role service-account-token-creator
, which allows to create Service Account Tokens.
Tip
This Service Account is going to be used by Vault
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-auth
automountServiceAccountToken: true
---
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
name: vault-auth-token
annotations:
kubernetes.io/service-account.name: vault-auth
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: service-account-token-creator
rules:
- apiGroups: [""]
resources: ["serviceaccounts/token"]
verbs: ["create", "update", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: service-account-token-creator-binding
subjects:
- kind: ServiceAccount
name: vault-auth
roleRef:
kind: Role
name: service-account-token-creator
apiGroup: rbac.authorization.k8s.io
EOF
Create a Service Account for which Vault
creates the ServiceAccount Token¶
This manifest creates a Service Account tmp-sa
that is bound to the role-list-pods
role that only allows to list pods in the default
namespace:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: tmp-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-list-pods
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: role-abilities
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: role-list-pods
subjects:
- kind: ServiceAccount
name: tmp-sa
EOF
Configure Vault
¶
Lastly, we will need to start and configure a local Vault Server
:
Authenticate to Vault
and check with vault status
:
export VAULT_ADDR="http://127.0.0.1:8200"
export VAULT_TOKEN="root"
> vault status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.18.3
Build Date 2024-12-16T14:00:53Z
Storage Type inmem
Cluster Name vault-cluster-4cab3957
Cluster ID 597257da-8e8d-6147-c379-e93e3a6013c7
HA Enabled false
Now, we will configure the Kubernetes Secrets Engine to connect to the local kind
Cluster with the vault-auth
ServiceAccount and create a role kind
, which will create a ServiceAccount Tokens for the tmp-sa
ServiceAccount:
#!/usr/bin/env bash
set -ex
K8S_JWT_TOKEN=$(kubectl get secret vault-auth-token -o jsonpath="{.data.token}" | base64 -d)
K8S_CA_CERT=$(kubectl get secret vault-auth-token -o jsonpath="{['data']['ca\.crt']}" | base64 -d)
vault secrets enable kubernetes
vault write -f kubernetes/config \
kubernetes_host="https://127.0.0.1:6443" \
kubernetes_ca_cert="$K8S_CA_CERT" \
service_account_jwt="$K8S_JWT_TOKEN"
vault write kubernetes/roles/kind \
allowed_kubernetes_namespaces="default" \
service_account_name="tmp-sa" \
token_default_ttl="10m"
Putting it together¶
Write kind
s kubeconfig
to a file:
and update it, to use kubectl-vault-login
for authentication:
KUBECONFIG=./kubeconfig.yml kubectl config set-credentials vault \
--exec-interactive-mode=Never \
--exec-api-version=client.authentication.k8s.io/v1 \
--exec-command=kubectl \
--exec-arg=vault-login \
--exec-arg=--role=kind
> cat kubeconfig.yml
[...]
users:
- name: vault
user:
exec:
apiVersion: client.authentication.k8s.io/v1
args:
- vault-login
- --role=kind
command: kubectl
env: null
interactiveMode: Never
provideClusterInfo: false
# create a pod to see some results
> kubectl run nginx --image=nginx
# use the updated kubeconfig to list pods in the default namespace
> KUBECONFIG=./kubeconfig.yml kubectl --user=vault get pod
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 73s
You can also use curl
to communicate with the Kubernetes API directly:
> curl -sk \
-H "Authorization: Bearer $(./kubectl-vault-login -r kind | jq -r .status.token)" \
$(kubectl config view --minify -o 'jsonpath={.clusters[].cluster.server}')/api/v1/namespaces/default/pods
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "707"
},
"items": []
}
The role role-list-pods
allows listing pods for the default
namespace, but not for kube-system
:
> KUBECONFIG=kubeconfig.yml kubectl --user=vault get pod -n kube-config
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:v-token-kind-1739680669-u5x0uqreffqt8hf2qdydpksf" cannot list resource "pods" in API group "" in the namespace "kube-system"
Teardown¶
Tear everything down by running: