Kubernetes Policy as Code: Managing Infrastructure with Crossplane
Learn to implement Kubernetes Policy as Code using Crossplane and OPA Gatekeeper. Secure your infrastructure with automated guardrails in a GitOps workflow.
Kubernetes Policy as Code: Managing Infrastructure with Crossplane
I’ve spent years watching teams struggle with "shadow IT" and manual cloud configuration drift. When we moved our infrastructure into Kubernetes using Crossplane, it felt like a superpower. Suddenly, an RDS instance or an S3 bucket became just another CRD (Custom Resource Definition). But with great power comes the inevitable risk of someone spinning up an unencrypted database at 3 AM.
That’s where Kubernetes Policy as Code comes in. By combining Crossplane’s ability to manage cloud resources with OPA Gatekeeper’s validation capabilities, we can enforce compliance before the infrastructure ever hits the provider’s API.
Why Crossplane and Gatekeeper?
Crossplane extends the Kubernetes API to manage external services (AWS, GCP, Azure). Gatekeeper, on the other hand, acts as a validating admission controller using the OPA (Open Policy Agent) engine.
When you define infrastructure as code (IaC) via Crossplane, those resources are just objects in your cluster. If we treat them as objects, we can apply policies to them. We aren’t just securing pods anymore; we’re securing our VPCs, IAM roles, and managed databases.
Setting Up the Guardrails
First, ensure you’re running OPA Gatekeeper v3.11+ and Crossplane v1.14+. You’ll need the gatekeeper-system namespace active.
The Problem: The "Public S3 Bucket" Trap
Let’s say we want to prevent anyone from creating an S3 bucket that is publicly readable. In a standard setup, you'd rely on cloud-level IAM or SCPs. With Policy as Code, we stop it at the Kubernetes API level.
Step 1: Define a ConstraintTemplate
The template defines the logic using Rego. Here’s a simplified version for checking S3 bucket privacy:
REGO# s3-policy.rego package k8spsps3bucket violation[{"msg": msg}] { input.review.object.spec.forProvider.publicAccessBlockConfiguration.blockPublicPolicy == false msg := "Public access is not allowed on S3 buckets via Crossplane." }
Apply this via a ConstraintTemplate CRD.
Step 2: Create the Constraint
Once the template is registered, we enforce it with a Constraint. This tells Gatekeeper to apply the logic to Bucket resources managed by the s3.aws.upbound.io provider.
YAMLapiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sS3PublicAccess metadata: name: block-public-s3 spec: match: kinds: - apiGroups: ["s3.aws.upbound.io"] kinds: ["Bucket"]
Integrating with GitOps
If you’re using ArgoCD or Flux to manage your Crossplane manifests, this setup creates a powerful feedback loop.
- Developer opens a PR: A developer adds a new
Bucketmanifest to the Git repo. - ArgoCD attempts to sync: As ArgoCD pushes the manifest to the cluster, the Kubernetes API server calls Gatekeeper.
- Policy check fails: Gatekeeper rejects the request because the bucket is marked
public: true. - Immediate Feedback: The GitOps controller reports a
SyncFailedstatus. The developer gets immediate notification in their PR that their infrastructure change violates company policy.
This is the ultimate win for DevOps engineers. You’ve shifted security left, removed the need for manual audit scripts, and ensured that your Infrastructure as Code remains compliant by design.
Hard-Won Lessons
I’ve learned a few things the hard way while implementing this:
- Audit Mode First: Always deploy your constraints in
auditmode before switching todeny. You don't want to break production deployments until you're sure your Rego logic is sound. - Keep Rego Simple: Don't write complex, nested Rego logic. If it’s too hard to test, it’s too hard to maintain. Keep policies granular.
- Monitor Violations: Use the Gatekeeper dashboard or Prometheus metrics to track how often your policies are triggered. If you see constant violations, it’s a signal that your developers need better documentation or self-service templates.
Final Thoughts
Managing cloud infrastructure with Crossplane is the future of platform engineering. By layering Kubernetes Policy as Code on top, you move from "hoping" your developers follow the security handbook to "enforcing" that they do. It’s clean, it’s scalable, and it keeps your infrastructure posture predictable.
Start small. Protect your S3 buckets first, then move to IAM policies and RDS instances. Your future self will thank you when the next compliance audit rolls around.