# Ingress Without TLS

> 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.

Source: https://zop.dev/integrations/kubernetes/recommendations/ingress-without-tls
Updated: 2026-08-19

---

## 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

```bash
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 // "-")"'
```
