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:
- The client sends an HTTPS request to either hostname.
- The Ingress Controller uses the TLS certificate from my-site-tls to terminate TLS.
- It routes the decrypted HTTP request according to the hostname:
main.example.com→ main-svc:8080 → Main App Podsdocs.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.yamlmain.Dockerfile: Builds an NGINX image that serves the main site’shtml/index.html.docs.Dockerfile: Builds an NGINX image that serves the documentation site’sdocs/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 formain.example.comanddocs.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/anddocs/: 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.crtIn 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: 8080Note
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/hostsBefore 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 theingress-nginxnamespace 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.confThis 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.