Kubernetes has vastly improved containerized apps manageability through its extensible and pluggable architecture. It has emerged as the standard for providing various features by utilizing custom plugins known as Admission Controllers.
Admission controllers are introduced in Kubernetes to enable advanced security features of Kubernetes. They are used to modify the default behavior of many Kubernetes components while decreasing code implementation.
Despite the functionality and easy integration of Admission controllers, implementing them can still be challenging due to the vast scope of Kubernetes features.
For overcoming the implementation issues, it’s best to know the working of admission controllers, which we will discuss in this blog. We will understand the admission controller logic and how it provides capabilities to make the most out of Kubernetes features.
Kubernetes admission controllers are plugins that help define and govern what resource configurations can run on the cluster. An admission controller acts as a gatekeeper that processes the requests made to the Kubernetes API for authentication and authorization.
Admission controller depending upon the configuration, accepts or denies the cluster requests. Integrating functionality using admission controller plugins enforces custom policies that run in sequence for approval of the request.
If any plugins reject the requests in the sequence, the changes are immediately ignored, and end-users receive an error message. By extending the admission controller functionality, users can also hook up custom controllers to either modify or validate the request depending upon the specific business needs.
Requests made to the Kubernetes API by admission controllers are classified mainly into two types. Validating or mutating admission controllers. Once the request is authenticated through the desired type, the API dictates if the end-user may perform the operation. After the request is properly authorized, the admission logic/control comes into play.
Admission control usually occurs in two distinct phases: In the first phase, mutating admission controllers are executed, and in the second phase, validating admission controllers are.
Implementation of policies is not done by these admission controllers directly; their actions are handled through a standard webhook interface configured at runtime to ease the communication between the Kubernetes API server and your custom code implementation.
Admission webhooks implements an HTTP callback that receives admission requests and executes custom logic whenever resources are deleted or created in the Kubernetes cluster.
Webhooks also decouple the admission controller logic from the Kubernetes API server, allowing users to efficiently use, configure, and monitor admission webhooks for their custom admission controllers.
Users can define two types of admission webhooks, validating admission webhook, and mutating admission webhook. Validating admission webhook handles requests proxied by validating admission controller. Requests are based on the resource and request methods, which return a failed response in cases when rejected. Otherwise, the controller handles the next admission request.
On the other hand, mutating admission webhooks handles mutating admission controller requests, which can modify the objects while validating controllers do not. Mutating admission controller modifies the resource submitted by the user and performs schema validation.
In some cases mutating webhooks also applies custom defaults as part of the request processing to implement specific resources.
Both MutatingAdmissionWebhook and ValidatingAdmissionWebhook are admission interfaces that are independent of each other. They can act as a validating or mutating or as a combination of both depending upon the context. However, both webhooks have some advantages/disadvantages in different contexts.
Validating admission webhooks are run after any mutating ones. So, whatever request object a validating webhook validates is the version stored in etcd for final use. Sometimes mutating admission webhook is provided unmatched resource configuration which can alter the request validating webhook sees posing a security threat to the environment.
For example, the LimitRanger admission controller can increase the number of pods with given resource limits through a mutating webhook. In contrast, the explicitly set resource requirements may not match the limits specified in the validating phase.
It is recommended to disable the mutating admission webhook or implement a stricter RBAC restriction if they are utilized.
Containerized applications in Kubernetes run inside pods, which provides capabilities such as dynamic scaling and automated deployments. Many of these advanced features in Kubernetes are essential, but they require an Admission Controller to govern the behavior as per organization needs. Below are some of the common uses for admission controllers.
Implementing tight security control is always essential for any organization as it helps to monitor the excess resource usage by the pods. PodSecurity policy admission controller is created explicitly for these contexts disallowing containers from running with root privileges decreasing resource access.
Pulling the base images from authorized sources can also decrease excess resource consumption as they do not contain resource-hogging malware.
Determining the priority of deployment is essential when it comes to meeting the security requirements of the organization. Privileged deployments easily bypass many security monitoring strategies, which can pose various risks to the organization.
Adhering to the least privilege principle and using a web-hook-based custom controller that rejects privileged deployments by overriding the privileged flag to false can quickly mitigate these deployment security issues.
Validating the configuration of the resources running inside the cluster is mandatory as it helps prevents resource misconfigurations during the deployment and scaling stages.
Admission controllers can enforce specific labels that ensure teams are assigned the right resource configurations. Cluster Admins can use annotations to attach relevant metadata to Kubernetes objects.
Admission controllers can be used to inject sidecar into pods dynamically during pod creation. Such sidecars can take care of log collection and export, provide secrets to primary containers, implement service mesh capabilities, and much more.
Kubernetes supports various admission controllers from which some come pre-configured out of the box in a deployment. Admission controllers enforce aspects like pod priorities and resource limits by default. Admission controllers are also the last step in the process of authentication and authorization before Kubernetes persists resources in etcd.
There are many admission controllers available that manage several aspects of Kubernetes, such as security, provisioning, and resource control. Kubernetes also recommends a standard set of controllers that can be used in any necessary deployment. Below are some relevant examples.
DenyEscalatingExec admission plugin in Kubernetes denies execution commands for pods that are running with escalated privileges. Pods running with escalated privileges have access to the host, allowing an attacker to easily alter the host’s resource configurations to a namespace by executing shell commands in privileged containers.
This plugin restricts that ability and block end users from executing commands into these containers. Organizations deploying containers that run with escalated privileges are highly encouraged to use this plugin.
AlwaysPullImages admission plugin in Kubernetes forces the image pull policy setting to always. This has many security benefits; images are usable by the teams who have the credentials to pull them.
Generally, images in Kubernetes without any pulling restrictions can easily be used by simply knowing the image’s name. With this plugin, any image that a node pulls requires an authorization check against that image.
Also, with AlwaysPullImages enabled, images are always pulled before starting the containers, so the latest patches for vulnerabilities are always downloaded, and containers execute with up-to-date features.
While there is a performance disadvantage for pulling the images from the registry, Users can also set the image pull policy to IfNotPresent, which will only pull the image from the registry if there is no cached version on the node. But with the setting set to Always, the cluster will enforce that users cannot make use of cached images.
Both LimitRange and ResourceQuota plugins observe the incoming requests for violating the resources constraints specified in the LimitRange and ResourceQuota object in a namespace.
These are especially helpful to prevent unauthorized access and denial of service attacks where quota/limits are incremented for handling abnormal resource requests.
Organizations must implement both resource quota and limit range admission plugins if they utilize LimitRange and ResourceQuota objects in their deployment.
It is better to implement ResourceQuota at the end of deployments, while users can use LimitRanger in initial phases by applying default resource requests, which is a 0.1 CPU requirement to all Pods in the default namespace.
NodeRestriction plugin restricts the primary node agent(kubelet) ability to modify node and pod objects while ensuring that kubelet can only modify pods bound to its node.
It is important to limit kubelet permissions as it is the agent that runs on each node of the cluster and makes any configuration changes on the nodes. Restricting kubelet permissions with NodeRestriction will safeguard your kubelet credentials to a particular node, which attackers can use to do malicious things in your cluster.
For kubelet to be controlled by NodeRestriction, it must use credentials specified in the system: node group, and only modify specific label prefixes mentioned under kubernetes.io label.
Use of any other labels by kubelet is reserved and is not allowed by the NodeRestriction admission plugin.
NamespaceLifecycle plugin observes if a terminating Namespace is having new objects created in it, also any requests that are attempted towards a non-existent Namespace are rejected.
An earlier version of Kubernetes used the NamespaceExists admission plugin to reject the incoming requests if namespace was not previously created. Now features of NamepaceExists are merged into NamespaceLifecycle.
It is critical to use the NamespaceLifecycle plugin for data integrity as deleting a namespace can remove all the objects, services, and pods related to it.
NamespaceAutoProvision plugin is the reverse of the NameSpaceExists plugin. It observes requests that attempt to create a Kubernetes Namespace and provision a new Namespace if there is no existent one.
InitialResources plugin observes the requests for the creation of pods. If a container neglects the resource limits, this plugin populates a resource request based on container usage from previously available data. If there is not enough historical data to populate a resource request, the requests remain unhandled.
Implementing admission controllers in Kubernetes provides a secure and simple way to integrate advanced features in Kubernetes. The reliability of containerized apps is critical, and each organization has its own defaults, which makes a “plugin” based admission controller mechanism very useful for expanding the functionality of Kubernetes without adding any code integration complexities.