Kubernetes Auditing

Kubernetes Auditing

Hrishikesh Deodhar
Hrishikesh Deodhar

Auditing is an essential administrative input to understand the way a system is affected or being used. An audit trail is a log of the sequence of chronological events that occurred on a system. It helps administrators understand what event occurred, when did it take place and the trigger or the source of the event. It is also compliance or legal requirement for many businesses and hence an important part of IT systems. In this tutorial, we will look at how to do auditing in Kubernetes.

Introduction

An simple example of an audit log in the context of kubernetes would be read as

An nginx pod(podname) was scheduled on a node(nodename) by a serviceaccount(serviceaccountname).The above statement helps an administrator or a developer understand the intent of the event (a pod being scheduled), the object in question (the nginx pod), the affecting system, (the node) and, the actor which was the serviceaccount.

An audit trail would be an effective input to help debug issues, fine-tune permissions, understand the way the system is being used and maybe write dynamic webhooks by looking at the audit logs to further tighten what can be done based on the knowledge of these usage patterns. Let us look at the way kubernetes auditing is enabled and dive deeper at the actors in play here. Working on kubernetes objects eventually means using the kubernetes REST API, which is either via kubectl or via the kubernetes client libraries. Hence, all the auditing is performed by the kubeapiserver by enabling options for auditing.

Since the kube-apiserver is the one which generates events, the memory consumption of the apiserver pod increases. This is directly proportional to the number of events generated.

An audit event is logged when each request is received. A request as it traverses through several stages generates events. A Policy resource defines if these events need to be ignored or to be logged. Below are the several stages which generate events as the request traverses through them.

  • RequestReceived : The stage when the request is first received before being sent for further processing
  • ResponseStarted : This stage is generated for long running requests only. It is triggered when the response headers are sent but the response body is yet to be sent.
  • ResponseComplete : This stage signifies that the response is completed and there is no further processing that will occur on the request.
  • Panic : Generated event when a panic occurs.

Audit policies allow the definition of a set of rules which act on the above events and define the level of data that needs to be logged. These are called as audit levels. Below are the audit levels which can be used in an Audit Policy definition.

  • None : Do not log anything.
  • Metadata : Logs only the metadata of the request. Does not log the request or the response body. This is specially useful when you do not want to log request body for a secret or a configmap.
  • Request : Logs metadata and request body but does not log the response.
  • RequestResponse : Logs metadata, request and response bodies.

Here is a sample audit policy which we will use in the demonstration below.

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Request
resources:
 - group: "" # core
resources: ["secrets", "configmaps"]
namespaces: [""]

The above Policy logs at the Request level for the secrets and configmap resources. Let us enable auditing on the kube-apiserver and write and deploy a sample audit policy. We will also create a couple of users and look at the audit events generated for those users. To start off clone the git repository

git clone https://github.com/hr1sh1kesh/k8s-play.git
cd k8s-play/kubernetes-auditing

Create users to view their audit events later

Allowing an external user to be authenticated to the kubernetes cluster in order to create / update resources requires performing the following steps. Create a request specifying the details of the user which needs access to the cluster. The users/ folder already has the json files for 2 users, user:jai and user:veeru.

cat << eof >> jai.json
{
"CN": "user:jai",
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"C": "US",
"L": "CA",
"ST": "San Francisco",
"O" : "system:users"
}
]
}
eof

Generate the private key and the csr based on the request json. Alternatively, you can also use cfssl print-defaults csr and edit the json request file.

cfssl genkey jai.json | cfssljson -bare certificate

With the csr and the private key generated, we now need to create a CertificateSigningRequest resource which is then approved/rejected by a cluster administrator to allow access to this user on the kubernetes cluster. The request is a base64 encoded string of the generated csr.

cat certificate.csr | base64 | tr -d '\n'

Copy the above base64 encoded string and use that in the CertificateSigningRequest

apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:ud
name: user-jai
spec:
groups:
- system:authenticated
request: # base64 encoded csr
usages:
- digital signature
- key encipherment
- client auth

Apply the above spec using kubectl for both the users.

