Learn to implement Kubernetes Policy as Code using Crossplane and OPA Gatekeeper. Secure your infrastructure with automated guardrails in a GitOps workflow.
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.
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.
First, ensure you’re running OPA Gatekeeper v3.11+ and Crossplane v1.14+. You’ll need the gatekeeper-system namespace active.
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.
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.
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"]
If you’re using ArgoCD or Flux to manage your Crossplane manifests, this setup creates a powerful feedback loop.
Bucket manifest to the Git repo.public: true.SyncFailed status. 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.
I’ve learned a few things the hard way while implementing this:
audit mode before switching to deny. You don't want to break production deployments until you're sure your Rego logic is sound.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.
Master GitOps with Argo CD and Crossplane to manage infrastructure as code. Learn how to unify your Kubernetes deployment strategy for apps and cloud resources.
Read moreMaster Kubernetes Secret Management by syncing HashiCorp Vault with External Secrets Operator. Learn how to automate secure, GitOps-friendly secret injection.