← Back to blog
Launch·7 min read

Shipping the PostQ Kubernetes agent

TLS Secrets, Ingress, cert-manager, and mesh mTLS — all in one Helm install.

We just cut v0.2.0of the PostQ Kubernetes agent. It’s shipped two ways — as a multi-arch container image and as a Helm chart, both hosted as OCI artifacts in GitHub Container Registry:

install
helm install postq-agent oci://ghcr.io/postqdev/charts/postq-agent \
  --version 0.2.0 \
  --namespace postq --create-namespace \
  --set apiKey=$POSTQ_API_KEY \
  --set orgId=$POSTQ_ORG_ID \
  --set clusterName=prod-east-1

That one command gives you a read-only, non-root, distroless pod running on a CronJob inside your cluster. It walks every namespace you let it see, finds every quantum-vulnerable algorithm hiding in there, and ships the findings back to your PostQ dashboard over a single outbound HTTPS connection. No inbound network. No sidecars. No webhook controllers.

Why an in-cluster agent?

The PostQ CLI can already point at any TLS endpoint and tell you what’s broken. The web scanner can do the same from the cloud. So why a third surface?

Three things kept coming up in customer calls:

  1. “Half our certs aren’t on the public internet.” Internal services. Service-to-service mTLS. Nothing a SaaS scanner can reach without a corporate firewall change nobody wants to sign.
  2. “We rotate certs every 24 hours via cert-manager.” A point-in-time scan is borderline useless when your cert inventory churns daily. You need a recurring sweep that catches a regression within the hour.
  3. “Show me the asset, not just the algorithm.” Engineers don’t want to hear “you have RSA-2048 somewhere” — they want to hear “Secret prod/api-tls referenced by Ingress prod/api-public uses a 2048-bit RSA key, expires in 17 days, and is signed by thiscert-manager Issuer.”

An in-cluster agent is the right answer to all three. It runs where the truth lives.

What v0.2 actually scans

The agent is a small Go binary that uses client-go and the in-cluster ServiceAccount. Per scan, it walks:

  • kubernetes.io/tls Secrets — parses the leaf cert and the private key, extracts the public-key algorithm and bit length, classifies risk against the current PQC guidance.
  • Ingresses— cross-references TLS-enabled ingresses with the secrets they reference, so a vulnerable cert that’s actually serving traffic gets bumped a severity level.
  • ConfigMaps— yes, people still embed PEM blocks in ConfigMaps. We find them.
  • cert-manager Certificates / Issuers / ClusterIssuers — if the chart is installed, the agent reads the desired spec, not just the rendered Secret, so you see vulnerabilities before they get rotated into the cluster.
  • Istio PeerAuthentication / DestinationRule / Gateway and Linkerd MeshTLSAuthentication — mTLS posture for service meshes. PERMISSIVE mode is loud today; it’ll be louder once Q-day is on the calendar.

Each finding lands in the dashboard with the full Kubernetes coordinates — namespace, kind, name, the cert SANs, the algorithm, the bit length, and a remediation suggestion that points at the relevant NIST FIPS standard.

The chart, in 60 seconds

The two install modes most people will use:

chart-managed Secret (simplest)
helm install postq-agent oci://ghcr.io/postqdev/charts/postq-agent \
  --version 0.2.0 \
  --namespace postq --create-namespace \
  --set apiKey=$POSTQ_API_KEY \
  --set orgId=$POSTQ_ORG_ID \
  --set clusterName=prod-east-1
bring your own Secret (works with External Secrets / Sealed Secrets / SOPS)
# Pre-create the Secret however your org likes
kubectl -n postq create secret generic postq-agent-key \
  --from-literal=POSTQ_API_KEY=$POSTQ_API_KEY

helm install postq-agent oci://ghcr.io/postqdev/charts/postq-agent \
  --version 0.2.0 \
  --namespace postq \
  --set existingSecret.name=postq-agent-key \
  --set orgId=$POSTQ_ORG_ID \
  --set clusterName=prod-east-1

Defaults that we’d like other people to copy: pod runs as UID 65532, read-only root filesystem, all capabilities dropped, seccompProfile: RuntimeDefault. Resource requests sit at 50m / 64Mi — you won’t see this thing on a noisy-neighbor report. The CronJob defaults to hourly; flip oneShot=true to swap it for a one-off Job for a smoke test.

How we ship it

The whole release pipeline is two GitHub Actions workflows. Tag a commit postq-agent-v0.2.0, and:

  1. Image: docker buildx builds linux/amd64 and linux/arm64 from the distroless Dockerfile and pushes to ghcr.io/postqdev/postq-agent:0.2.0 (and :latest).
  2. Chart: helm lint and helm template run on every PR (in default and existingSecret mode), then on tag we helm package and helm push the resulting tarball to oci://ghcr.io/postqdev/charts. A guard step refuses to publish if Chart.yaml and the tag disagree.

Both workflows authenticate with GITHUB_TOKEN (with a PAT fallback for cross-org pushes). No third-party services in the supply chain.

Where this fits

The agent is the third pillar of PostQ’s discovery story:

  • Web— quick spot-checks against any public hostname.
  • CLI— CI gates and scripted scans across an inventory.
  • Kubernetes agent — continuous, in-cluster inventory with full asset context.

They feed the same dashboard, so a finding from the agent deduplicates against a finding from the CLI against a finding from the web scanner. One asset, one verdict, one remediation thread.

What’s next

  • Optional admission webhook that can block a vulnerable Secret or Ingress at apply time, not just report it after the fact.
  • cert-manager Issuer policy hints — suggesting a hybrid ML-DSA + ECDSA profile for issuers that are eligible.
  • SBOM + Cosign signatures on both the image and the chart.
  • Helm chart published to Artifact Hub for one-click discovery.

Try it, file an issue, tell us what you find. The agent is designed to make “how much quantum-vulnerable crypto is in my cluster?” a question with a clear, dated answer instead of a shrug.


Read the docs: postq.dev/docs. Ship a finding to the dashboard: app.postq.dev. Source for the agent + chart lives at PostQDev/postq-scanner-tool.