The new user certificate request needs to be approved, which essentially signs the user generated certificate by the cluster CA.

Note: These steps are performed by a cluster administrator

kubectl certificate approve user-jai

Get the signed certificate for the user.

kubectl get csr user-jai -o jsonpath='{.status.certificate}' | base64 -d > jai.crt

Create a kubeconfig entry to be used to interact with the cluster using kubectl

kubectl --kubeconfig ~/.kube/config-jai config set-cluster jai --insecure-skip-tls-verify=true --server=
kubectl --kubeconfig ~/.kube/config-jai config set-credentials jai --client-certificate=jai.crt --client-key=certificate-key.pem --embed-certs=true
kubectl --kubeconfig ~/.kube/config-jai config set-context jai --cluster=jai --user=jai
kubectl --kubeconfig ~/.kube/config-jai config use-context jai

These above steps will help prove the authenticity of the user. but the user still does not have any roles assigned to him. Create RBAC resources to provide access to the user either across namespaces or within a namespace. Provide the user(s) RBAC permissions as suitable on the cluster.

kubectl apply -f rbac.yaml

The user(s) should be able to list pods in the default namespaces now.

kubectl --kubeconfig ~/.kube/config-jai get pods

Enable auditing.

Kubernetes auditing needs to be enabled on the kube-apiserver hence ssh onto the node which runs the kube-apiserver. Create a folder which holds the audit policy

mkdir -p /etc/kubernetes/auditpolicies

Place the policy.yaml spec in the above folder. We will volume mount this in the kube-apiserver pod spec. Enable auditing by adding the following options to the kube-apiserver podspec

- --audit-policy-file=/etc/kubernetes/auditpolicies/policy.yaml
- --audit-log-path=/var/log/audit.log

Refer sample-kube-apiserver.yaml The above steps would enable kubernetes auditing and will log the audit events at a location /var/log/audit.log

Visualize Audit Logs

Deploy Elasticsearch and Kibana to ingest and visualize the logs. Fluentd would be used as the log shipper which publishes the audit log to an elasticsearch endpoint.

kubectl apply -f es.yaml
kubectl apply -f kibana.yaml

This will deploy an elasticsearch cluster with 3 data pods, 2 master pods and 2 client pods. The elasticsearch client service is exposed via NodePort. The clusterIP of this service will be used in the fluentd-config.

kubectl get svc elasticsearch

The kibana service is exposed over NodePort. Edit the fluentd Configmap in the fluentd.yaml file. Provide the clusterIP of the elasticsearch client service.

Note: Fluentd is used as a deployment in this case. It also has a Pod affinity where it gets scheduled to a node which runs the pod with label “component=kube-apiserver”

kubectl apply -f fluentd.yaml

This will publish the audit log at /var/log/audit.log to the elasticsearch cluster, and these logs can be viewed by Kibana

Audit trail

Test the setup.

kubectl --kubeconfig ~/.kube/config-jai create secret generic jai-secret --from-literal=username=jai --from-literal=password=topsecret
kubectl --kubeconfig ~/.kube/config-veeru create secret generic veeru-secret --from-literal=username=veeru --from-literal=password=topsecret
kubectl --kubeconfig ~/.kube/config-veeru get secret veeru-secret
kubectl --kubeconfig ~/.kube/config-jai edit secret jai-secret

The Audit trail for the above commands should look something like this

user audit trail

Conclusion:

So we learnt how to setup audit policies and how to view the audit logs generated by the kube-apiserver via a logmanagement solution such as EFK. The kubernetes auditing policy defines the kind of audit trail that gets generated. This also does come at a cost to the processing for the kube-apiserver, so needs to be setup judiciously. A benchmark of the effect of kubernetes auditing on the kube-apiserver would be a really nice article for the future.

As a side note, webhook and dynamic backends (currently alpha in kubernetes v 1.13) are also supported for audit trails.

Looking for help with Kubernetes adoption or Day 2 operations? learn more about our capabilities and why startups & enterprises consider as one of the best Kubernetes consulting services companies.

References

Posts You Might Like

This website uses cookies to offer you a better browsing experience