4 min read

K8s Security: Interacting with the Kubernetes API.

The Kube-API server is the first point of contact for any K8s cluster and it doesn't like strangers knocking on the doors.

The API server is the equivalent to a border control force. It is the gateway to the cluster and any user (human or otherwise) will access the API server by making a RESTful API call via HTTPS and authenticating against the /.kube/config file.

How is a request processed?

Stages of request processing are:

  1. Authentication: Authentication validates the identity of the caller by inspecting the client certificates or bearer tokens.
  2. Authorization: Determines if the identity provided in the first stage can access the verb and HTTP path request using standard Kubernetes RBAC model.
  3. Admission Controller: Verifies if the request is well-formed and, if not, modifies it. An admission control policy could, for example, ensure that the request for creating a Pod includes the definition of a specific label. If it doesn’t define the label, then the request is rejected.

How is the connection to Kube-API Server made?

Of course, in order to connect to the Kube-API Server, we need the IP address of a Control Plane node.

To find the IP address of the Control Plane node, use kubectl cluster-info as shown below

💡
We can also use the --advertise-address and --secure-port values in the configuration file of the API server to find the IP for the endpoint. Both these are in the API server configuration file at /etc/kubernetes/manifests/kube-apiserver.yaml.

Anonymous Access

$ curl https://172.31.91.124:6443 -k

Using curl with the -k flag will skip the verification of the servers TLS certificate and display the following as a response:

As you can see from the JSON-formatted HTTP response body, anonymous calls are accepted by the API server but do not have the appropriate permissions for the operation. Internally, Kubernetes maps the call to the username system:anonymous, which effectively isn’t authorized to start any operations.

The response shows that:

  • API server allowed the anonymous call to be accepted
  • API server mapped the call to the username system:anonymous (since no user name was sent)
  • Because the user is not known and system:anonymous does not have any rights on the cluster, a 403 is sent back.

Access with a client certificate.

The default username that comes as part of K8s installation is kubernetes-admin.

We can confirm this fact by taking a peek inside the /.kube/config file (which is popularly called kubeconfig) using

$ kubectl config view
  • 1: The base64 encoded value of the certificate authority data
  • 2: The default user of the cluster
  • 3: The base64 encoded client certificate data
  • 4: The base64 encoded client private key
💡
To view all the DATA+OMITTED results, use $ kubectl config view --raw

We will use the kubernetes-admin user and its certificate and key, along with the clusters certificate authority data to access the Kube-API server by executing the following command:

$ curl --cacert <name of file containing cluster-certificate-authority> \
--cert <name of file w/ client certificate data> \
--key <client key data> \
https://172.31.91.124:6443/api/v1/namespaces

But where do we get the files mentioned above?

The content for these files is in the kubeconfig file.

Use the command $kubectl config view --raw to print the contents of the file on terminal.

For --cacert, copy the value of certificate-authority-data and make it part of the command shown:

$ kubectl config view --raw -o jsonpath='{.clusters[?(@.name == "kubernetes")].cluster.certificate-authority-data}'| base64 -d > ca.crt

For --cert, copy the value of client-certificate-data and save it in a file named client-cert.crt:

$ kubectl config view --raw -o jsonpath='{.users[?(@.name == "kubernetes-admin")].user.client-certificate-data}'| base64 -d > client-cert.crt

For --key, copy the value of client-key-data and save it in a file named client-key-data.key:

$ kubectl config view --raw -o jsonpath='{.users[?(@.name == "kubernetes-admin")].user.client-key-data}'| base64 -d > client-key-data.key

Finally, use the curl command to send a request with certificates:

curl --cacert ca.crt --cert client-cert.crt
--key client-key-data.key
https://172.31.91.124:6443/api/v1/namespaces

I write to remember and if in the process, I can help someone learn about Containers, Orchestration (Docker Compose, Kubernetes), GitOps, DevSecOps, VR/AR, Architecture, and Data Management, that is just icing on the cake.