Nginx Sidecar for TLS/SSL Termination on Kubernetes
If you’re not ready for a service mesh like Istio, Cillium or Nginx’s own service mesh, an easy way to implement end-to-end encryption from the application LB to your pod or TLS/SSL termination for your pods behind a network LB is a proxy sidecar using Nginx. I’m going to describe how to set this up using cert-manager to fetch a letsencrypt certificate and mount that to the Nginx base image.
Cert-manager will need to be setup before the next steps and that can be seen here.
Snippet from a deployment manifest for the nginx proxy container that is mounting the config and certificate keypair. Just need to make sure the secret created from the ingress manifest is the same name referenced in the volume.
deployment.yaml
1...
2containers:
3 - name: nginx-proxy
4 image: public.ecr.aws/nginx/nginx:1.23
5 ports:
6 - name: https
7 containerPort: 443
8 volumeMounts:
9 - name: nginx-config
10 mountPath: /etc/nginx/conf.d
11 readOnly: true
12 - name: certs
13 mountPath: /certs
14 readOnly: true
15...
16volumes:
17 - configMap:
18 name: nginx
19 name: nginx-config
20 - name: certs
21 secret:
22 secretName: demo-app-cert
This example ingress manifest is using the AWS LB controller, but the parts to take note of are the TLS section and the cert-manager annotation that will tell cert-manager to generate a certificate and secret with the keypair that will be mounted in the Nginx sidecar.
ingress.yaml
1apiVersion: networking.k8s.io/v1
2kind: Ingress
3metadata:
4 name: demo-app
5 namespace: sandbox
6 labels:
7 helm.sh/chart: demo-app-0.1.0
8 app.kubernetes.io/version: "1.0.0"
9 app.kubernetes.io/managed-by: Helm
10 app.kubernetes.io/name: demo-app
11 annotations:
12 cert-manager.io/cluster-issuer: letsencrypt-staging
13 alb.ingress.kubernetes.io/backend-protocol: HTTPS
14 alb.ingress.kubernetes.io/group.name: sandbox-app-external
15 alb.ingress.kubernetes.io/healthcheck-port: "443"
16 alb.ingress.kubernetes.io/healthcheck-protocol: HTTPS
17 alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
18 alb.ingress.kubernetes.io/load-balancer-name: sandbox-app-external
19 alb.ingress.kubernetes.io/scheme: internet-facing
20 alb.ingress.kubernetes.io/target-type: ip
21spec:
22 ingressClassName: alb
23 rules:
24 - host: demo.sandbox.example.com
25 http:
26 paths:
27 - path: "/"
28 pathType: Prefix
29 backend:
30 service:
31 name: demo-app
32 port:
33 number: 443
34 tls:
35 - hosts:
36 - demo.sandbox.example.com
37 secretName: demo-app-cert
This configmap is our Nginx config file that will proxy traffic to the app listening on port 8000.
1apiVersion: v1
2kind: ConfigMap
3metadata:
4 name: nginx
5data:
6 image-app.conf: |
7 server {
8 listen 443 ssl http2;
9 server_name demo.sandbox.example.com;
10 ssl_certificate /certs/tls.crt;
11 ssl_certificate_key /certs/tls.key;
12 ssl_session_cache shared:SSL:10m;
13 ssl_session_timeout 1h;
14 ssl_buffer_size 8k;
15
16 location / {
17 proxy_pass http://0.0.0.0:8000;
18 proxy_set_header Host $host;
19 proxy_set_header X-Real-IP $remote_addr;
20 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
21 proxy_set_header X-Forwarded-Host $server_name;
22 proxy_set_header Upgrade $http_upgrade;
23 proxy_set_header Connection 'upgrade';
24 proxy_cache_bypass $http_upgrade;
25 }
26 }