
Identity-Driven Kubernetes Access: Beyond RBAC with Gatekeeper and Kyverno
An examination of identity-driven Kubernetes access management using OPA Gatekeeper and Kyverno for enhanced policy-as-code security.
Kubernetes Role-Based Access Control (RBAC) remains the foundational standard for securing clusters, yet it operates on a static trust model. When an administrator creates a Role binding for a developer-role, permissions are granted based on a fixed set of rules attached to a user or group. This approach fails when security policies must depend on dynamic context, such as the specific image registry being used, the time of day, or the specific claims within a JSON Web Token (JWT) issued to a service account. To transcend these static limitations, we must shift from verifying "who you are" to evaluating "what you are doing and under what conditions." This paradigm shift is enabled by Open Policy Agent (OPA) Gatekeeper and Kyverno, which transform the Kubernetes API server into a dynamic policy engine capable of identity-driven access control.
The Mechanism of Failure in Static RBAC
The fundamental failure of standard RBAC manifests at the moment of authorization. When a request reaches the Kubernetes API server, the AuthZ layer evaluates the SubjectAccessReview API against defined RoleBindings and ClusterRoleBindings. This evaluation checks if the subject—whether a user, group, or service account—possesses a matching verb (get, create, delete) on a specific resource (pods, deployments). However, this check is blind to the payload content.
If a user with create permissions on pods sends a request to spin up a container running the latest tag from a public registry, RBAC permits it because the permission exists. This occurs even if the action violates the organization's security posture regarding supply chain integrity. The system grants access based on identity alone, ignoring the dynamic nature of the request payload.
The Admission Control Pipeline
To resolve this, we leverage the Kubernetes Admission Controller webhook interface. This mechanism intercepts requests before they are persisted to etcd. Both Gatekeeper and Kyverno register as validation or mutation webhooks. When a request arrives, the API server pauses the write operation and sends the request object, along with the user's identity context (if configured), to the policy engine.
The engine evaluates a set of rules and returns a deny or allow decision. If denied, the API server rejects the request immediately, never touching the cluster state. This shifts the security boundary from the static definition of "who can do what" to a dynamic evaluation of "is this specific action compliant right now?"
Identity Context Injection
In a concrete scenario, consider a user alice in the frontend team attempting to deploy a new microservice. The request includes a ServiceAccount token containing a JWT. An identity-driven admission controller can extract the iss (issuer) and aud (audience) claims from the token. If the iss does not match the internal identity provider, the controller denies the deployment regardless of whether alice possesses the standard RBAC permission to create a Deployment. Furthermore, the controller can inspect the sub (subject) claim to ensure the specific service account ID matches the expected workload identity, adding a layer of granularity that standard RBAC cannot achieve without external tooling.
This demonstrates "identity-driven" logic where the decision is not just about the user's role, but about the context of the action itself.
OPA Gatekeeper: Constraint Enforcement
OPA Gatekeeper implements this mechanism using a ConstraintTemplate and Constraint architecture. You define a template in YAML that describes the logic (e.g., "images must be signed"), and then you instantiate a Constraint that applies that logic to specific namespaces or resources. Gatekeeper is particularly strong at "constraint enforcement." This separation allows administrators to define the policy logic once in a template and apply it across multiple namespaces or resources without rewriting the code. The ConstraintTemplate lifecycle ensures that the logic is version-controlled and reusable, while the Constraint resources act as the active configuration that triggers the evaluation against incoming requests.
# OPA Gatekeeper ConstraintTemplate
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg}] {
input.review.object.kind == "Deployment"
not input.review.object.metadata.labels["team"]
msg := sprintf("Deployment %v must have label 'team'", [input.review.object.metadata.name])
}Kyverno: Policy-as-Code and Mutation
Kyverno approaches the same problem with a policy-as-code philosophy, treating policies as native Kubernetes resources (ClusterPolicy and Policy). While Gatekeeper focuses heavily on the ConstraintTemplate pattern, Kyverno excels at mutation and verification logic written in Go-like patterns within the YAML. In our alice scenario, Kyverno can not only reject the request but also automatically mutate it. If alice forgets to specify a resource limit, Kyverno can inject a default resources.limits block before the object reaches etcd, effectively fixing the configuration error before it becomes a production issue. The use of Go-like patterns makes these policies more readable for developers who are already familiar with Kubernetes YAML structures, reducing the learning curve compared to writing Rego.
# Kyverno Policy for Image Verification
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-image-signature
spec:
validationFailureAction: Enforce
rules:
- name: check-image-signature
match:
resources:
kinds:
- Pod
verifyImages:
- imagePatterns:
- "*"
key: |-
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
cty: "sha256"The distinction between these two tools often comes down to the mechanism of implementation. Gatekeeper relies on the OPA Rego language, a declarative query language designed for complex logic trees. It is incredibly powerful for scenarios requiring deep traversal of the object graph or cross-resource checks. Kyverno uses a more procedural approach within its policy definitions, making it easier for Kubernetes-native developers to read and write policies without learning a new language like Rego.
Operational Tradeoffs and Deployment Strategy
However, this power comes with a cost. Every admission request now triggers a network call to the policy engine. In a high-throughput cluster, this adds latency to the API server. If the policy engine is slow or unavailable, it can introduce significant latency or cause request failures for affected resources, potentially impacting cluster availability depending on failure mode configuration (e.g., blocking vs. non-blocking). Refer to the Kubernetes admission controller failure modes documentation for details on configuring failurePolicy: Ignore versus Ignore to mitigate total outages. Therefore, the deployment strategy for these engines is critical. They must be run with high availability, and their policy evaluation logic must be optimized. For instance, using matchExpressions in Kyverno or specific constraint scopes in Gatekeeper to avoid evaluating every policy on every request is essential.
Furthermore, identity-driven access requires that the identity context be passed correctly to the webhook. Standard RBAC does not pass the full JWT payload to the admission controller by default. You must configure the API server's --service-account-issuer and --service-account-key flags to ensure the token is projected and the claims are injected into the Authorization header or extra fields of the Subject in the AdmissionReview. Note that the ServiceAccountToken projection is primarily for mounting tokens into pods, not for passing them to admission controllers; the latter relies on the API server's token injection configuration. Without this, the policy engine is just a firewall checking IP addresses, not a gatekeeper checking identities.
In practice, the choice between Gatekeeper and Kyverno depends on your team's expertise. If your team is comfortable with functional programming and needs complex, cross-resource logic, Gatekeeper's Rego engine is superior. If your team prefers a YAML-first, Kubernetes-native approach and needs frequent mutations to fix misconfigurations automatically, Kyverno is the pragmatic choice. Both, however, solve the same core problem: they decouple the decision to allow an action from the static definition of the user, allowing you to enforce security policies based on the dynamic reality of your workloads.
Conclusion
Ultimately, moving to policy-as-code is not just about adding another layer of security; it is about shifting the security model from "trust but verify" to "verify everything." By embedding identity and compliance logic directly into the admission control pipeline, you ensure that no resource ever enters your cluster unless the user's specific claims (e.g., issuer, audience) match the policy criteria. This is the only way to secure modern, ephemeral, and identity-rich Kubernetes environments.
FAQ
Q: Can I use both OPA Gatekeeper and Kyverno in the same cluster?
A: Yes, you can run both simultaneously, but you must carefully manage the failurePolicy and webhook order. Since both intercept requests, running both can increase latency significantly. It is generally recommended to choose one as the primary policy engine unless you have a specific need to leverage the unique strengths of both (e.g., Gatekeeper for complex cross-resource constraints and Kyverno for simple mutations).
Q: How does Kyverno handle policy updates compared to Gatekeeper?
A: Kyverno policies are native Kubernetes resources, meaning you can use kubectl apply or GitOps tools like ArgoCD to update them instantly, and the changes are visible in the cluster's state immediately. Gatekeeper uses ConstraintTemplate and Constraint resources which are also Kubernetes-native, but the underlying OPA logic is compiled into a decision tree, so updates might require a brief reload of the Gatekeeper controller depending on the configuration.
Q: Does identity-driven access require a specific Identity Provider (IdP)? A: Not strictly, but it requires that your IdP issues JWTs with claims that the admission controller can read. This works best with OIDC-compliant providers like Google Cloud IAM, Azure AD, or Okta. The Kubernetes API server must be configured to trust the IdP's issuer and validate the token signatures before passing the claims to the policy engine.
Common Pitfalls
- Assuming RBAC is sufficient: Relying solely on RBAC for security ignores the content of the request. A developer with
createpermissions can still deploy vulnerable images if RBAC is the only control. - Ignoring Failure Modes: Configuring admission controllers with
failurePolicy: Failwithout high availability can take down your entire cluster if the policy engine goes offline. Always test the "blocking" vs. "non-blocking" behavior. - Misconfiguring Identity Injection: Assuming the JWT claims are automatically available to the webhook. You must explicitly configure the API server to project and inject these claims into the
AdmissionReviewobject, otherwise, the policy engine receives empty or generic identity data.
Practical Takeaways
- Shift Left: Move security checks from post-deployment scanning to the admission control phase to prevent violations from ever entering the cluster.
- Context Matters: Leverage identity-driven access to make decisions based on dynamic claims (issuer, audience) rather than just static user roles.
- Choose Wisely: Select Gatekeeper for complex, declarative logic and Kyverno for native, mutation-heavy workflows that require minimal learning curves.
Related posts
Kubernetes RBAC and Service Account Security
An examination of Kubernetes RBAC and service account security strategies to enhance cluster protection using Kyverno and pod security policies.
RFC 8693: Token Exchange, Delegation, and Impersonation
RFC 8693 defines token exchange, delegation, and impersonation mechanisms for OAuth 2.0, enabling secure identity propagation across service boundaries.
Implementing Conditional Access Policies with Keycloak and ForgeRock
A technical examination of implementing conditional access policies using Keycloak and ForgeRock for context-aware access control.