Securing Kubernetes Ingress with Admission Webhooks and Policy Enforcement
Securing Kubernetes Ingress with Admission Webhooks and Policy Enforcement
Kubernetes Ingress resources are a critical attack surface in modern cloud-native environments, often misconfigured in ways that expose sensitive services to the public internet. Traditional network policies or RBAC rules fall short when addressing nuanced requirements like TLS enforcement, namespace isolation, or traffic routing constraints. This post explores a robust solution using Kubernetes admission webhooks combined with Kyverno for policy-as-code enforcement, ensuring Ingress resources adhere to strict security standards before they are persisted in the cluster.
The core idea is to intercept Ingress resource creation/update requests via a custom admission webhook. This webhook validates incoming requests against predefined policies (stored as code) and rejects non-compliant configurations. Unlike runtime monitoring, this "shift-left" approach prevents vulnerabilities at the deployment stage. We’ll walk through deploying Kyverno policies, creating a minimal webhook controller, and demonstrating how this setup blocks dangerous Ingress configurations.
Enforcing Ingress Policies with Kyverno and Admission Webhooks
Kyverno is a Kubernetes-native policy engine designed for declarative policy management. It can enforce policies like "all Ingress resources must have a secure annotation" or "Ingress rules must not expose ports 80/443 to external IPs". When combined with an admission webhook, Kyverno becomes a gatekeeper for resource creation.
Example 1: Kyverno Policy to Enforce TLS Requirements
Here’s a policy that blocks any Ingress without a secure annotation set to true:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-ingress-tls
spec:
validationFailureAction: enforce
rules:
- name: check-secure-annotation
match:
resources:
kinds:
- Ingress
validate:
message: "Ingress must have 'secure: "true"' annotation"
pattern:
metadata:
annotations:
secure: "true"
This policy ensures any Ingress without the required annotation is rejected during creation.
Example 2: Admission Webhook Controller (Go)
The webhook server validates requests before they’re accepted. Below is a simplified Go implementation using the Kubernetes API:
package main
import (
"encoding/json"
"fmt"
"net/http"
admissionv1 "k8s.io/api/admission/v1"
)
func validateIngress(w http.ResponseWriter, r *http.Request) {
var admissionReview admissionv1.AdmissionReview
json.NewDecoder(r.Body).Decode(&admissionReview)
// Extract Ingress resource from request
ingress := admissionReview.Request.Object.Raw
var ingressSpec map[string]interface{}
json.Unmarshal(ingress, &ingressSpec)
// Check for 'secure' annotation
annotations := ingressSpec["metadata"].(map[string]interface{})["annotations"].(map[string]interface{})
if annotations["secure"] != "true" {
admissionReview.Response = &admissionv1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{Message: "Ingress must have 'secure: true' annotation"},
}
} else {
admissionReview.Response = &admissionv1.AdmissionResponse{Allowed: true}
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(&admissionReview)
}
func main() {
http.HandleFunc("/validate", validateIngress)
http.ListenAndServeTLS(":8443", "server.crt", "server.key", nil)
}
This controller rejects any Ingress missing the secure: true annotation. The webhook must be registered in Kubernetes via a ValidatingWebhookConfiguration:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: ingress-validator
webhooks:
- name: validate-ingress.example.com
admissionReviewVersions: ['v1']
clientConfig:
service:
namespace: default
name: ingress-webhook
path: '/validate'
rules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['ingresses']
failurePolicy: Fail
Deployment and Verification
Deploy the webhook and Kyverno policies using standard YAML manifests. Once Kyverno is installed, apply its policies and the webhook configuration. When a developer attempts to create an Ingress without the required annotation:
kubectl apply -f bad-ingress.yaml
Error from server: [ValidatingWebhookConfiguration.ingress-validator]:
error when creating "bad-ingress.yaml":
Ingress networking.k8s.io "example-ingress" is invalid:
spec: Invalid value: ...: Ingress must have 'secure: true' annotation
The admission webhook blocks the request before it reaches the cluster, enforcing security at the API layer.
Real-World Use Cases and Benefits
This approach is invaluable for enforcing:
- TLS Enforcement: Require all Ingress resources to use HTTPS by mandating a
tlssection in the policy. - Namespace Isolation: Block cross-namespace service references in Ingress rules.
- Public Endpoint Restrictions: Prevent Ingress rules from exposing internal services to external IPs.
By combining Kyverno’s declarative policies with admission webhooks, teams can codify security standards, reduce human error, and achieve compliance at scale. The system also integrates seamlessly into GitOps workflows, where policies are version-controlled and peer-reviewed before deployment.
Conclusion
Kubernetes admission webhooks paired with policy engines like Kyverno provide a proactive defense mechanism for Ingress resources. By validating configurations at the API layer, organizations can prevent misconfigurations from reaching production, mitigate security risks, and maintain strict compliance. This practice elevates DevOps and security teams’ ability to enforce zero-trust principles in dynamic cloud-native environments.