Using the Namespace Default ServiceAccount

Each namespace has a default ServiceAccount, named default . We can verify this with the following command:

$ kubectl get sa --all-namespaces | grep default

default default 1 6m19s

kube-public default 1 6m19s

kube-system default 1 6m19s

Let’s inspect the ServiceAccount named default of the default namespace (this will be pretty much the same for the default ServiceAccount of another namespace).

$ kubectl get sa default -o yaml

apiVersion: v1

kind: ServiceAccount

metadata:

name: default

namespace: default

...

secrets:

- name: default-token-dffkj

We can see here that a Secret is provided to this ServiceAccount. Let’s have a closer look at this one:

$ kubectl get secret default-token-dffkj -o yaml

apiVersion: v1

data:

ca.crt: LS0tLS1CRU...0tLS0tCg==

namespace: ZGVmYXVsdA==

token: ZXlKaGJHY2...RGMUlIX2c=

kind: Secret

metadata:

name: default-token-dffkj

namespace: default

...

type: kubernetes.io/service-account-token

There are several key/value pairs under the data key of this Secret. For readability, I’ve shortened the value of the ca.crt and token values), basically:

ca.crt is the Base64 encoding of the cluster certificate.

is the Base64 encoding of the cluster certificate. namespace is the Base64 encoding of the current namespace (default).

is the Base64 encoding of the current namespace (default). token is the Base64 encoding of the JWT used to authenticate against the API server.

Note: JSON Web Token (JWT) is an open standard (RFC 7519), that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed.

Let’s focus on the token . Once decoded (using base64 -d on Linux, or base64 -D on MacOS), we can easily get the payload of this JWT from the command line, or an online service like jwt.io.

This payload has the following format:

{

“iss”: “kubernetes/serviceaccount”,

“kubernetes.io/serviceaccount/namespace”: “default”,

“kubernetes.io/serviceaccount/secret.name”: “default-token-dffkj”,

“kubernetes.io/serviceaccount/service-account.name”: “default”,

“kubernetes.io/serviceaccount/service-account.uid”: “ac5aa972–80ae-11e9–854d-0800278b691f”,

“sub”: “system:serviceaccount:default:default”

}

We can see the ServiceAccount it is linked to, the namespace it exists in, and some other internal information.

We will see below how to use this token from within a simple Pod, based on the following specification:

Assuming this specification is in the pod-default.yaml file, you can create the Pod with the following (and standard) command:

$ kubectl apply -f pod-default.yaml

As no serviceAccountName key is specified, the default ServiceAccount of the Pod’s namespace is used. We can confirm this by checking the specification of this Pod once created (Kubernetes adds a lot of things for us during the creation process).

Important things to note here:

The serviceAccountName key is set with the name of the default ServiceAccount.

key is set with the name of the default ServiceAccount. The information of the ServiceAccount is mounted inside the container of the Pod, through the usage of volume, in /var/run/secrets/kubernetes.io/serviceaccount (more on that in a bit).

Anonymous call of the API server

Let’s run a shell within this container and install the curl utility:

$ kubectl exec -ti pod-default -- sh

/ # apk add --update curl

From this shell, we can try to get information from the API server without authentication.

Note: as said above, from a Pod running in the cluster, the API server can be reached using the Kubernetes ClusterIP service.

We then get an error message, as an unauthenticated user is not allowed to perform this request.



{

“kind”: “Status”,

“apiVersion”: “v1”,

“metadata”: {},

“status”: “Failure”,

“message”: “forbidden: User \”system:anonymous\” cannot get path \”/api\””,

“reason”: “Forbidden”,

“details”: {},

“code”: 403

} / # curl https://kubernetes/api --insecure“kind”: “Status”,“apiVersion”: “v1”,“metadata”: {},“status”: “Failure”,“message”: “forbidden: User \”system:anonymous\” cannot get path \”/api\””,“reason”: “Forbidden”,“details”: {},“code”: 403

Let’s go one step further and try to issue the same query using the token of the default ServiceAccount.

Call using the ServiceAccount token

From the alpine container, the token of the default ServiceAccount can be retrieved from /run/secrets/kubernetes.io/serviceaccount/token (remember the volume/volumeMounts instructions above). Using this token, we can use it as a Bearer token to authenticate against the API server:



/ # curl -H “Authorization: Bearer $TOKEN” / # TOKEN=$(cat /run/secrets/kubernetes.io/serviceaccount/token)/ # curl -H “Authorization: Bearer $TOKEN” https://kubernetes/api/v1/ --insecure

This time the request goes fine — no more error querying this end point. The list of resources is returned.

{

"kind": "APIResourceList",

"groupVersion": "v1",

"resources": [

{

"name": "bindings",

"singularName": "",

"namespaced": true,

"kind": "Binding",

"verbs": [

"create"

]

},

...

]

}

Let’s now try something more ambitious, and use this token to list all the Pods within the default namespace:



{

“kind”: “Status”,

“apiVersion”: “v1”,

“metadata”: {},

“status”: “Failure”,

“message”: “pods is forbidden: User \”system:serviceaccount:default:default\” cannot list resource \”pods\” in API group \”\” in the namespace \”default\””,

“reason”: “Forbidden”,

“details”: {

“kind”: “pods”

},

“code”: 403

} / # curl -H “Authorization: Bearer $TOKEN” https://kubernetes/api/v1/namespaces/default/pods/ --insecure“kind”: “Status”,“apiVersion”: “v1”,“metadata”: {},“status”: “Failure”,“message”: “pods is forbidden: User \”system:serviceaccount:default:default\” cannot list resource \”pods\” in API group \”\” in the namespace \”default\””,“reason”: “Forbidden”,“details”: {“kind”: “pods”},“code”: 403

The default ServiceAccount does not have enough rights to perform this query. In the following part, we will create our own ServiceAccount and provide it with the additional rights it needs for this action.