TLS

Transport Layer Security (TLS) in Kubernetes Ingress provides encrypted communication between clients and your Services. Ingress Controllers handle TLS termination—they receive encrypted HTTPS traffic, decrypt it, and forward it as HTTP to backend Services. This allows you to secure external traffic while keeping internal communication simple.

What is TLS Termination?

TLS termination means the Ingress Controller handles SSL/TLS encryption/decryption. The flow works like this:

  1. Client sends HTTPS request (encrypted)
  2. Ingress Controller receives and decrypts the request
  3. Ingress Controller forwards HTTP request (unencrypted) to backend Service
  4. Backend Service processes the request
graph LR A[Client] -->|HTTPS Encrypted| B[Ingress Controller] B -->|Decrypts| C[TLS Termination] C -->|HTTP Unencrypted| D[Backend Service] D -->|HTTP Response| C C -->|Encrypts| B B -->|HTTPS Encrypted| A style A fill:#e1f5ff style B fill:#e8f5e9 style C fill:#fff4e1 style D fill:#fff4e1

TLS Configuration in Ingress

Configure TLS in Ingress resources using the tls section:

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

Key components:

  • hosts - Hostnames that should use TLS
  • secretName - Kubernetes Secret containing certificate and key

Creating TLS Secrets

TLS certificates are stored in Kubernetes Secrets:

Using kubectl

kubectl create secret tls example-tls \
  --cert=path/to/cert.crt \
  --key=path/to/key.key

Using YAML

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

Note: The secret type must be kubernetes.io/tls, and the keys must be named tls.crt and tls.key.

TLS Secret Structure

A TLS secret contains:

apiVersion: v1
kind: Secret
metadata:
  name: example-tls
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTi...  # Base64-encoded certificate
  tls.key: LS0tLS1CRUdJTi...  # Base64-encoded private key

Fields:

  • tls.crt - Certificate (PEM encoded, base64)
  • tls.key - Private key (PEM encoded, base64)

Multiple TLS Hosts

You can configure multiple TLS hosts in one Ingress:

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

Each host can use a different certificate.

Wildcard Certificates

Use wildcard certificates for multiple subdomains:

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

Note: Wildcard certificates cover subdomains but not the root domain unless explicitly included.

Automatic Certificate Management

Use cert-manager for automatic certificate provisioning and renewal:

Installing cert-manager

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml

Using cert-manager with Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: auto-tls-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    secretName: example-tls  # cert-manager creates this
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

cert-manager will:

  1. Create the TLS secret automatically
  2. Obtain certificate from Let’s Encrypt
  3. Renew certificate before expiration
graph LR A[Ingress Created] --> B[cert-manager Watches] B --> C[Creates Certificate Request] C --> D[Let's Encrypt Challenge] D --> E[Certificate Issued] E --> F[TLS Secret Created] F --> G[Ingress Uses Certificate] style A fill:#e1f5ff style B fill:#e8f5e9 style F fill:#fff4e1 style G fill:#fff4e1

HTTP to HTTPS Redirect

Most Ingress Controllers support automatic HTTP to HTTPS redirect:

NGINX Ingress Controller

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

Annotations:

  • ssl-redirect: "true" - Redirects HTTP to HTTPS (if TLS is configured)
  • force-ssl-redirect: "true" - Always redirects, even without TLS config

TLS Versions and Ciphers

Configure TLS versions and cipher suites via controller annotations:

NGINX Ingress Controller

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: secure-tls-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.2 TLSv1.3"
    nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-RSA-AES128-GCM-SHA256,ECDHE-RSA-AES256-GCM-SHA384"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    secretName: example-tls
  rules:
  # ...

Best practices:

  • Use TLS 1.2 or higher
  • Disable weak ciphers
  • Prefer ECDHE ciphers for forward secrecy

Certificate Chain

For proper TLS validation, include the full certificate chain:

# Combine certificate and chain
cat server.crt intermediate.crt root.crt > fullchain.crt

# Create secret with full chain
kubectl create secret tls example-tls \
  --cert=fullchain.crt \
  --key=server.key

The certificate file should contain:

  1. Server certificate
  2. Intermediate certificate(s)
  3. Root certificate (optional, usually not needed)

Best Practices

  1. Use cert-manager - Automate certificate provisioning and renewal
  2. Use Let’s Encrypt - Free, automated certificates for public domains
  3. Enable HTTPS redirect - Always redirect HTTP to HTTPS
  4. Use strong TLS versions - TLS 1.2 or higher
  5. Rotate certificates - Regularly rotate certificates
  6. Monitor expiration - Set up alerts for certificate expiration
  7. Use wildcards wisely - Wildcard certs simplify multi-subdomain setups
  8. Store secrets securely - Use Kubernetes Secrets with proper RBAC
  9. Test TLS configuration - Verify certificates work correctly
  10. Document certificate sources - Keep track of where certificates come from

Troubleshooting

TLS Not Working

  1. Check secret exists: kubectl get secret <secret-name>
  2. Verify secret type: Should be kubernetes.io/tls
  3. Check secret data: kubectl get secret <secret-name> -o yaml
  4. Verify certificate format: Should be PEM encoded
  5. Test certificate: openssl x509 -in cert.crt -text -noout

Certificate Errors

  1. Check expiration: kubectl get secret <secret-name> -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates
  2. Verify hostname match: Certificate must match Ingress hostname
  3. Check certificate chain: Ensure full chain is included
  4. Verify private key: Ensure key matches certificate
  5. Test with openssl: openssl s_client -connect <host>:443 -servername <host>

cert-manager Issues

  1. Check cert-manager pods: kubectl get pods -n cert-manager
  2. Review certificate status: kubectl describe certificate <cert-name>
  3. Check ClusterIssuer: kubectl get clusterissuer
  4. Review events: kubectl get events -n cert-manager
  5. Check logs: kubectl logs -n cert-manager <cert-manager-pod>

HTTPS Redirect Not Working

  1. Check annotations: Verify redirect annotations are set
  2. Verify controller support: Ensure controller supports redirect
  3. Test HTTP access: curl -I http://example.com
  4. Check controller logs: Review for redirect configuration
  5. Verify TLS config: Ensure TLS is properly configured

See Also