Skip to main content
mastering ckad certified kubernetes application developer

Ingress Rules and TLS Termination

9 min read Chapter 35 of 87
Summary

Covers Ingress as a Layer 7 HTTP routing...

Covers Ingress as a Layer 7 HTTP routing mechanism, the NGINX Ingress Controller setup on Kind, path-based and host-based routing rules, Exact vs Prefix path types, TLS termination with Kubernetes Secrets, complete Ingress YAML examples, and debugging with kubectl get ingress and describe.

Ingress Rules and TLS Termination

Ingress routing: external HTTP traffic enters through the Ingress Controller, which routes requests based on hostname and path to different backend Services

Ingress routing flow: HTTP requests arrive at the Ingress Controller (typically an NGINX or Traefik Pod running inside the cluster). The controller evaluates the request’s hostname and URL path against Ingress rules defined in Ingress resources. Based on matching rules, it proxies the request to the appropriate backend Service. For example, requests to /api route to the api-service while requests to /web route to the web-service. TLS termination happens at the Ingress Controller — it decrypts HTTPS traffic using certificates stored in Kubernetes Secrets, then forwards plain HTTP to backend Services.

What Is an Ingress?

An Ingress is a Kubernetes API resource (kind: Ingress) that defines HTTP and HTTPS routing rules. Unlike a Service, which operates at Layer 4 (TCP/UDP), an Ingress operates at Layer 7 — it inspects HTTP headers, URL paths, and hostnames to decide where to route each request.

Critically, an Ingress resource does nothing on its own. It is a declaration of routing intent. To make it functional, the cluster needs an Ingress Controller — a running process that watches Ingress resources and configures a reverse proxy (NGINX, Traefik, HAProxy, etc.) accordingly.

Think of it this way: the Ingress resource is the configuration file; the Ingress Controller is the web server that reads it.

Setting Up NGINX Ingress Controller on Kind

Kind clusters do not ship with an Ingress Controller. Install the NGINX Ingress Controller with a single command:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

Wait for the controller Pod to become ready:

kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=90s

Verify the controller is running:

kubectl get pods -n ingress-nginx

Expected output:

NAME                                        READY   STATUS    RESTARTS   AGE
ingress-nginx-controller-7d4d8b5c6-xyz12    1/1     Running   0          45s

The Kind cluster configuration from Chapter 2 maps host ports 80 and 443 to the control-plane node. The NGINX Ingress Controller’s Service uses hostPort on these ports, so HTTP traffic to localhost:80 reaches the Ingress Controller.

Path-Based Routing

The most common Ingress pattern routes different URL paths to different backend Services.

Setting Up Backend Services

Create two Deployments and Services:

kubectl create deployment api --image=hashicorp/http-echo -- -text="API response"
kubectl expose deployment api --port=80 --target-port=5678

kubectl create deployment web --image=hashicorp/http-echo -- -text="Web response"
kubectl expose deployment web --port=80 --target-port=5678

Creating the Ingress Resource

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api
                port:
                  number: 80
          - path: /web
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

Key fields:

  • ingressClassName: nginx: Links this Ingress to the NGINX Ingress Controller. Multiple controllers can coexist in a cluster, each watching for Ingress resources with their class name.
  • rules: A list of routing rules. Each rule can specify a host (hostname) and a list of paths.
  • path: /api: Matches requests whose URL path starts with /api.
  • pathType: Prefix: Determines how the path is matched (see below).
  • backend.service: The Service and port to forward matched requests to.

Apply it:

kubectl apply -f app-ingress.yaml

Test the routes:

curl http://localhost/api
# Output: API response

curl http://localhost/web
# Output: Web response

Path Types

Kubernetes supports three path matching strategies:

Prefix

Matches any URL path that starts with the specified value, split on / boundaries:

  • path: /api matches /api, /api/, /api/v1, /api/v1/users
  • path: /api does not match /apis or /apikey (no / boundary match)

Prefix is the most commonly used path type and the one you will reach for most often on the exam.

Exact

Matches the URL path exactly — no prefix matching, no trailing slash tolerance:

  • path: /api matches only /api
  • Does not match /api/ or /api/v1

Use Exact when you need to distinguish between /api and /api/v1 as separate routes.

ImplementationSpecific

Matching behavior depends on the Ingress Controller. NGINX treats this similarly to Prefix, but other controllers may differ. Avoid this type on the exam unless specifically asked — Prefix and Exact cover almost all scenarios.

Host-Based Routing

Host-based routing inspects the HTTP Host header to route requests to different backends:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-host-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api
                port:
                  number: 80
    - host: web.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

Requests to api.example.com route to the api Service. Requests to web.example.com route to the web Service. Both share the same Ingress Controller and external IP.

Test with curl using the Host header:

curl -H "Host: api.example.com" http://localhost/
# Output: API response

