Admission controllers are an essential Kubernetes-native option for enforcing policy on your containers and overall Kubernetes environment. But there are tons of flavors and ways to do it.
In this Kubernetes Admission Controller Guide, we’ll explore:
By mastering these concepts, security engineers can enforce policies, audit configurations, and prevent unauthorized or harmful deployments, thus playing a crucial role in the organization's security posture.
Simply put though, admission controllers allow you to set security policies for your Kubernetes clusters. At a glance, admission controllers provide access control to users and applications within a cluster. They allow administrators to set various policies including which users can add or delete objects in a cluster, as well as which applications can be deployed to the clusters.
Admission controllers are typically used to ensure that only safe code and configurations are allowed into the production environment. By intercepting and validating requests to the Kubernetes API before they are allowed to persist in runtime in the cluster, admission controllers mitigate risks associated with unauthorized actions, malicious entities, or erroneous configurations. This preemptive policy enforcement ensures that only safe and compliant workloads are deployed.
In practice, admission controllers wield great versatility, facilitating an array of custom policies tailored to specific organizational needs.
They can be leveraged to validate resource requests, evaluate labels and annotations, assess resource quotas, and even trigger webhooks for more advanced validation scenarios.
By offering this flexibility, admission controllers significantly enhance the platform's resilience and overall security posture. Kubernetes even provides Kubernetes Pod Security Admission, with three levels of restrictive policies.
Admission controllers are categorized into two main types: Mutating and Validating. Each plays an important role in securing your Kubernetes environment.
Mutating controllers are the first to interact with requests. They have the authority to modify or mutate requests before they materialize in the cluster. Their primary role is to implement changes to the incoming requests, ensuring that they align with standards and defaults. This could involve altering the request's content, such as adding labels, annotations, or even changing the specifications of the pods so that they meet specific standards or defaults before the request is validated.
Validating controllers are second to interact with requests. Their role is the realm of validation, where each request is compared against a set of established policies and standards. Unlike their mutating counterparts, validating controllers do not alter the requests; they merely ensure compliance with the cluster’s rules.
Webhooks serve as connectors that link external services to the Kubernetes admission control process. They are essentially HTTP callbacks triggered by specific events within the Kubernetes API server, allowing for real-time interaction with external systems for request modification or validation.
Webhooks differ from admission controllers in that they are only triggered when the cluster receives a request. A webhook enables developers to tailor specific instructions when creating new resources on a Kubernetes cluster. It acts as an interface between your application and the Kubernetes API server, allowing for custom validation rules such as resource quotas and limits, user and client authentication requirements, or admission control policies.
Admission controllers, on the other hand, run every time someone attempts to create or modify a resource on the cluster. And to make it even more confusing, there are Kubernetes webhooks that work with admission controllers; namely mutating and validating types. The behavior of these webhooks is determined with the admission controller configuration files.
A MutatingAdmissionWebhook intercepts and processes incoming requests to the Kubernetes API server before they are persisted to the cluster. The primary function of a typical mutating admission controller example in Kubernetes is to modify (mutate) the content of the resource being created or updated.
This is generally an intrusive option that does not allow for close management of what gets deployed to production, as the mutation is automatic and the enforcement of the policy in the first place has the potential to bring down production.
A ValidatingAdmissionWebhook, on the other hand, focuses on validating and potentially rejecting incoming requests before they are persisted to the cluster. Its primary role is to assess whether the request complies with the defined policies and standards of the cluster. This is much less intrusive than a mutating admission controller because it’s results are generally binary; deploy the workload or not.
Understanding admission controllers requires understanding the Kubernetes API server, which is how one would interact with Kubernetes objects.
The Kubernetes API Server is the medium through which all organization and processing within the cluster takes place.
The diagram below shows the three steps in the process of making a request to the Kubernetes API to change the state of an object; for example, deploying a pod.
The steps are Authentication, Authorization, and Admission Control.
In the Authentication step, which has more to do with cloud identity and access management, the server determines whether the presented user is valid.
Example: Is Bob from my cloud IAM account a valid user?
In the Authorization step, which concerns Kubernetes role-based access control, the server determines what actions the user is allowed to take.
If yes, can Bob create a new pod?
In the Admission Control step, the server determines if there are any specific limitations on the action the user or machine wants to take.
Etcd is where the data for the cluster is stored, so the state determined by this three-step process will be executed and represented in Etcd.
If yes, can Bob create a new pod with privileges within X namespace?
This would work similarly when substituting Bob, a real user, for a service account, or a machine user. This would work similarly in the case that, instead of the default Kubernetes API, you are trying to perform actions on custom resources, which are basically non-default extensions of the Kubernetes API.
Read more about the container lifecycle, which is necessarily involved with Kubernetes API processes but running on its own schedule.
Admission control policies can be used to define a wide range of security and operational policies for a Kubernetes environment. The following are example policies that can help further secure clusters, ensure that best-practice guidance is followed, and maintain a secure and compliant state:
Enforce Memory and CPU Resource Limits: Prevent any single pod’s ability to monopolize system resources by requiring all pods to specify memory and CPU limits through admission controllers. For example, a policy could reject any pod with a specification that does not include a requests and limits section, thus ensuring a fair allocation of resources and avoiding resource starvation for other pods.
Restrict Image Registries: Allow pods to be deployed using only container images from approved secure registries. This can be done by specifying a list of allowed registries while blocking all other sources; for example, it is only permissible to deploy pods with images from the organization’s private registry or specific trusted public registries.
Block Privileged Containers: Prohibit any pods from being executed with privileges, as this represents a severe threat to the system’s security. For instance, a policy could reject any pod specification including privileged: true in its container security context, thus ensuring that no container will operate with more permissions than it requires.
Require Read-Only Root Filesystems: Lockdown all containers by enforcing that every container should operate with a read-only root filesystem. Such a policy would reject any pod that hasn’t set readOnlyRootFilesystem: true in its security contexts. This prevents malicious intent from making modifications to the container’s filesystem.
Enforce Network Policies: Verify that all new pods or namespaces adhere to predefined network policies before they are permitted to be executed. This can require a network policy to be adequately labeled for enforcement or inspecting new deployments for annotations that correspond to the present network policies.
Validate ConfigMap and Secret Usage: Not allow open attachments of volumes containing ConfigMaps or Secrets. I.e., a policy could mandate the rejection of pods that mount sensitive secrets as clear text, rather than using Kubernetes’s native security context configurations, guaranteeing sensitive data is handled securely.
Prevent NodePort Services: Block services to be upgraded and expose them to the whole public internet using type: NodePort. I.e., a policy could not allow services to be accessible from the NodePort to other systems, ensuring only the clusters and configured ingress points are the edge for the network.
Restrict where pods can pull images from: Using an image from an unauthorized or unverified source could provide an in-road for malware in a Kubernetes cluster. Make sure your pods only pull images from the registries you know you can trust.
Enforce network policies for pod communication: Ensuring that every deployment of a pod is accompanied by an appropriate network policy before the pod is admitted. For example, every pod should have a network policy that has a least privilege restriction on ingress and egress traffic. This would enforce the least privilege principle and reduce the attack surface.
Deciding whether to use a Kubernetes admission controller requires weighing the security and operational benefits against the challenges of complexity and resource overhead.
Admission controllers offer various benefits when providing access control to your Kubernetes clusters, including real-time validation, authentication enforcement, and policy enforcement. After all, if a resource is created without the proper authorization or validation required by an admission controller, it could create security vulnerabilities within your system.
Most teams simply don’t turn admission controllers on, even if they can do so. Many well-meaning teams define the policies and go to enforce them, only to find that when they do, their production environments go down, and their development teams are not prepared to understand the consequences of deploying containers that violate admission control policy.
Using admission controllers can add complexity to the overall system architecture and may require the allocation of additional resources.
The teams best equipped to work with admission controllers in enforcement mode are those that have worked hard, between DevOps and Security, to define the policies, and who have done lots of practice and testing to understand the consequences of enforcement in any environment.
Multiple platforms are available for building Kubernetes clusters. The platform you are using influences how you should approach admission control. Any of these can be installed via AdmissionControl plugins.
OPA Kubernetes admission controller: OPA (Open Policy Agent) is based on Rego and is often used by teams that want to amass a large, ever-changing rule set that can be queried, QA’d, and managed separately from the Kubernetes resources themselves. Policies are not restricted to allow/deny or binary answers, and checks on a policy can result in structured output with lots of context.
Kyverno admission controller: Kyverno does not use Rego; it relies on YAML, which makes it easier to use than OPA from that perspective. However, managing Kyverno policies at scale is difficult because you cannot decouple the policies from the Kubernetes resources to which they apply.
JSPolicy admission controller: JSPolicy is a less popular choice than Kyverno and OPA, using JavaScript-based validation for resources created on a Kubernetes cluster.
Feature |
OPA – Open Policy Agent |
Kyverno |
JSPolicy |
Language and Complexity |
Rego, more complicated than YAML or JavaScript. Used for policies above complex data structures. |
YAML, quicker and easier for those familiar with Kubernetes manifests. |
JavaScript, offers flexibility and usability with a familiar structure to many. |
Flexibility and Power |
Allows creation of intricate policies with complex queries and responses. Suitable for robust security and governance. |
Simplified policy creation process but may lack the depth of control compared to OPA. |
Dynamic policies enabled by JavaScript, suitable for complex test scenarios. |
Management |
Policies managed separately from Kubernetes resources, simplifying versioning, testing, and distribution. |
Integration with Kubernetes resources, without the need for a separate management layer. |
Not as detailed, but JavaScript's flexibility could imply similar separate management possibilities. |
Scalability Concerns |
The complexity of Rego could impact scalability indirectly. |
Management of a large number of policies as separate resources could become challenging. |
Not as popular, which may impact community support and documentation, indirectly affecting scalability. |
Use Cases |
Suitable for companies requiring advanced policy enforcement willing to master Rego. Heavily dependent on various conditions for decisions. |
Novice teams looking for quick rollout solutions with simple policies. |
Projects seeking flexibility with complex policy creation beyond simple allow/deny conditions. |
Popularity and Support |
Well-established with a significant user base and community support. |
Good popularity, with growing community support and documentation. |
Less popular than OPA and Kyverno, which may affect community involvement and documentation. |
The Kubernetes pod security policy, and the usage of the associated admission controller, was deprecated in Kubernetes version 1.21. The PodSecurityPolicy guidelines set a baseline to strive toward, and the admission controller is available to enforce these baseline guardrails. Many are still on applicable Kubernetes versions, but the recommended replacement for anybody using a version more recent than 1.21 is usage of the Kubernetes pod security standards, with options for privileged, baseline and restrictive levels.
Let’s look at another type of Kubernetes admission controller example.
The configuration of admissions controllers differs slightly when used with managed Kubernetes offerings such as EKS and GKE. In these environments, many admission controllers are already pre-configured within the platform, making it easier for developers to get their applications up and running on a cluster quickly.
That said, EKS admission controllers offer support for OPA, Kyverno, Kubewarden, JSPolicy, and similar solutions. GKE admission controllers integrate with OPA and Kyverno to provide access control policies out of the box.
Given this context, what do you need in an admission controller in order to be successful? Here is a checklist to help you rate vendors’ admission control options and understand whether you are getting something useful, or something you will pay for but never operationalize.
Does the vendor keep the Rego policy in a black box? Is it labeled as ‘proprietary’? This can be an issue when you need to customize policies for your own environment. If you are able to transparently view - and even change - the Rego policy as you see fit, your engineers will be more comfortable with the policies you are enforcing and your most advanced teammates will feel they are in control of any downtime in production caused by admission control.
One of the issues with using the open source version of OPA (or any other admission controller) is the management and implementation across dozens (or even hundreds) of clusters. Quickly, the duplication and QA process becomes overwhelming to the point that admission control is no longer a practical option. However, if admission control policy creation can be folded directly into the regular GitOps, or CI/CD process, quickly the team will be able to scale across multiple clusters and environments, and the engineering team will have access to the policies themselves, which will reduce back-and-forth between engineering and security.
Admission control will block a workload before deployment which, in some cases, can break production. It is critical to have an understand of what is going to happen before it happens. Can your vendor get a view to share with engineering of what workloads would be blocked, based on the policies in place? If not, it will be a rocky road getting to actual enforcement.
Some admission controllers are based only on one risk factor, such as critical or high-rated image CVEs. The breadth of criteria available for enforcement is something to check on ahead of time; do they take into account Kubernetes misconfigurations, at a minimum?
Is the vendor requiring you to send data somewhere else in order to make a policy decision? This is worth checking, especially if you have a broad set of criteria you want to enforce. Do they use OPA, do they use Kyverno? This is important to understand, at a minimum in light of understanding the vulnerabilities your admission control will be subject to. For example, there was a recent Kyverno CVE that required a breaking update. You will want to know what third-party risks you are exposed to.
At RAD Security, we understand the dynamic between the engineers managing Kubernetes and the security teams trying to secure it; in most cases, both teams are focused on security to some extent. RAD Security’s admission controller capability is created to help security teams make Kubernetes secure by default in partnership with their engineering colleagues, with:
RAD Security makes all Rego policies visible in the cluster. Our visibility measures comply with Open Policy Agent (OPA) standards, which perform essential and accessible actions for teams by:
We integrate with GitHub Actions to set policies for both detection and admission control within the CI/CD pipeline, offering:
Instead of going straight to enforcement, you can get an easy list to share with engineering of policies that you would have blocked:
We use a broad range of criteria across Kubernetes configurations and other Kubernetes components, which ensures:
Our threat vector capability also allows you to immediately triage risk across your clusters, which will help tremendously in deciding which admission control policies to set in the first place.
To sum up, this Kubernetes admission controller guide for security engineers champions the role of admission controllers as a central component in the Kubernetes ecosystem. By using mutating and validating admission controllers and dynamic webhooks, organizations can set up a flexible policy enforcement system that is robust and responsive to their own specific security and operational demands.
With a comparison of policy enforcement tools like OPA, Kyverno, and JSPolicy, and our evaluation checklist in hand, security engineers can now decide which policy enforcement tool best fits their needs.
Lastly, RAD Security’s admission controller capabilities show how security engineers can make Kubernetes secure by default. In the future, this strategic adoption of admission controllers will undoubtedly become the bedrock of Kubernetes security, enabling organizations to orchestrate containers confidently.