Back to Blog
Software EngineeringTechnologyJune 19, 20263 min read

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.

KubernetesCrossplaneOPADevOpsInfrastructure as CodeGitOpsSecurityLinuxServer

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.

YAML
apiVersion: 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.

  1. Developer opens a PR: A developer adds a new Bucket manifest to the Git repo.
  2. ArgoCD attempts to sync: As ArgoCD pushes the manifest to the cluster, the Kubernetes API server calls Gatekeeper.
  3. Policy check fails: Gatekeeper rejects the request because the bucket is marked public: true.
  4. Immediate Feedback: The GitOps controller reports a 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.

Hard-Won Lessons

I’ve learned a few things the hard way while implementing this:

  • Audit Mode First: Always deploy your constraints in audit mode before switching to deny. 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.

Similar Posts