curl -H "Host: web.example.com" http://localhost/
# Output: Web response

You can combine host-based and path-based routing within a single Ingress resource. For example, api.example.com/v1 and api.example.com/v2 can route to different backends.

TLS Termination

Ingress Controllers can terminate TLS (HTTPS) using certificates stored in Kubernetes Secrets. The controller decrypts incoming HTTPS traffic and forwards plain HTTP to backend Services.

Step 1: Create a TLS Secret

Generate a self-signed certificate (for lab purposes):

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

Create a Kubernetes Secret of type kubernetes.io/tls:

kubectl create secret tls app-tls --cert=tls.crt --key=tls.key

Step 2: Reference the Secret in the Ingress

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

The tls section tells the Ingress Controller to:

  1. Listen for HTTPS connections on app.example.com
  2. Load the certificate and key from the app-tls Secret
  3. Terminate TLS at the controller and forward decrypted traffic to the web Service on port 80

Test with curl (allowing self-signed certs):

curl -k -H "Host: app.example.com" https://localhost/

The -k flag skips certificate verification (acceptable for self-signed certs in lab environments).

Exam Tip: TLS Secret Format

The Secret must be type kubernetes.io/tls and contain two keys: tls.crt (certificate) and tls.key (private key). Any other format causes the Ingress Controller to reject it silently — the Ingress will not serve HTTPS, and kubectl describe ingress may show a warning in the Events section.

Inspecting and Debugging Ingress

View Ingress Resources

kubectl get ingress

Expected output:

NAME          CLASS   HOSTS             ADDRESS     PORTS     AGE
app-ingress   nginx   *                 localhost   80        5m
tls-ingress   nginx   app.example.com   localhost   80, 443   2m

The PORTS column shows 80, 443 when TLS is configured.

Describe for Details

kubectl describe ingress app-ingress

This shows:

  • The rules and their backend Services
  • Whether the backends have endpoints (connected to Pods)
  • Events — look here for configuration errors

Common Issues

SymptomCauseFix
404 for all pathsNo Ingress Controller runningInstall the NGINX Ingress Controller
503 Service Temporarily UnavailableBackend Service has no endpointsCheck Service selector matches Pod labels
TLS not workingSecret not found or wrong typeVerify Secret exists and is type kubernetes.io/tls
Wrong backend receiving trafficPath matching orderMore specific paths should appear first; use Exact for precise matches
ADDRESS column emptyIngress Controller hasn’t processed the resourceCheck ingressClassName matches the controller

Logs from the Ingress Controller

When routing does not work as expected, check the controller’s logs:

kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller --tail=50

The NGINX controller logs every request with the status code, upstream service, and response time. A 502 or 503 in the log paired with the upstream address helps pinpoint whether the issue is the backend Pod or the routing configuration.

Default Backend

When no Ingress rule matches an incoming request (wrong hostname, wrong path), the Ingress Controller returns a 404. You can configure a default backend that handles unmatched requests:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: fallback-ingress
spec:
  ingressClassName: nginx
  defaultBackend:
    service:
      name: fallback-svc
      port:
        number: 80
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api
                port:
                  number: 80

Requests to app.example.com/api route to the api Service. Requests to any other path on app.example.com, or requests with no matching host, route to fallback-svc. This is useful for serving custom 404 pages or redirecting unknown paths.

Ingress Annotations

Ingress Controllers extend Ingress behavior through annotations. The NGINX Ingress Controller supports dozens of annotations for customizing routing, timeouts, rate limiting, and more. Key annotations for CKAD:

annotations:
  # Rewrite the URL path before forwarding
  nginx.ingress.kubernetes.io/rewrite-target: /
  
  # Enable SSL redirect (HTTP → HTTPS)
  nginx.ingress.kubernetes.io/ssl-redirect: "true"
  
  # Set proxy timeout
  nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
  
  # Limit request body size
  nginx.ingress.kubernetes.io/proxy-body-size: "10m"

The rewrite-target annotation is the most commonly needed. Without it, a request to /api/users is forwarded as /api/users to the backend. With rewrite-target: /, the path is rewritten to /users before forwarding. If the backend expects paths without the Ingress prefix, this annotation is required.

Exam Tips for Ingress

  • Always verify the Ingress Controller is running before creating Ingress resources. On the exam, the controller may already be deployed.
  • Use ingressClassName rather than the deprecated kubernetes.io/ingress.class annotation. The field-based approach is the current standard.
  • TLS Secrets must be in the same namespace as the Ingress resource. Cross-namespace Secret references are not supported.
  • Test with curl and Host header: curl -H "Host: my.domain.com" http://<ingress-ip>/ lets you test host-based routing without DNS configuration.
  • Check Events section of kubectl describe ingress for configuration errors — this is where the controller reports issues with missing backends or invalid TLS Secrets.