How do you terminate TLS for a hostname at a Kubernetes Ingress, and what exactly must the referenced Secret contain?
answer
- spec.tls: hosts + secretName
- Secret type kubernetes.io/tls, keys tls.crt + tls.key
- same namespace as the Ingress — no cross-namespace refs
- tls.crt = leaf + intermediates (browser lies, curl tells truth)
- default/fake cert served = SNI matched nothing
basics
~20 sAdd a spec.tls entry listing the hosts and a secretName. The Secret must be type kubernetes.io/tls in the same namespace as the Ingress, with keys tls.crt (leaf plus intermediate chain) and tls.key. The controller loads it and serves that certificate by SNI.
solid answer
~50 sTwo pieces. In the Ingress: a `spec.tls[]` entry with `hosts` and `secretName`. The hosts listed there select which SNI names get that certificate; the rules still control routing. The Secret must be of type `kubernetes.io/tls`, live in the **same namespace as the Ingress** (Ingress cannot reference a Secret in another namespace), and contain exactly two base64-encoded keys: `tls.crt` and `tls.key`. `tls.crt` must be the **full chain** — leaf certificate first, then intermediates — or clients that don't cache the intermediate will fail even though a browser you tested with succeeds. The certificate's SAN must cover the hostnames. The controller watches the Secret and reloads on change, which is what makes cert-manager rotation seamless — it rewrites the Secret and the controller picks it up without a redeploy. Multiple `tls` entries with different Secrets are normal; the controller selects by SNI, falling back to a default certificate when SNI matches nothing.
code
yaml · 21 linesapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
namespace: shop
spec:
ingressClassName: nginx
tls:
- hosts:
- shop.example.com
secretName: shop-tls
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port: {number: 80}go deeper
Name the two pieces: a spec.tls entry with hosts and secretName, and a kubernetes.io/tls Secret with tls.crt and tls.key in the same namespace.
Add that tls.crt must be the full chain, that hostnames must be covered by the certificate SAN, and that the controller picks a certificate by SNI.
Cover rotation via cert-manager and Secret watching, the browser-versus-strict-client chain trap, the default/fake certificate signal, and a concrete debug order.
Frame certificate lifecycle as platform policy: issuance and renewal automation, wildcard versus per-host tradeoffs, cross-namespace distribution, and edge-observed expiry monitoring.
## The Ingress side ```yaml spec: tls: - hosts: ["shop.example.com"] secretName: shop-tls ``` `spec.tls` is a list, and each entry pairs a set of hostnames with one Secret. Its only job is telling the controller which certificate to present for which SNI name; it does not create routing. Routing still comes from `spec.rules`, and a host must appear in the rules to be served at all. Conversely, a host listed only in `rules` and not in `tls` is served over plain HTTP. Most controllers add an automatic HTTP→HTTPS redirect for hosts covered by `tls`, but that is controller behaviour (and usually annotation-tunable), not something the Ingress spec mandates. ## The Secret The Secret must be `type: kubernetes.io/tls`. That type is enforced by the API server to contain exactly two data keys: - `tls.crt` — PEM certificate data. - `tls.key` — the PEM private key, unencrypted (no passphrase; controllers do not prompt). Two details cause most production incidents: **1. Chain order and completeness.** `tls.crt` should hold the leaf certificate first, followed by any intermediate CA certificates, in order. The root is not needed and is normally omitted. If you ship only the leaf, some clients still succeed — browsers often cache intermediates or fetch them via AIA — while curl, Java clients and mobile apps fail with "unable to get local issuer certificate". That asymmetry is exactly why the bug reaches production: the developer's browser test passes. Verify with `openssl s_client -connect host:443 -servername host` and count the certificates in the returned chain. **2. Namespace locality.** The Secret must be in the same namespace as the Ingress. There is no cross-namespace reference mechanism for Ingress TLS, so a shared wildcard certificate must be copied (or synced by a tool) into every namespace that needs it. This is a genuine operational wart and one of the things the Gateway API's ReferenceGrant addresses. Create one directly with `kubectl create secret tls`, which takes the cert and key files and sets the type and keys correctly. ## SNI and multiple certificates One Ingress may carry several `tls` entries, and multiple Ingress objects served by the same controller each bring their own. At handshake time the client sends the hostname in the TLS SNI extension and the controller picks the matching certificate. If nothing matches, controllers serve a **default certificate** — often a self-signed "fake certificate". Seeing that self-signed cert in a browser is a strong signal that SNI matched nothing: the host is missing from `spec.tls`, the Secret is absent or misnamed, the Secret is in the wrong namespace, or the certificate's SAN does not cover the requested name. Note that hostnames matter twice, and independently: in `spec.tls[].hosts` for certificate selection, and in the certificate's own Subject Alternative Name list. A wildcard certificate for `*.example.com` covers `shop.example.com` but not `example.com` itself and not `a.b.example.com`. Common Name is ignored by modern clients — only SANs count. ## Rotation Controllers watch the Secret, so updating its contents reprograms the data plane within seconds without restarting anything. cert-manager relies on this: it obtains and renews certificates (ACME/Let's Encrypt or an internal CA) and writes them back into the same `kubernetes.io/tls` Secret. With the `cert-manager.io/cluster-issuer` annotation on an Ingress, cert-manager can create the Secret from the `spec.tls` entry automatically, including solving the ACME HTTP-01 challenge through the very Ingress being configured. A caveat worth stating in an interview: renewal writes the Secret, but if your controller caches aggressively or the Secret name changes, you can serve a stale certificate. Monitor actual expiry **as observed on the wire** at the edge, not just the Secret's contents. ## Passthrough and mTLS Some deployments must not terminate TLS at the edge — the backend needs the original session, for example for client-certificate authentication end to end. The Ingress spec has no field for that; controllers expose it via annotations such as NGINX's `ssl-passthrough`, which operates on SNI at the TCP level and bypasses HTTP routing entirely, so path rules stop applying for that host. Client-certificate (mTLS) verification at the edge is likewise annotation-driven and controller-specific. Both are reminders that Ingress standardises only basic termination; anything richer is outside the spec. ## Debug order Check the Secret exists in the Ingress's namespace with the right type and keys; check `spec.tls[].hosts` contains the hostname; check the certificate SAN covers it; check chain completeness with `openssl s_client`; check controller events and logs for a load failure. In that order, most TLS incidents resolve in a few minutes.
- A site works in Chrome but a Java client fails with 'unable to find valid certification path'. What do you check first?An incomplete chain in `tls.crt` — almost certainly the leaf certificate without its intermediates. Browsers frequently paper over this by caching intermediates from earlier visits or fetching them via the AIA extension, while stricter clients do not. Rebuild the Secret from the full chain file (leaf first, then intermediates) and confirm with `openssl s_client` that more than one certificate comes back.
- How would you share one wildcard certificate across twenty namespaces?Ingress can only reference a Secret in its own namespace, so the certificate has to exist in each one. In practice you let cert-manager issue and renew it once and use a syncing mechanism — cert-manager's replication support or a tool like reflector — to copy the Secret into the target namespaces, so rotation stays a single source of truth rather than twenty manual copies.
saying these in an interview costs you the question
- Putting the TLS Secret in a different namespace and expecting the Ingress to find it
- Shipping only the leaf certificate and declaring it works because a browser accepted it
- Believing an entry in spec.tls also creates routing for that host
- Using a passphrase-protected private key in the Secret
- Relying on the certificate Common Name instead of Subject Alternative Names