14
votes

I have a kubernetes setup that looks like this:

nginx ingress -> load balancer -> nginx app

after getting an SSL certificate for www.foo.com, I've installed it in my nginx ingress as a secret, and it works as expected - traffic to www.foo.com gets redirected to the https version instead, and browsers display a secure connection indicator. Great.

What hasn't been easy, however, is getting the ingress to redirect non-www traffic to the www version of the site. I've tried using kubernetes.io/from-to-www-redirect: "true", but it doesn't seem to do anything - navigating to foo.com doesn't redirect me to the www version of the url, but either takes me to an insecure version of my site, or navigates me to default backend - 404 depending on whether i include foo.com as a host with it's own path in my ingress.

I have been able to set up a patchy redirect by adding the following to my actual application's nginx config -

server {
  listen       80;
  server_name  foo.com;
  return       301 http://www.foo.com$request_uri;
}

UPDATE: from-to-www-redirect DOES work; you just have to reference it with nginx.ingress.kubernetes.io rather than kubernetes.io as I was. But, this only works for foo.com - typing in https://foo.com explicitly causes browsers to display a security warning and no redirect to the proper URL of https://www.foo.com occurs.

Here's my current config for the nginx ingress itself:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: foo-https-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
spec:
  rules:
    - host: www.foo.com
      http:
        paths:
          - backend:
              serviceName: foo-prod-front
              servicePort: 80
            path: /
  tls:
      - hosts:
          - www.foo.com
        secretName: tls-secret
5
I can confirm I'm getting the same results as you and will post back if I find a solution!Dave Mateer

5 Answers

15
votes

You need to add the certificate for the domain you want to be redirected:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: foo-https-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
spec:
  rules:
    - host: foo.com
      http:
        paths:
          - backend:
              serviceName: foo-prod-front
              servicePort: 80
            path: /
    - host: www.foo.com
      http:
        paths:
          - backend:
              serviceName: foo-prod-front
              servicePort: 80
            path: /
  tls:
      - hosts:
          - foo.com
          - www.foo.com
        secretName: tls-secret

I am not completely sure, whether from-to-www-redirect works with this setup, but you can replace it with the following lines, which do work:

    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($host = 'foo.com' ) {
        rewrite ^ https://www.foo.com$request_uri permanent;
      }
8
votes

I have the following doing the job with the latest nginx-ingress 0.25.1:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-rule-web
  annotations:
    kubernetes.io/ingress.class: nginx
    certmanager.k8s.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/from-to-www-redirect: 'true'
spec:
  rules:
    - host: foo.org
      http:
        paths:
          - path: /
            backend:
              serviceName: web
              servicePort: 80
  tls:
    - hosts:
        - foo.org
        - www.foo.org
      secretName: letsencrypt-prod
5
votes

I found the docs to be confusing here as well. Below is an example i have working. I believe you need to define the naked url in tls certs to avoid a cert error(your cert needs to be valid for both foo.com and www.foo.com). You CANNOT list the naked url under rules: hosts because that will get picked up prior to the redirect.

http://foo.com -> https://www.foo.com

https://foo.com -> https://www.foo.com

http://www.foo.com -> https://www.foo.com

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: foo-https-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
spec:
  rules:
    - host: www.foo.com
      http:
        paths:
          - backend:
              serviceName: foo-frontend
              servicePort: 80
            path: /
  tls:
      - hosts:
          - foo.com
          - www.foo.com
        secretName: tls-secret
1
votes

This is rather a problem with your ssl certificate than the nginx ingress configuration. My guess is that your certificate is only valid for foo.com and not for www.foo.com. If you access www.foo.com your browser shows a security warning because the certificate isn't valid for the domain you are visiting.

1
votes

I had to solve an issue first:

The solution that worked for me is the one from @demisx but on my first try, the solution was not working for another reason. I had more than one ingress with reference to the "example.com" root host and as described on the documentation this was omitting my www redirect rule.

Documentation refers that "If at some point a new Ingress is created with a host equal to one of the options (like domain.com) the annotation will be omitted."

This is example is wrong:

Ingress 1 - to handle example.com

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example-https-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
spec:
  rules:
    - host: www.example.com
      http:
        paths:
          - backend:
              serviceName: example-frontend
              servicePort: 80
            path: /
  tls:
      - hosts:
          - example.com
          - www.example.com
        secretName: tls-secret

Ingress 2 - to handle example.com/news

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example-https-ingress-news
spec:
  rules:
    - host: example.com. # <--------- I HAD ANOTHER REFERENCE
      http:
        paths:
          - backend:
              serviceName: example-news
              servicePort: 80
            path: /news
     - host: www.example.com
       http:
         paths:
           - backend:
              serviceName: example-news
              servicePort: 80
            path: /news
  tls:
      - hosts:
          - example.com
          - www.example.com
        secretName: tls-secret

Solution - Correct Configuration

Make sure that you don't have any other ingress created with the root domain otherwise the redirect will not work as documentation refers. I removed the reference to example.com host on ingress 2 and then immediately started to work.

Ingress 1 - to handle example.com

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example-https-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
spec:
  rules:
    - host: www.example.com
      http:
        paths:
          - backend:
              serviceName: example-frontend
              servicePort: 80
            path: /
  tls:
      - hosts:
          - example.com
          - www.example.com
        secretName: tls-secret

Ingress 2 - to handle example.com/news

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example-https-ingress-news
spec:
  rules:
     # <--------- removed the other reference to the root host 
     - host: www.example.com
       http:
         paths:
           - backend:
              serviceName: example-news
              servicePort: 80
            path: /news
  tls:
      - hosts:
          - example.com
          - www.example.com
        secretName: tls-secret

Note: By the way, I didn't need to add the forward annotation to the 2nd ingress cause is already handled by the first ingress. I'm not sure though, if the order of deployment matters for nginx ingress controllers so take this as a note only and try to confirm yourself.