Getting HTTPS working with Traefik and GCE Ingress

11/2/2018

I'm after a very simple requirement - yet seems impossible to make Traefik redirect traffic from HTTP to HTTPS when behind an external load balancer.

This is my GCE ingress

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  namespace: platform
  name: public-ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: "kubernetes-cluster-dev-ip"
    kubernetes.io/ingress.class: "gce"
    ingress.gcp.kubernetes.io/pre-shared-cert: "application-dev-ssl,application-dev-graphql-ssl"
spec:
  backend:
    serviceName: traefik-ingress-service
    servicePort: 80

Which receive traffic from HTTP(S) then forward to Traefik to port 80.

I initially tried to using Traefik way of redirecting matching the schema with this configuration:

[entryPoints]
  [entryPoints.http]
    address = ":80"
    compress = true
    [entryPoints.http.redirect]
    entryPoint = "https"
  [entryPoints.https]
    address = ":443"
    compress = true
    [entryPoints.https.tls] 

But obviously gets into an infinite redirect loop because of the load balancer always proxy traffic to Traefik port 80.

The simple solution to make this work is exactly what GCE suggests https://github.com/kubernetes/ingress-gce#ingress-cannot-redirect-http-to-https

Being able to check for the http_x_forwarded_proto header and redirect based on that.

Nginx equivalent

# Replace '_' with your hostname.
server_name _;
if ($http_x_forwarded_proto = "http") {
    return 301 https://$host$request_uri;
}

Can someone advice what's the best way of handling this with Traefik, please!

-- Fabrizio Fenoglio
google-kubernetes-engine
https
kubernetes
traefik
traefik-ingress

1 Answer

11/2/2018

To recap, you have a GCE L7 (Layer 7) load balancer proxying another L7 load balancer in Traefik that you can potentially use it to proxy to another backend service. So looks like you have something like this:

GCE L7 LB HTTP 80
=> Forwarded to Traefik HTTP 80
=> Redirect initial request to HTTPS 443 
=> The client thinks it needs to talk to GCE L7 LB HTTPS 443
=> GCE L7 LB HTTP 443
=> Forwarded to Traefik HTTP 80
=> Infinite loop

and you need to have something like this:

GCE L7 LB HTTP 80
=> Forwarded to Traefik HTTP 80
=> Redirect initial request to HTTPS 443 
=> The client thinks it needs to talk to GCE L7 LB HTTPS 443
=> GCE L7 LB HTTP 443
=> Forwarded to Traefik HTTP 443

It's not documented anywhere if Traefik redirects to HTTPS based on the value of http_x_forwarded_proto being http, but that would be the general assumption. In any case, the Ingress doesn't know anything about an HTTPS backend (you didn't specify how you configured the HTTPS GCE LB endpoint).

You can see that it's documented here how to make the GCE LB directly create an HTTPS endpoint that forward directly to your HTTPS backend. Basically, you can try adding the service.alpha.kubernetes.io/app-protocols annotation to the HTTPS Traefik service:

apiVersion: v1
kind: Service
metadata:
  name: traefik-https
  annotations:
      service.alpha.kubernetes.io/app-protocols: '{"my-https-port":"HTTPS"}'
  labels:
    app: echo
spec:
  type: NodePort
  ports:
  - port: 443
    protocol: TCP
    name: my-https-port
  selector:
    app: traefik

So you would have something like this:

GCE L7 LB HTTP 80
=> Forwarded to Traefik HTTP 80
=> Redirect initial request to HTTPS 443 
=> The client thinks it needs to talk to GCE L7 LB HTTPS 443
=> GCE L7 LB HTTP 443
=> Forwarded to Traefik HTTPS service
=> Service forward to Traefik port 443

Hope this helps.

-- Rico
Source: StackOverflow