Back to Blog

Securing Ingress with TLS for the CKS Exam: TLS Secrets, Termination, HTTPS Redirects & Hardening the Controller

A practitioner's guide to setting up Ingress with TLS for the CKS exam. How to create a TLS Secret the right way, wire it into spec.tls, why the Secret must live in the Ingress namespace, HTTP-to-HTTPS redirects, minimum TLS versions, client-certificate mTLS, and how to harden the ingress controller itself.

By Sailor Team , September 7, 2026

“Properly set up Ingress with TLS” is one line in the CKS Cluster Setup domain, and it is one of the most reliably testable skills on the whole exam. It is hands-on, it has a small number of moving parts, and there are two or three specific mistakes that trip up almost everyone the first time. That combination — narrow, mechanical, and easy to get subtly wrong — is exactly what a performance-based exam loves.

This guide is only about the securing angle: how TLS attaches to an Ingress, how to create the Secret it needs, and how to harden the path from client to controller. If you need to review how Ingress routing itself works — rules, paths, hosts, the controller, and the newer Gateway API — read the Ingress and Gateway API guide first, then come back here. This topic also sits inside the broader CKS Cluster Setup and hardening material, and it pairs naturally with network policies, since a hardened ingress controller still needs its traffic locked down.

What “Ingress with TLS” Actually Means

An Ingress object is a set of routing rules. On its own it carries no encryption. TLS is added by pointing the Ingress at a TLS Secret — a Kubernetes Secret of type kubernetes.io/tls that holds a certificate and its private key. The ingress controller reads that Secret and uses it to terminate TLS for the hostnames you list.

The critical word is terminate. In the default and by far most common model, the client opens an HTTPS connection to the ingress controller, the controller decrypts it, and then forwards the request to your backend Service — usually over plain HTTP inside the cluster. So “Ingress with TLS” secures the hop that matters most for the exam: the public, untrusted hop between the outside world and the edge of your cluster.

There are three termination models worth knowing by name:

ModelWhere TLS is decryptedWhen you use it
TLS termination (edge)At the ingress controllerThe default; the client-to-edge hop is encrypted
TLS passthroughNot at the controller — the backend does itThe backend must see the raw TLS (e.g. it does its own client-cert checks)
Re-encryptionTerminated at the controller, then a new TLS session to the backendYou need encryption in transit inside the cluster too

For CKS, edge termination is the one you will configure. Know that passthrough and re-encryption exist and what they trade off, but the muscle memory you need is creating a TLS Secret and referencing it in spec.tls.

Step 1: Create the TLS Secret

A TLS Secret holds exactly two keys: tls.crt (the certificate, or full chain) and tls.key (the private key). The single fastest way to create one:

kubectl create secret tls my-app-tls \
  --cert=tls.crt \
  --key=tls.key \
  -n my-app

This is the command to have in your fingers on exam day. A few things that separate a pass from a lost mark:

  • The key names are fixed. Inside the Secret, the data is stored under tls.crt and tls.key. If you build the Secret by hand from YAML and use certificate/key or any other names, the controller will not find the cert and will silently serve its own default (fake) certificate instead. kubectl create secret tls sets the right names for you — prefer it.
  • The values are base64-encoded PEM. If you write the manifest yourself, remember data: fields are base64. Using stringData: lets you paste raw PEM and let Kubernetes encode it.
  • --cert should be the full chain (leaf + any intermediates) when a CA chain is involved, or clients may reject the connection with an “unknown authority” error even though the leaf is valid.

If you need a certificate for a lab and have no CA, generate a self-signed one with openssl:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout tls.key -out tls.crt \
  -subj "/CN=app.example.com/O=app.example.com"

Then feed tls.crt and tls.key into the kubectl create secret tls command above. Self-signed is fine for the exam — you are being tested on wiring, not on trust chains.

The mistake that costs the most people: namespace

The TLS Secret must live in the same namespace as the Ingress that references it. An Ingress cannot reference a Secret in another namespace. If your Ingress is in my-app and your Secret is in default, TLS will not work, and the controller will fall back to its default certificate with no obvious error on the Ingress object.

This is the number-one failure in both the exam and real clusters, precisely because it fails quietly. Always create the Secret in the Ingress’s namespace, and verify it with kubectl get secret -n my-app.

Step 2: Wire the Secret into the Ingress

