loading...
Cover image for Kubernetes - Redirect HTTP to HTTPS with ELB and the nginx ingress controller

Kubernetes - Redirect HTTP to HTTPS with ELB and the nginx ingress controller

tomhoule profile image Tom Houlé ・3 min read

TL;DR Read the last paragraph for the most recent way of implementing HTTPS with an nginx ingress controller while letting ELB handle the certificates.

In our exploratory work with Kubernetes, one of the first hurdles was configuring a proper HTTP ingress. At first, we wanted three properties from our setup:

All unencrypted http traffic is immediately redirected to https.
The ingress controller does not handle TLS certificates. instead we have TLS termination at the ELB level.
The redirection is handled by the ingress controller, not the individual ingress resources, since we always want HTTP traffic to be encrypted, and it limits the potential for errors.

It turns out that combining those three was more complicated than expected, since ELB does not do HTTP redirects, and neither træfik nor nginx ingress controllers supported redirecting unless they handled TLS termination themselves.

Our solution

The DNS (Route 53) and ELB configurations were the simplest.

We first created a LoadBalancer service for our ingress:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: kube-system
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: <the arn for our wildcard TLS cert>
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https
spec:
  selector:
    k8s-app: nginx-ingress-lb
  type: LoadBalancer
  ports:
  - name: http
    port: 80
    targetPort: 80
  - name: https
    port: 443
    targetPort: 80

When applied, the service will create the ELB load balancer for you.

Our DNS configuration is very simple: a “gateway” domain that points to the load balancer. The other subdomains are just CNAME records that point to it. The “gateway” domain is updated manually as a part of the cluster creation, and we just add the CNAME records when needed. We plan on trying out external-dns for the “gateway” domain to automate this in the future.

The last part was less ideal, since the nginx ingress controller from kubernetes (there is another implementation by the nginx people) did not support redirecting to https unless it is configured to handle the https traffic itself (which we really don't want).

Fortunately, the controller lets you completely override its nginx config file, so we just copied the default file from the source, which is of course not ideal, and added what we wanted, which is a 301 redirect based on the X-Forwarded-Proto header set by ELB:

if ( $http_x_forwarded_proto = http ) {
  rewrite ^(.*) https://$host$1 permanent;
}

We can then just put the modified config template in a ConfigMap (in our case nginx-config in the kube-system namespace) and pass it to nginx by placing this in the container section of the Deployment:

args:
  - /nginx-ingress-controller
  - --default-backend-service=kube-system/default-http-backend
  - --publish-service=kube-system/nginx-service
  - --configmap=kube-system/nginx-config

How to do it today

This story is a testimony to how fast things are moving in the k8s ecosystem. We came up with this solution on the nginx ingress controller version 0.9.0-beta.1, exactly two weeks ago. Now the current version is 0.9.0-beta.3 and the ingress.kubernetes.io/force-ssl-redirect annotation was added, making the same configuration achievable by adding it in each ingress resource.

This option ticks 2 of our 3 boxes, and we found that the last point, having this setting centralized, was not important enough to justify the hack.

Hi there, we're store2be, a Berlin based startup that builds a SaaS enabled marketplace for short term retails space. If you like what we are posting you might wanna check out the store2be tech page or follow our Medium channel.

Discussion

pic
Editor guide
Collapse
tprakash17 profile image
DevOps Engineer

I got the point of putting following into ConfigMap

if ( $http_x_forwarded_proto = http ) {
rewrite .* https://$host$1 permanent;
}

Can you please paste configmap yaml just want to see the syntax how is above put in there.

Also, listeners are http and https at ELB? Not TCP right

Collapse
tprakash17 profile image
DevOps Engineer

Got this working.
Changed listeners on ELB to HTTP and HTTPS and following annotations on ingress seems to be working. SSL certificate still terminating on ELB.

Ingress annotation
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"

ELB listener setting.

Collapse
fwynyk profile image
fwynyk

Hey, nice article. But what about the ingress/controller yaml files?

Collapse
rastut profile image
Carlos Lopez

Ey we have a similar configuration but running over tcp. If we try to use as default backen http we always get a 504 error, the request die on the ELB. Any idea?

Collapse
tomhoule profile image
Tom Houlé Author

We haven't tried configuring ELB for TCP so I don't have interesting insight, but as a first step I'd try to tweak the LoadBalancer configuration. Sorry :/