Nginx Ingress - Redirect WWW to non-WWW on Minikube vs GKE

Viewed 549

I know this is topic that comes up every once in a while. I did read many of the existing posts and answers, bit wasn't able to figure it out.

What I need is nginx-ingress to redirect from www.foo.bar to just foo.bar. I set up a test environment on Minikube by installing the nginx ingress the minikube way: minikube addons enable ingress and put the following ingress manifest in place:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.allow-http: "false"
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($host = 'www.foo.bar') {
        return 301 https://foo.bar;
      }
    nginx.ingress.kubernetes.io/proxy-body-size: 600m
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.org/client-max-body-size: 600m
  name: foo-ingress
spec:
  rules:
  - host: api.foo.bar
    http:
      paths:
      - backend:
          service:
            name: identity
            port:
              number: 80
        path: /identity
        pathType: ImplementationSpecific
      - backend:
          service:
            name: catalog
            port:
              number: 80
        path: /catalog
        pathType: ImplementationSpecific
      - backend:
          service:
            name: marketing
            port:
              number: 80
        path: /marketing
        pathType: ImplementationSpecific
      - backend:
          service:
            name: marketplace
            port:
              number: 80
        path: /marketplace
        pathType: ImplementationSpecific
      - backend:
          service:
            name: notification
            port:
              number: 80
        path: /notification
        pathType: ImplementationSpecific
      - backend:
          service:
            name: provider
            port:
              number: 80
        path: /provider
        pathType: ImplementationSpecific
  - host: www.foo.bar
    http:
      paths:
      - backend:
          service:
            name: frontend
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  - host: foo.bar
    http:
      paths:
      - backend:
          service:
            name: frontend
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  - host: blog.foo.bar
    http:
      paths:
      - backend:
          service:
            name: wordpress
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - foo.bar
    - api.foo.bar
    - blog.foo.bar
    secretName: foo-cert

And it works:

➜  ~ curl -k -v -o /dev/null https://www.foo.bar
  ...
> GET / HTTP/2
> Host: www.foo.bar
> user-agent: curl/7.68.0
> accept: */*
> 
< HTTP/2 301 
< date: Sat, 27 Mar 2021 16:37:50 GMT
< content-type: text/html
< content-length: 162
< location: https://foo.bar

Now, let me get to the problem: I have a production cluster on GCP (GKE) on which I installed nginx-ingress via Helm:

helm -n nginx-ingress ls
NAME            NAMESPACE       REVISION    UPDATED                                 STATUS      CHART               APP VERSION
nginx-ingress   nginx-ingress   2           2021-03-27 15:05:41.229254628 +0000 UTC deployed    nginx-ingress-0.8.1 1.10.1  

The Ingress manifest I have is (I don't see a relevant difference to the one above):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.allow-http: "false"
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($host = 'www.foo.bar') {
        return 301 https://foo.bar;
      }
    nginx.ingress.kubernetes.io/proxy-body-size: 600m
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.org/client-max-body-size: 600m
  name: foo-ingress
spec:
  rules:
  - host: api.foo.bar
    http:
      paths:
      - backend:
          service:
            name: identity
            port:
              number: 80
        path: /identity
        pathType: ImplementationSpecific
      - backend:
          service:
            name: catalog
            port:
              number: 80
        path: /catalog
        pathType: ImplementationSpecific
      - backend:
          service:
            name: marketing
            port:
              number: 80
        path: /marketing
        pathType: ImplementationSpecific
      - backend:
          service:
            name: marketplace
            port:
              number: 80
        path: /marketplace
        pathType: ImplementationSpecific
      - backend:
          service:
            name: notification
            port:
              number: 80
        path: /notification
        pathType: ImplementationSpecific
      - backend:
          service:
            name: provider
            port:
              number: 80
        path: /provider
        pathType: ImplementationSpecific
  - host: foo.bar
    http:
      paths:
      - backend:
          service:
            name: frontend
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  - host: www.foo.bar
    http:
      paths:
      - backend:
          service:
            name: frontend
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  - host: blog.foo.bar
    http:
      paths:
      - backend:
          service:
            name: wordpress
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - foo.bar
    - api.foo.bar
    - www.foo.bar
    - blog.foo.bar
    secretName: foo-cert

You can see, the redirect is not working:

➜  ~ curl -v -o /dev/null https://www.foo.bar
> GET / HTTP/1.1
> Host: www.foo.bar
> User-Agent: curl/7.68.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: nginx/1.19.8
< Date: Sat, 27 Mar 2021 16:42:18 GMT
< Content-Type: text/html
< Content-Length: 671097
< Connection: keep-alive
< Last-Modified: Tue, 09 Mar 2021 11:51:47 GMT
< ETag: "60476153-a3d79"
< Expires: Thu, 01 Jan 1970 00:00:01 GMT
< Cache-Control: no-cache
< Accept-Ranges: bytes

I noticed, that prod and Minikube deploy different images but don't really know what the difference is if there's any.

Prod:

➜  ~ kubectl -n nginx-ingress get pod nginx-ingress-nginx-ingress-68f5bc7654-kffnm -o json | jq ".spec.containers[0].image"
"nginx/nginx-ingress:1.10.1"

Minikube:

➜  ~ kubectl -n kube-system get pod ingress-nginx-controller-65cf89dc4f-xkdtn -o json | jq ".spec.containers[0].image"
"us.gcr.io/k8s-artifacts-prod/ingress-nginx/controller:v0.40.2@sha256:46ba23c3fbaafd9e5bd01ea85b2f921d9f2217be082580edc22e6c704a83f02f"

Any ideas what's going on?

1 Answers

The reason this is happening is that you've deploy different version of the nginx ingress controller called nginxincinto your GKE cluster. This is very common mistake made especially if you use helm you don't really check which you are deploying.

So kubernetes/ingress-nginx which you used in minikube is maintained by the Kubernetes open source community and the nginxinc/kubernetes-ingress is maintained by NGINX, Inc. You will notice many cases where the one maintained by Kubernetes will be commonly called community Ingress controller and second one NGINX's.

You can find the differences here but main one that is causing you problems is that they both use differences annotation formula with different prefix. For example:

  • Community ingress controller uses: nginx.ingress.kubernetes.io/server-snippet
  • NGINX's uses: nginx.org/server-snippets
Related