TLS attaches through the spec.tls block. Each entry lists one or more hosts and the secretName that provides the certificate for them:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  namespace: my-app
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com
      secretName: my-app-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-svc
                port:
                  number: 80

Two consistency checks the exam rewards:

  1. The host in spec.tls[].hosts should match the host in spec.rules[].host. The certificate is presented based on the TLS SNI (Server Name Indication) the client sends, which is the hostname. A mismatch means the controller cannot pick the right cert.
  2. The backend port is your Service’s port, not 443. TLS is terminated at the controller; the backend still receives plain HTTP on whatever port the Service exposes.

Multiple hosts and multiple certificates

If one Ingress serves several hostnames, you can either put them under one tls entry (if one certificate — for example a wildcard *.example.com — covers them all) or use several tls entries, each with its own Secret:

  tls:
    - hosts:
        - app.example.com
      secretName: app-tls
    - hosts:
        - api.example.com
      secretName: api-tls

The controller uses SNI to serve the correct certificate per hostname. This is worth understanding conceptually: SNI is how one IP and one controller can serve many TLS sites with distinct certs.

Step 3: Verify It Worked

Do not assume. Verify. kubectl describe ingress shows whether the controller accepted the TLS block:

kubectl describe ingress my-app -n my-app

Then check what certificate is actually being served. If you have external reachability in the lab:

# See the cert the controller presents for this SNI host
openssl s_client -connect <ingress-ip>:443 -servername app.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer

If the subject/issuer comes back as something like Kubernetes Ingress Controller Fake Certificate, your Secret is not being used — almost always because of the wrong namespace, wrong key names inside the Secret, or a host/SNI mismatch. That fake-certificate symptom is the single most useful troubleshooting signal for this topic; recognizing it instantly tells you the wiring is off, not the cert.

You can also confirm from inside the cluster without external DNS:

curl -k --resolve app.example.com:443:<ingress-ip> https://app.example.com/

The --resolve flag forces the hostname to the controller’s IP so SNI is sent correctly; -k skips trust validation for a self-signed cert.

Step 4: Force HTTPS (Redirect HTTP to HTTPS)

Configuring TLS does not, by itself, stop plain HTTP from working. To require HTTPS you enable an SSL redirect. This is controller-specific and is set through annotations, not core Ingress fields. For the common NGINX ingress controller:

metadata:
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"

ssl-redirect redirects HTTP to HTTPS when TLS is configured for the host; force-ssl-redirect does so even in cases where the controller might otherwise serve HTTP. Because these are annotations owned by the controller, the exact keys differ between controllers — the exam environment will use a specific one, so read the task and match its controller. The concept to carry in is: TLS configured ≠ HTTP disabled; you must explicitly redirect.

Hardening Beyond the Basics

Getting a green padlock is the floor, not the ceiling. CKS is a security exam, so know how to make the TLS itself strong and the controller itself defensible.

Minimum TLS version and ciphers

Old TLS versions (1.0, 1.1) are deprecated and weak. Enforce a modern floor at the controller. For the NGINX ingress controller this lives in its ConfigMap, not on the Ingress object:

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  ssl-protocols: "TLSv1.2 TLSv1.3"
  ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384"

The takeaway is architectural: cipher and protocol policy is a controller-wide setting, applied once in the controller’s ConfigMap, not per-Ingress. That is a common “where does this setting go?” style question.

Client-certificate authentication (mTLS at the edge)

You can require clients to present their own certificate — mutual TLS — so only holders of a trusted client cert can reach the backend. With the NGINX controller this is again annotation-driven, backed by a Secret holding the CA that signs valid client certs:

metadata:
  annotations:
    nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
    nginx.ingress.kubernetes.io/auth-tls-secret: "my-app/client-ca"

mTLS at the ingress is how you turn “encrypted” into “encrypted and authenticated” for sensitive endpoints. It complements the workload-to-workload mTLS you get from a service mesh, which is covered in the minimize microservice vulnerabilities material — the ingress guards the north-south (client-to-cluster) hop, the mesh guards the east-west (pod-to-pod) hops.

Harden the controller itself

