Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

Secure an Ingress with TLS

Learn to secure an Ingress with TLS in Kubernetes: generate a certificate, create a TLS secret, and configure the Ingress resource for HTTPS traffic. Hands-on steps, edge cases, and next lesson included.

Focus: secure an ingress with tls

Sponsored

You have deployed your application on Kubernetes, and the Ingress resource is routing traffic to it—but only over plain HTTP. In production, unencrypted traffic is a security risk. Attackers can intercept sensitive data. This lesson shows you how to secure an ingress with TLS, transforming your HTTP endpoint into an HTTPS one using Kubernetes-native secrets and standard TLS certificates.

The problem this lesson solves

By default, an Ingress resource routes HTTP traffic. Anyone on the network path can read the request body, headers, and cookies. If your application handles login credentials, payment details, or any private data, sending it over HTTP is unacceptable.

TLS (Transport Layer Security) encrypts the traffic between the client and your Ingress controller. The controller terminates the TLS connection and forwards decrypted traffic to your backend services. This way, the backend pods never need to handle certificates directly.

The challenge: where do you store the certificate and private key? Kubernetes secrets. How do you tell the Ingress to use them? The tls field in the Ingress spec. Let's build that mental model.

Core concept / mental model

Think of TLS termination as a gateway guard that decrypts incoming HTTPS requests before they enter your cluster.

  • Certificate: a digital file that proves the identity of your service (e.g., example.com). It includes a public key.
  • Private key: the matching secret half—never share this.
  • Secret: a Kubernetes resource that holds sensitive data (here, both certificate and private key).
  • Ingress controller: the component that runs in your cluster and actually terminates TLS. It reads the tls field in the Ingress resource, fetches the secret, and uses its contents to perform HTTPS handshakes.

Pro tip: The Ingress resource is declarative—you say "I want TLS for this host," and the controller makes it happen. You don't manage TLS listeners manually.

Key definitions

  • TLS secret: a kubernetes.io/tls type secret containing tls.crt (certificate) and tls.key (private key).
  • Ingress tls spec: an array of objects inside the Ingress YAML, each with hosts and secretName fields.

How it works step by step

  1. Obtain a TLS certificate – You can generate a self-signed certificate for testing, or purchase / get a free one from Let's Encrypt for production.

  2. Create a Kubernetes secret of type kubernetes.io/tls – The secret stores the certificate and private key in base64-encoded form. You can create it with kubectl create secret tls or from a YAML manifest.

  3. Edit the Ingress resource to add a tls block – You specify one or more hostnames and the name of the secret.

  4. The Ingress controller reads the secret – On apply, the controller verifies the secret type and loads the certificate. If the secret is missing or malformed, the controller logs an error and falls back to HTTP.

  5. Traffic flows over HTTPS – Clients connect on port 443, the controller performs the TLS handshake using the loaded certificate, and proceeds with routing.

Hands-on walkthrough

We'll use a self-signed certificate for testing. In production, use a proper CA (e.g., Let's Encrypt with cert-manager, covered in a later lesson).

Step 1: Generate a self-signed certificate

# Create a private key and a self-signed certificate for example.com
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout tls.key \
    -out tls.crt \
    -subj "/CN=example.com/O=example"

This creates two files: tls.crt (certificate) and tls.key (private key).

Step 2: Create the TLS secret in Kubernetes

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

Verify:

kubectl get secret example-tls -o yaml

You should see type: kubernetes.io/tls and two data fields (tls.crt, tls.key).

Step 3: Update the Ingress resource

Assume you already have an Ingress for example.com. Add a tls block:

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

Apply:

kubectl apply -f ingress-tls.yaml

Step 4: Test HTTPS access

# If using a local cluster with a known IP, adjust the host header
curl -k https://example.com --resolve example.com:443:<INGRESS_IP>

Expected output: Your application responds (if the service is healthy). The -k flag skips certificate verification (self-signed). Remove -k once you have a real certificate.

