Kubernetes - Redirect HTTP to HTTPS with ELB and the nginx ingress controller
Tom Houlé
Posted on April 4, 2017
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.
Posted on April 4, 2017
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
April 4, 2017