The ingress controller is a privileged, internet-facing component. It terminates TLS for every app behind it, which means it holds every one of those private keys in memory. Treat it as high-value:

  • Restrict its network exposure with a NetworkPolicy. Only the controller should be reachable from outside on 80/443; backends should accept traffic only from the controller’s namespace or pod selector. This dovetails directly with default-deny network policies.
  • Scope its ServiceAccount RBAC. The controller needs to read Ingresses, Services, Endpoints, and TLS Secrets — nothing more. It should never have cluster-admin. Over-permissioned controllers are a real lateral-movement path.
  • Run it in its own namespace (commonly ingress-nginx) so its blast radius and policies are contained.
  • Keep it patched. Ingress controllers have a history of high-severity CVEs precisely because they parse untrusted input at the edge. Verifying and updating it is part of the same discipline as verifying platform binaries in the Cluster Setup domain.

Common CKS Exam Tasks (and How to Nail Them)

The exam tends to phrase this topic as a small, concrete change. Practice these until they are automatic:

  • “Create a TLS Secret from these files and secure this Ingress.” → kubectl create secret tls <name> --cert=... --key=... -n <ns>, then add or edit the spec.tls block referencing <name>. Verify the namespace matches.
  • “HTTPS works but HTTP should be rejected.” → add the controller’s ssl-redirect/force-ssl-redirect annotation.
  • “TLS is configured but the wrong (default) certificate is served.” → check three things in order: Secret namespace, Secret key names (tls.crt/tls.key), and host/SNI match. It is almost always one of those three.
  • “Only clients with a valid certificate may access this endpoint.” → client-cert mTLS annotations plus a CA Secret.

Frequently Asked Questions

Does the TLS Secret have to be type kubernetes.io/tls?

Yes for correctness and for the redirect/verification logic to behave. kubectl create secret tls creates it with the right type and the required tls.crt / tls.key keys. A generic Opaque Secret with differently named keys will not be recognized as a TLS cert by the controller.

Why does my Ingress serve a “fake certificate”?

The controller falls back to a self-signed default when it cannot use your Secret. The usual causes, in order of likelihood: the Secret is in a different namespace than the Ingress, the Secret uses the wrong internal key names, or the requested hostname (SNI) does not match any spec.tls[].hosts entry.

Is cert-manager part of the CKS exam?

No. cert-manager is a popular real-world tool for automating certificate issuance and renewal, but it is out of scope for CKS. The exam expects you to create and wire a TLS Secret manually. Learn cert-manager for your day job; for the exam, master kubectl create secret tls and spec.tls.

Where do TLS version and cipher settings go — the Ingress or the controller?

The controller. Protocol and cipher policy is a controller-wide setting (for NGINX, its ConfigMap). The spec.tls block on an Ingress only says which certificate to use for which hosts — not how TLS is negotiated.

Does terminating TLS at the ingress encrypt traffic inside the cluster?

No. Edge termination encrypts only the client-to-controller hop; traffic from the controller to your backend is plain HTTP by default. If you need in-cluster encryption too, use re-encryption at the ingress or workload mTLS via a service mesh, and lock down the internal path with network policies.

Bringing It Together

Ingress with TLS rewards precision over theory. The whole skill compresses to: create a kubernetes.io/tls Secret in the right namespace with kubectl create secret tls, reference it from spec.tls with hosts that match your rules, force HTTPS with the controller’s redirect annotation, and then harden the negotiation (TLS floor, ciphers, optional mTLS) and the controller (NetworkPolicy, least-privilege RBAC, its own namespace, patched). If you can do that from memory and diagnose the “fake certificate” symptom on sight, this Cluster Setup objective is a guaranteed set of marks.

Practice This Under Exam Conditions

Reading the steps is not the same as doing them against a clock in a live cluster. The Certified Kubernetes Security Specialist (CKS) mock exam bundle is built for exactly that: five full-length, performance-based labs with 100+ hands-on scenarios spanning all six CKS domains, each with detailed explanations that draw the same distinctions this article does — including securing ingress, network policies, and controller hardening. You get 90 days of access to repeat the labs until the muscle memory is automatic.

Pair it with the CKS exam guide for 2026 to map your full preparation, the CKS study plan to pace it, and the CKS exam topics breakdown to see where Cluster Setup fits against the rest of the curriculum. Learn the concept here, then drill it until “set up Ingress with TLS” is something your hands do without thinking.

Limited Time Offer: Get 80% off all Mock Exam Bundles | Sale ends in 7 days. Start learning today.

Claim Now