Ingress resources serving without a TLS block
What does ZopNight detect here?
An Ingress with no `tls` section serves plaintext HTTP, exposing cookies and tokens on the wire. Adding 1 `tls` block plus a matching `kubernetes.io/tls` Secret fixes it, and cert-manager issues and renews those free from Let's Encrypt. ZopNight skips `cm-acme-http-solver-*` ingresses, which are HTTP-only by protocol design.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1720 · RC-1820 · RC-1920 |
| Category | security |
| Severity | high |
| Metric | none — pure configuration read |
| Source | internal/rules/k8s/ingress_no_tls.go |
Where it applies
Plaintext at the edge, not just in transit
An Ingress is usually the first thing outside the cluster touches. Without a tls block, the
controller serves the route over HTTP, and everything on the wire (session cookies, bearer
tokens, form posts) is readable by anything between the client and the load balancer.
The part worth being precise about: a cloud load balancer sitting in front of the Ingress may already be terminating TLS. In that case the exposure is hop-by-hop rather than end-to-end, and the finding is about the leg from the load balancer to the cluster, not about the browser connection. Check which is true before deciding urgency.
The ACME solver exception
Ingresses named cm-acme-http-solver-* are excluded. cert-manager creates these temporarily to
answer an HTTP-01 challenge, which by protocol definition must be served over plain HTTP.
Let’s Encrypt fetches a token from /.well-known/acme-challenge/ before any certificate exists.
Flagging them would mean flagging the mechanism that fixes the finding.
Why this rarely stays unfixed once someone looks
Certificates are the reason people assume this is hard, and they have not been hard for some years. cert-manager watches Ingress resources, requests a certificate from Let’s Encrypt, writes the Secret, and renews it before expiry with no human involved. The cost is zero.
Most untagged Ingresses in this state are not a decision. They are a manifest written before cert-manager was installed and never revisited.
TLS present is not redirect present
Adding the tls block makes HTTPS work. It does not stop HTTP working. A client that types the
bare hostname still gets plaintext unless the controller is told to redirect, which on
ingress-nginx is the ssl-redirect annotation and on other controllers is its own equivalent.
Half-finished remediation here is common and looks complete from a certificate check.
When the rule stays silent
If the discoverer captured no tlsEnabled key for the Ingress, nothing is emitted. Missing
metadata is not read as “no TLS”.
Listing Ingresses with an empty spec.tls
kubectl get ingress -A -o json | jq -r ' .items[] | select((.spec.tls | length) == 0) | select(.metadata.name | startswith("cm-acme-http-solver-") | not) | "\(.metadata.namespace)/\(.metadata.name)\t\(.spec.rules[]?.host // "-")"'