Cloud Native 12 min read

Secure Your Kubernetes Ingress-Nginx with mTLS and HTTPS: Step‑by‑Step Guide

Learn how to configure Ingress‑Nginx in a running Kubernetes cluster for secure mTLS and HTTPS communication, covering prerequisites, certificate creation, deployment of HTTP and mTLS services, Ingress rules, SSL passthrough setup, and verification steps with practical kubectl and OpenSSL commands.

Linux Ops Smart Journey
Linux Ops Smart Journey
Linux Ops Smart Journey
Secure Your Kubernetes Ingress-Nginx with mTLS and HTTPS: Step‑by‑Step Guide
Ingress-Nginx overview
Ingress-Nginx overview

Preparation

Running Kubernetes cluster.

Ingress‑Nginx deployed.

At least one domain name that resolves to the Ingress‑Nginx service.

Environment Description

Business containers are accessed via Ingress rather than being exposed directly. Two TLS/HTTPS scenarios exist: (1) Backend service uses HTTP – Ingress terminates TLS/HTTPS and forwards HTTP to the backend. (2) Backend service uses HTTPS – Ingress passes TLS/HTTPS through to the backend.

Tip: When the backend service uses HTTPS, it usually requires mTLS. This article only demonstrates the HTTP‑backend case and the mTLS case.

Backend Service HTTP Protocol

1. Create a simple HTTP application

<code>$ cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: simple
  template:
    metadata:
      labels:
        app: simple
    spec:
      containers:
      - name: nginx
        args:
        - -p=1234
        image: registry.cn-guangzhou.aliyuncs.com/jiaxzeng6918/simple:v2.0
        imagePullPolicy: IfNotPresent
EOF</code>

2. Create the Service for the simple app

<code>$ cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: simple
  namespace: default
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 1234
  selector:
    app: simple
  type: ClusterIP
EOF</code>

3. Verify the simple service

<code>$ curl $(kubectl get svc simple -ojsonpath='{.spec.clusterIP}:{.spec.ports[0].port}')/who/hostname
simple-7b844c74dc-ptv4m

$ curl $(kubectl get svc simple -ojsonpath='{.spec.clusterIP}:{.spec.ports[0].port}')/who/hostname
simple-7b844c74dc-fnj2n</code>

Backend Service HTTPS Protocol (mTLS)

1. Create certificates

<code># Self‑signed CA certificate
$ openssl req -newkey rsa:2048 -x509 -nodes -keyout ca.key -out ca.crt -days 3650 -subj "/CN=mTLS ca"

# Service certificate
$ openssl genrsa -out server.key 2048
$ openssl req -new -key server.key -out server.csr -subj "/CN=server simple"
$ openssl x509 -req -CA ca.crt -CAkey ca.key -in server.csr -out server.crt -CAcreateserial -days 365 -extfile <(printf "subjectAltName=IP:127.0.0.1,DNS:simple.jiaxzeng.com")

# Store CA and service certificates in a secret
$ kubectl create secret generic simple-mtls-certs --from-file=ca.crt=ca.crt --from-file=tls.crt=server.crt --from-file=tls.key=server.key</code>

Tip: The certificate must include a

subjectAltName

that contains the domain name; otherwise Ingress will fall back to the default SSL certificate.

2. Deploy the mTLS‑enabled simple application

<code>$ cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-mtls
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: simple-mtls
  template:
    metadata:
      labels:
        app: simple-mtls
    spec:
      containers:
      - name: simple-mtls
        image: registry.cn-guangzhou.aliyuncs.com/jiaxzeng6918/simple:v2.0
        imagePullPolicy: IfNotPresent
        args:
        - --mtls
        - --cacert=/opt/ca.crt
        - --cert=/opt/tls.crt
        - --key=/opt/tls.key
        volumeMounts:
        - name: mtls
          mountPath: /opt/
      volumes:
      - name: mtls
        secret:
          secretName: simple-mtls-certs
EOF</code>

3. Create the Service for the mTLS app

<code>$ cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: simple-mtls
  namespace: default
spec:
  ports:
  - name: https
    port: 443
    protocol: TCP
    targetPort: 1234
  selector:
    app: simple-mtls
  type: ClusterIP
EOF</code>

4. Verify the mTLS service

<code># Generate a client certificate
$ openssl genrsa -out client.key 2048
$ openssl req -new -key client.key -out client.csr -subj "/CN=client simple"
$ openssl x509 -req -CA ca.crt -CAkey ca.key -in client.csr -out client.crt -CAcreateserial -days 365

# Access without a client certificate (expected to fail)
$ curl -k https://$(kubectl get svc simple-mtls -ojsonpath='{.spec.clusterIP}:{.spec.ports[0].port}')/who/hostname
curl: (58) NSS: client certificate not found (nickname not specified)

# Access with the client certificate
$ curl -k --cert ./client.crt --key ./client.key https://$(kubectl get svc simple-mtls -ojsonpath='{.spec.clusterIP}:{.spec.ports[0].port}')/who/hostname
simple-mtls-5b7f54bbbc-fhwk9</code>

Enable SSL Passthrough for mTLS Backend

1. Enable SSL passthrough in the ingress‑nginx controller

<code># Helm installation – add two extra arguments
controller:
  extraArgs:
    enable-ssl-passthrough: "true"

$ helm -n kube-system upgrade ingress-nginx -f /etc/kubernetes/addons/ingress-nginx-value.yml /etc/kubernetes/addons/ingress-nginx

# Manifest‑based installation – edit the deployment args
$ kubectl -n kube-system edit deploy ingress-nginx-controller
# Add the following argument to spec.template.spec.containers.args
- --enable-ssl-passthrough=true</code>

2. Configure Ingress for the HTTPS (mTLS) backend

<code>$ cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
  name: simple-mtls
  namespace: default
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - mtls.jiaxzeng.com
    secretName: simple-mtls-certs
  rules:
  - host: mtls.jiaxzeng.com
    http:
      paths:
      - backend:
          service:
            name: simple-mtls
            port:
              number: 443
        path: /
        pathType: Prefix
EOF</code>

3. Verify via Ingress using OpenSSL

<code>$ openssl s_client -connect 172.139.20.100:443 -cert ./client.crt -key ./client.key -CAfile ./ca.crt -servername mtls.jiaxzeng.com
... (full handshake output omitted for brevity) ...
GET /who/hostname HTTP/1.1
Host: mtls.jiaxzeng.com
Connection: close

HTTP/1.1 200 OK
... (headers) ...
simple-mtls-5b7f54bbbc-rw2lv</code>

Configure Ingress Access (HTTP Backend)

1. Ingress for the simple HTTP application

<code>$ cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple
  namespace: default
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - simple.jiaxzeng.com
  rules:
  - host: simple.jiaxzeng.com
    http:
      paths:
      - backend:
          service:
            name: simple
            port:
              number: 80
        path: /
        pathType: Prefix
EOF</code>

2. Verify the HTTP Ingress

<code>$ curl -k -H "Host: simple.jiaxzeng.com" https://172.139.20.100/who/hostname
simple-7b844c74dc-ptv4m
$ curl -k -H "Host: simple.jiaxzeng.com" https://172.139.20.100/who/hostname
simple-7b844c74dc-fnj2n</code>

Conclusion

By following these steps, TLS/HTTPS encryption is enabled for services running on Kubernetes, improving data‑in‑transit security and enhancing user trust.

TLS handshake result
TLS handshake result
cloud-nativekubernetesTLSHTTPSmTLSIngress-Nginx
Linux Ops Smart Journey
Written by

Linux Ops Smart Journey

The operations journey never stops—pursuing excellence endlessly.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.