Compare options / when to choose what

Approach Use case Complexity Certificate management
Self-signed certificate Development / testing only Low Manual expiration tracking
CA-issued certificate (commercial) Production with fixed hostnames Medium Manual renewal (yearly)
Let's Encrypt + cert-manager Production automation High (initial setup) Automatic renewal

Recommendation: - For local testing, self-signed is fine. - For production, use either a paid CA or Let's Encrypt. cert-manager automates the entire lifecycle—create the issuer once, and it handles certificates for any Ingress annotated correctly.

Troubleshooting & edge cases

Symptom: HTTPS returns "connection refused" or TLS handshake error

Possible causes: - The secret does not exist in the same namespace as the Ingress. - The secret is of the wrong type (e.g., Opaque instead of kubernetes.io/tls). - The Ingress controller is not running or does not support the tls field (e.g., older nginx-ingress versions).

Fix:

kubectl get secret example-tls -o jsonpath='{.type}'
# Should output: kubernetes.io/tls

kubectl describe ingress example-ingress
# Look under the 'Events' section for errors

Symptom: Certificate warnings in browser

Cause: Self-signed certificate or certificate does not match the hostname (e.g., issued for example.com but accessed via app.example.com).

Fix: Ensure the Common Name (CN) or Subject Alternative Name (SAN) matches the hostname. For modern browsers, SAN is required. Regenerate with -addext "subjectAltName=DNS:example.com".

Edge case: Multiple TLS certificates for different hosts

The tls block is an array. Add multiple entries:

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

What you learned & what's next

You now understand how to secure an ingress with TLS: generate a certificate, store it as a kubernetes.io/tls secret, and reference it in the Ingress resource. The entire process is declarative—once applied, the Ingress controller handles termination.

You have met these learning objectives: - Explain the core idea behind Secure an Ingress with TLS - Complete a practical exercise for Secure an Ingress with TLS

Next lesson: Automate certificate renewal with cert-manager. You'll replace manual secret creation with automatic TLS certificate provisioning.

Practice recap

Create a new deployment and service for a simple app (e.g., nginx). Write an Ingress YAML that routes traffic to it on both HTTP and HTTPS with a self-signed certificate. Test with curl -k. Then delete the secret and observe the Ingress controller's error in its logs — you'll learn how to debug TLS issues.

Common mistakes

  • Creating the secret in the wrong namespace (kubectl defaults to 'default'; move it to the same namespace as your Ingress).
  • Using a secret of type 'Opaque' instead of 'kubernetes.io/tls' — the controller will ignore it.
  • Forgetting to re-apply the Ingress after adding the tls block — changes are not live until you run kubectl apply.
  • Generating a certificate with mismatched common name or missing SAN — browsers will reject it.

Variations

  1. Instead of kubectl create secret tls, you can craft the secret YAML manually and encode the cert/key with base64.
  2. For multiple ingresses sharing the same certificate, reuse the same secret across all Ingress resources.
  3. For wildcard certificates (e.g., *.example.com), set the host to a specific subdomain or use the wildcard domain in the tls hosts list.

Real-world use cases

  • Production microservice API behind HTTPS — every external-facing Ingress must have a valid TLS certificate signed by a public CA.
  • Staging environment using self-signed certificates for internal testing, with the same Ingress spec as production.
  • Multi-tenant SaaS platform with separate TLS certificates for each customer subdomain (e.g., customer1.app.com, customer2.app.com).

Key takeaways

  • TLS termination occurs at the Ingress controller, not at the pod — certificates live in the cluster, not app images.
  • A valid TLS secret must be of type 'kubernetes.io/tls' with keys 'tls.crt' and 'tls.key'.
  • The tls field in the Ingress spec is an array of hosts and secretName pairs — one per certificate.
  • Self-signed certificates are fine for development; production requires a proper CA or cert-manager automation.
  • Always verify the secret exists in the same namespace as the Ingress — cross-namespace references are not allowed by default.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.