Skip to content

TLS Offloading Using Ingress


Before this practical demonstration, it is important to understand Kubernetes Ingress which is what previous chapter does. Allocate your 5 minutes there before starting this.

What is TLS Offloading

TLS offloading is a network technique that shifts the heavy processing work of Transport Layer Security (TLS)—commonly still called SSL—encryption and decryption away from your main application or web servers to a specialized intermediary device, such as a load balancer.

Main Types of TLS Offloading

TLS Termination: The intermediary decrypts the incoming TLS traffic completely and passes it as plain HTTP to the internal backend servers. This maximizes performance and saves internal CPU cycles, though internal network traffic flows unencrypted.

    flowchart LR
    C[Client] -->|HTTPS| LB[Load Balancer<br/>TLS Termination]
    LB -->|HTTP| S[Backend Server]
  

TLS Bridging (Re-encryption): The intermediary decrypts the traffic to inspect it for security threats or malware, and then re-encrypts it before forwarding it to the backend servers. This keeps internal data protected in transit.

    flowchart LR
    C[Client] -->|HTTPS| LB[Load Balancer<br/>TLS Decryption]
    LB -->|HTTPS<br/>New TLS Connection| S[Backend Server]
  

TLS Tunneling / Pass-through: The intermediary passes the encrypted traffic straight to the backend server without decrypting it at the edge.

    flowchart LR
    C[Client] -->|HTTPS<br/>Encrypted TLS| LB[Load Balancer<br/>TCP Forwarding]
    LB -->|HTTPS<br/>Encrypted TLS| S[Backend Server<br/>TLS Termination]
  

Project/Example Overview

    flowchart TB
    C[Client / Browser]

    C -->|HTTPS: main.example.com| IG
    C -->|HTTPS: docs.example.com| IG

    subgraph K8S["Kubernetes Cluster"]
        IG["Ingress Controller<br/>TLS Termination + Host-Based Routing"]
        TLS[("TLS Secret<br/>my-site-tls")]

        IG -.->|Loads certificate| TLS

        IG -->|HTTP :8080<br/>Host: main.example.com| MS[main-svc<br/>Service]
        IG -->|HTTP :8080<br/>Host: docs.example.com| DS[docs-svc<br/>Service]

        MS --> MP["Main App Pods"]
        DS --> DP["Docs App Pods"]
    end

    style IG fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
    style TLS fill:#fef3c7,stroke:#d97706,color:#92400e
    style MS fill:#dcfce7,stroke:#16a34a,color:#166534
    style DS fill:#dcfce7,stroke:#16a34a,color:#166534
  

How it works:

  1. The client sends an HTTPS request to either hostname.
  2. The Ingress Controller uses the TLS certificate from my-site-tls to terminate TLS.
  3. It routes the decrypted HTTP request according to the hostname:
    • main.example.com → main-svc:8080 → Main App Pods
    • docs.example.com → docs-svc:8080 → Docs App Pods

Note

Both hostnames are configured to use the same TLS Secret. The certificate must be valid for both hostnames so we will issue self signed wildcard certificate.

Project Structure

The code is available at Github. If you are reading Ingress example, you should be able to build image, read and write services.yaml and maybe everything except ingress.yaml. Therefore, I have only explained important part that is about secret.yaml and ingress.yaml.

.
├── default-ingressclass.yaml
├── deploy.yaml
├── docs/
│   └── index.html
├── docs.Dockerfile
├── html/
│   └── index.html
├── ingress.yaml
├── main.Dockerfile
├── secret.yaml
└── services.yaml
  • main.Dockerfile: Builds an NGINX image that serves the main site’s html/index.html.
  • docs.Dockerfile: Builds an NGINX image that serves the documentation site’s docs/index.html.
  • deploy.yaml: Defines the Deployments for the main and documentation applications.
  • services.yaml: Exposes the application Pods through Kubernetes Services.
  • ingress.yaml: Configures HTTPS and host-based routing for main.example.com and docs.example.com.
  • secret.yaml: Defines the TLS Secret using the site’s certificate and private key.
  • default-ingressclass.yaml: Configures the default IngressClass, if needed.
  • html/ and docs/: Contain the HTML pages served by the respective applications.

The two applications use separate container images and Services, while a single Ingress routes requests to the correct application based on the hostname.

Example TLS Termination

The Ingress API only supports TLS termination and simple content-based request routing of HTTP traffic.

The connection from client to laod balancer is encrypted which means TLS key and certificate is required for the load balancer for example site example.com:

openssl req -x509 -nodes -days 365 -newkey rsa:3072 -keyout example.com.key -out example.com.crt

In Common Name field provide *.example.com because we are using same certificate for both main.example.com and docs.example.com.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-host-based-routing-with-tls
spec:
  tls:
  - hosts:
      - main.example.com
      - docs.example.com
    secretName: my-site-tls
  rules:
  - host: "main.example.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: main-svc
            port:
              number: 8080
  - host: "docs.example.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: docs-svc
            port:
              number: 8080

Note

Since, example.com is not a domain you own, you need to modify your DNS settings to access the site using those names. For minikube you can:

echo "$(minikube ip)    main.example.com docs.example.com" | sudo tee -a /etc/hosts

Before the Ingress can serve HTTPS, Kubernetes needs a TLS Secret containing the certificate and private key.

secret.yaml defines Secret named my-site-tls in the same namespace as the Ingress:

apiVersion: v1
kind: Secret
metadata:
  name: my-site-tls
  namespace: default
type: kubernetes.io/tls
data:
  tls.crt: <base64-encoded-certificate>
  tls.key: <base64-encoded-private-key>

Encode the certificate and private key with Base64:

cat example.com.crt | base64 | tr -d '\n'
cat example.com.key | base64 | tr -d '\n'

Replace the placeholders with the corresponding encoded values.

How Does the Ingress Configuration Work?

Creating an Ingress resource alone does not implement routing. An Ingress Controller watches Ingress resources and configures the underlying proxy to handle incoming traffic.

For this example, we use the NGINX Ingress Controller.

  • Minikube: Enable the NGINX Ingress Controller addon with minikube addons enable ingress. It runs in the ingress-nginx namespace and typically configures itself as the default IngressClass.
  • Real Kubernetes clusters: Install an Ingress Controller by following the NGINX Ingress Controller documentation. Minikube addons are intended for local development, not production cluster installation.

The controller reads the Ingress rules and TLS Secret, then configures NGINX to terminate TLS and route requests to the appropriate Services.

Inspect the generated NGINX configuration

You can inspect the configuration generated by the controller using:

kubectl exec -n ingress-nginx \
  deploy/ingress-nginx-controller -- \
  cat /etc/nginx/nginx.conf

This command executes cat /etc/nginx/nginx.conf inside the controller Pod managed by the ingress-nginx-controller Deployment. You should see TLS settings and host based routing for main.example.com and docs.example.com

You will find the generated server blocks, hostname-based routing rules, TLS configuration, and upstream definitions that NGINX uses to forward requests to backend Services.

Note

The inspection is possible because at the end of the day, Controllers are just Pods or containers.

Accessing Application

curl -k https://main.example.com
<h1 text-align="center">I AM SVC1 HOME!</h1>
curl -k https://docs.example.com
<h1 text-align="center">I AM SVC2 DOCS!</h1>

Important

The -k flag in curl tells curl to skip SSL/TLS certificate verification when making HTTPS requests. It’s shorthand for --insecure.

Normally, curl verifies that the server’s TLS certificate is valid and trusted. But because we are performing demonstration that uses self-signed certificate which curl doesn’t trust, so -k is used.

Last updated on