nginx-ingress: Too many redirects when force-ssl is enabled

Jonny picture Jonny · Apr 16, 2018 · Viewed 8.5k times · Source

I am setting up my first ingress in kubernetes using nginx-ingress. I set up the ingress-nginx load balancer service like so:

{
  "kind": "Service",
  "apiVersion": "v1",
  "metadata": {
    "name": "ingress-nginx",
    "namespace": "...",
    "labels": {
      "k8s-addon": "ingress-nginx.addons.k8s.io"
    },
    "annotations": {     
      "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp",
      "service.beta.kubernetes.io/aws-load-balancer-proxy-protocol": "*",
      "service.beta.kubernetes.io/aws-load-balancer-ssl-cert": "arn....",
      "service.beta.kubernetes.io/aws-load-balancer-ssl-ports": "443"
    }
  },
  "spec": {
    "ports": [
      {
        "name": "http",
        "protocol": "TCP",
        "port": 80,
        "targetPort": "http",
        "nodePort": 30591
      },
      {
        "name": "https",
        "protocol": "TCP",
        "port": 443,
        "targetPort": "http",
        "nodePort": 32564
      }
    ],
    "selector": {
      "app": "ingress-nginx"
    },
    "clusterIP": "...",
    "type": "LoadBalancer",
    "sessionAffinity": "None",
    "externalTrafficPolicy": "Cluster"
  },
  "status": {
    "loadBalancer": {
      "ingress": [
        {
          "hostname": "blablala.elb.amazonaws.com"
        }
      ]
    }
  }
}

Notice how the https port has its targetPort property pointing to port 80 (http) in order to terminate ssl at the load balancer.

My ingress looks something like this:

apiVersion: extensions/v1beta1
kind: Ingress
metadata: 
  name: something
  namespace: ...
  annotations:
    ingress.kubernetes.io/ingress.class: "nginx"
    ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  rules:
    - host: www.exapmle.com
      http:
        paths:
         - path: /
           backend:
            serviceName: some-service
            servicePort: 2100

Now when I navigate to the url I get a Too many redirects error. Something that is confusing me is that when I add the following header "X-Forwarded-Proto: https" I get the expected response (curl https://www.example.com -v -H "X-Forwarded-Proto: https").

Any ideas how I can resolve the issue?

P.S. this works just fine with ingress.kubernetes.io/force-ssl-redirect: "false" and it doesn't seem that there are any extraneous redirects.

Answer

Anton Kostenko picture Anton Kostenko · Apr 16, 2018

That is a known issue with the annotation for SSL-redirection in combination with proxy-protocol and termination of SSL connections on ELB.

Question about it was published on GitHub and here is a fix from that thread:

  1. You should create a custom ConfigMap for an Nginx-Ingress instead of using force-ssl-redirect annotation like the following:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      labels:
        app: ingress-nginx
      name: nginx-ingress-configuration
      namespace: <ingress-namespace>
    data:
      ssl-redirect: "false"
      hsts: "true"
      server-tokens: "false"
      http-snippet: |
        server {
          listen 8080 proxy_protocol;
          server_tokens off;
          return 301 https://$host$request_uri;
        }
    

    That configuration will create an additional listener with a simple redirection to https.

  2. Then, apply that ConfigMap to your ingress-controller, add NodePort 8080 to its container definition and to the Service.
  3. Now, you can point the port 80 of your ELB to port 8080 of the Service.

With that additional listener, it will work.