Authentication on Kubernetes, the easy way
service account / x509 certificate and kubeconfig. Deep dive into the common technics to authenticate someone / something on kubernetes
Kubernetes make a distinction between authentication and authorisation. This post focus on the authentication mechanism, authorisation which is done via Role, RoleBinding, ClusterRole and ClusterRoleBinding is a topic for another day.
Kubectl is a convenience layer that speak to the API server. You can see those query being made with a verbose flag in kubectl. For instance:
# kubectl get namespaces -v=6 -n default
I0624 17:26:18.599399 39157 loader.go:375] Config loaded from file: /home/mickael/.kube/config
I0624 17:26:19.575659 39157 round_trippers.go:443] GET https://116.203.202.210:6443/api/v1/namespaces?limit=500 200 OK in 966 milliseconds
NAME STATUS AGE
cert-manager Active 18d
default Active 18d
...
As you can see, kubectl use our kubeconfig file securely authenticate us to the API server. Without authentication, the api server refuse to do anything:
# curl -I --insecure https://116.203.202.210:6443/api/v1/namespaces?limit=500
HTTP/2 403
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
content-length: 320
date: Wed, 24 Jun 2020 07:29:48 GMT
Our query comes back with a 403 HTTP status code which stands for “Forbidden”. The server effectively understood the query but refused to answer it because of some issues from the client.
The reason is we’re missing authentication. Authentication can be quite thick to understand if you simply dig into the official documentation. This post is my attempt at making authentication on a kubernetes cluster simple. Before digging deeper, let’s make our life easier by creating a few environment variable:
API_SERVER=$(kubectl -n default get endpoints kubernetes --no-headers | awk '{ print $2 }')
Authentication
Kubernetes handle authentication either via a bearer token or x509 certificates.
Authentication with a Bearer Token
How to create a token?
Bearer token either comes from:
- the secret associated to a service account.
- a static token file known by the API server from one of its flag. See the doc
- a static password file known by API server from its flag. See the doc
- other ways that are discussed in the doc
In the common use case, you’d get a token from a service account by following this steps:
- Create a service account:
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ServiceAccount metadata: name: testing-account
- find the secret kubernetes have associated to the service account:
SECRET_NAME=$(kubectl get serviceaccount testing-account -o=jsonpath='{.secrets[0].name}')
- extract the token and root certificate from the secret:
TOKEN=$(kubectl get secret $SECRET_NAME -o=go-template='' | base64 -d)
How to use a token via curl?
The way you use a bearer token is sending it to the API server via the “Authorization” header like Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269
. For example:
curl -X GET --insecure https://$API_SERVER/apis/ -H "Authorization: Bearer $TOKEN"
# let's make things secure by using the proper ca certificate and removing the insecure flag:
kubectl get secret $SECRET_NAME -o=go-template='' | base64 -d > /tmp/ca.crt
curl --cacert /tmp/ca.crt https://$API_SERVER/apis/ -H "Authorization: Bearer $TOKEN"
Authentication with X509 Certificate
How to create the certificate?
- with openssl, create a key and certificate signing request:
openssl genrsa -out testing-user.key 2048 openssl req -new -key testing-user.key -out testing-user.csr -subj "/CN=testing-user/O=testing-group"
- send your certificate to kubernetes:
cat <<EOF | kubectl apply -n kubernetes-dashboard -f - apiVersion: certificates.k8s.io/v1beta1 kind: CertificateSigningRequest metadata: name: testing-user spec: request: $(cat testing-user.csr | base64 | tr -d '\n') usages: - digital signature - key encipherment - client auth EOF
- generate the cert by signing the csr:
kubectl get csr kubectl certificate approve testing-user
How to use the certificate via curl ?
# get the certificate
kubectl get csr testing-user -o=go-template='' | base64 -d > /tmp/testing-user.crt
curl --insecure --key testing-user.key --cert /tmp/testing-user.crt -X GET https://$API_SERVER/apis/
# let's remove the insecure flag by first pulling the cacert from the cluster:
kubectl config view --raw -o=jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d > /tmp/ca.crt
curl --cacert /tmp/ca.crt --key testing-user.key --cert /tmp/testing-user.crt -X GET https://$API_SERVER/apis/
How to use the certificate via kubeconfig?
cat > /tmp/kube-config.yaml <<EOF
apiVersion: v1
kind: Config
clusters:
- name: kubernetes
cluster:
certificate-authority-data: $(cat /tmp/ca.crt | base64 | tr -d '\n')
server: https://$API_SERVER
users:
- name: testing-user
user:
client-certificate-data: $(cat /tmp/testing-user.crt | base64 | tr -d '\n')
client-key-data: $(cat ./testing-user.key | base64 | tr -d '\n')
contexts:
- context:
cluster: kubernetes
user: testing-user
name: testing-user@kubernetes
current-context: testing-user@kubernetes
EOF
With the proper authorisation set in the Role, RoleBinding and ClusterRole / ClusterRoleBinding:
kubectl get pod --kubeconfig=/tmp/kube-config.yaml