deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Copilot's Kubernetes manifest passed schema validation while carrying 21 security findings

A Copilot-generated Kubernetes Deployment cleared kubeconform's strict schema checks, but a policy scanner found 21 security findings — validation and safety are not the same question.

Copilot's Kubernetes manifest passed schema validation while carrying 21 security findings

A Kubernetes manifest generated by GitHub Copilot cleared schema validation without a single warning, only for a policy scanner to flag 21 security findings on the exact same file. The case, documented in a post on dev.to, is a compact illustration of the gap between "this file is well-formed" and "this workload is safe to run".

The author asked Copilot for a Deployment manifest. The output was valid YAML, and kubeconform confirmed it conformed to the strict Kubernetes schema without complaint. The policy scan told a different story: twenty-one findings, none of which a schema check could ever surface. In the author's framing, the YAML was fine; the Deployment it described was the problem.

Validation and review are different jobs

A structural validator verifies shape, not intent. As the post puts it, kubeconform can confirm that a container's image field is a string, but it has no opinion on whether that container runs as root. Both tools worked exactly as designed; only one of them performed anything resembling a review. The recommendation is to run both, with the fast schema check as a pre-commit hook and the policy scan sitting in CI.

What generated manifests omit

According to the post, three things are missing from Copilot's manifests almost every time:

  • Health probes — no readinessProbe, livenessProbe or startupProbe.
  • Resource requests and limits — a pod without requests can be scheduled anywhere and is first in line for eviction.
  • securityContext — no runAsNonRoot, no allowPrivilegeEscalation: false, no readOnlyRootFilesystem, no dropped capabilities.

Those three omissions are, in the author's view, essentially the entire review: if you inspect nothing else on a generated manifest, inspect those. The post adds that a namespace with Pod Security Admission set to restricted will refuse such a pod at admission time, which is a far better moment to discover the problem than at review.

The shared-probe failure mode

The post singles out one subtle mistake deserving its own attention: generated manifests frequently wire the liveness and readiness probes to the same endpoint. The three probes answer distinct questions — has the process finished booting, should it receive traffic right now, and is it wedged beyond recovery and worth killing. If a shared health endpoint checks the database and the database slows down, every pod fails liveness simultaneously, and Kubernetes responds by restarting the entire deployment in the middle of an incident rather than pulling pods out of rotation and letting them recover.

Two pipeline gotchas

The author also flags two operational traps. First, kubectl apply --dry-run=client is not an offline check: it fetches the OpenAPI schema from the API server and fails outright when no cluster is reachable, making it the wrong tool for a pipeline without cluster credentials — kubeconform fills that role instead. Second, running kubectl config current-context before every command guards against a generated command intended for staging landing on production, a hazard that predates AI but is amplified by how quickly generated commands get pasted.

Where Copilot genuinely helps

Per the post, Copilot's strength in this domain is not producing manifests but explaining them — pointing it at an inherited chart and clarifying what a particular block does, or why a probe is configured a certain way. It also handles mechanical transformations well, such as converting a Deployment to a StatefulSet, adding an init container or expanding a Kustomize overlay. The pattern: the model is strong where the answer follows from the input alone, and weak where the answer depends on what your cluster is actually like.

Making the rules stick

Rather than re-typing requirements in every prompt, the author keeps them in a .github/copilot-instructions.md file: resources on every container, the full securityContext set (runAsNonRoot, allowPrivilegeEscalation: false, readOnlyRootFilesystem, drop ALL), images pinned by digest rather than tag, and three probes pointing at different endpoints. Persisted instructions change what comes back on every request by supplying information the model did not have, which re-prompting never does. The post links to a longer write-up plus a companion repository with the manifests and policy tests.

Why it matters

AI-assisted ops is being adopted faster than review practices are adapting. Schema-clean output produces green pipelines and false confidence, while the specific omissions — root containers, missing limits, shared probes — are precisely the things that turn a slow dependency into a full outage. The lesson generalises beyond Kubernetes: any structural validator, whether for Terraform or OpenAPI, answers a different question than a policy engine. Durable guardrails belong in policy-as-code, admission control and persisted model instructions, not in per-prompt vigilance.

  • #kubernetes
  • #github-copilot
  • #security
  • #devops
  • #policy-as-code

Related posts