Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

from-to-www-redirect is applied after SSL instead of before, causing security warning in browser #2043

Closed
artemzakharov opened this issue Feb 7, 2018 · 33 comments · Fixed by #3637

Comments

@artemzakharov
Copy link

artemzakharov commented Feb 7, 2018

Is this a request for help?: No, bug

What keywords did you search in NGINX Ingress controller issues before filing this one?: force-www, ssl, https, kubernetes ingress controller fake certificate


Is this a BUG REPORT or FEATURE REQUEST?: bug

NGINX Ingress controller version: 0.9.1

Kubernetes version: 1.8.5

Environment: GKE

  • Cloud provider or hardware configuration: Google Cloud
  • OS (e.g. from /etc/os-release): Container OS
  • Kernel (e.g. uname -a):
  • Install tools: Helm
  • Others: n/a

What happened: I have an app I would like to host on www.foo.com. My nginx ingress has the from-to-www-redirect flag enabled to redirect requests from the base domain url, and has a TLS secret to provide for secure connections. This combination of redirect and SSL works for most url inputs, but not one in particular - https://foo.com. In this case, browsers present a security warning like this:

Attackers might be trying to steal your information from foo.com (for example, passwords, messages, or credit cards). Learn more
NET::ERR_CERT_AUTHORITY_INVALID
Subject: Kubernetes Ingress Controller Fake Certificate
Issuer: Kubernetes Ingress Controller Fake Certificate
Expires on: Feb 1, 2019
Current date: Feb 7, 2018
PEM encoded chain:
...

Forcing the browser to proceed to the "unsafe" site redirects to the correct destination of https://www.foo.com and enables SSL like nothing ever happened. For reference, the following urls all redirect to https://www.foo.com with no warnings:

foo.com
http://foo.com
www.foo.com
http://www.foo.com

What you expected to happen: I expect https://foo.com to redirect to https://www.foo.com without browsers displaying a false alarm.

How to reproduce it (as minimally and precisely as possible):

  1. Set up an application on GKE
  2. Place it behind an nginx ingress configured to enable from-to-www-redirect and SSL via a certificate, with a subdomain host like www.foo.com pointing to the application service
  3. Attempt to reach the application with https://<base-domain>.

Anything else we need to know:

Here's my nginx ingress config file:

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
@JordanP
Copy link
Contributor

JordanP commented Feb 12, 2018

Is your certificate valid for both foo.com and www.foo.com ?

@artemzakharov
Copy link
Author

artemzakharov commented Feb 12, 2018

No, it's for www.foo.com only. Does that actually matter though? It seems like it's not bringing up my certificate at all when I hit https://foo.com, hence why the browser sees a 'kubernetes ingress controller fake certificate'. Or does a mismatch cause the ingress to throw out a fake cert? Seems confusing if that's the case.

@aledbf
Copy link
Member

aledbf commented Feb 25, 2018

Closing. This works as expected. As @JordanP said, if you have an SSL certificate, you need one that contains all the hosts you need (CN in the certificate). This is a restriction in NGINX that we cannot change.

@aledbf aledbf closed this as completed Feb 25, 2018
@artemzakharov
Copy link
Author

@aledbf So the Ingress Controller throws a 'fake' certificate when the provided certificate doesn't cover the requested host?

@aledbf
Copy link
Member

aledbf commented Feb 26, 2018

@artemzakharov NGINX, not the controller.

@artemzakharov
Copy link
Author

@aledbf Got it, thank you for the clarification. Hadn't used NGINX before using this library so wasn't sure who was responsible for which behavior.

@ghost
Copy link

ghost commented Mar 11, 2018

I have same issue, my certificate is valid for both www.foo.com and foo.com but when using from-to-www-redirect and

  tls:
  - hosts:
    - www.foo.com
    - foo.com
    secretName: foo-com-ssl

ssl_certificate and ssl_certificate_key directives don't get generated for foo.com server block. I've checked this with nginx template and these directives don't exists in them either

{{/* Build server redirects (from/to www) */}}
{{ range $hostname, $to := .RedirectServers }}
server {
{{ range $address := $all.Cfg.BindAddressIpv4 }}
listen {{ $address }}:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }};
listen {{ $address }}:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }} ssl;
{{ else }}
listen {{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }};
listen {{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }} ssl;
{{ end }}
{{ if $IsIPV6Enabled }}
{{ range $address := $all.Cfg.BindAddressIpv6 }}
listen {{ $address }}:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }};
listen {{ $address }}:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }};
{{ else }}
listen [::]:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }};
listen [::]:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }};
{{ end }}
{{ end }}
server_name {{ $hostname }};
{{ if ne $all.ListenPorts.HTTPS 443 }}
{{ $redirect_port := (printf ":%v" $all.ListenPorts.HTTPS) }}
return {{ $all.Cfg.HTTPRedirectCode }} $scheme://{{ $to }}{{ $redirect_port }}$request_uri;
{{ else }}
return {{ $all.Cfg.HTTPRedirectCode }} $scheme://{{ $to }}$request_uri;
{{ end }}
}
{{ end }}

@kwojtaszek
Copy link

kwojtaszek commented May 5, 2018

I can confirm that issue exists in 0.14.0, generic block with no ssl included is generated for this specific scenario:

	server {

		listen 80;
		listen 443 ssl;

		listen [::]:80;
		listen [::]:443;

		server_name www.foo.com;

		return 308 $scheme://foo.com$request_uri;

	}

@johnmshields
Copy link

@aledbf Can this be re-opened? The comments above show the issue. It works fine if you come in on http but if the client comes in on https then the connection cannot be made so the redirect cannot occur.

@aledbf aledbf reopened this May 26, 2018
@renaudguerin
Copy link

Experiencing this as well, with certs generated by cert-manager (ex kube-lego)

Pretty serious issue IMHO, this makes from-to-www-redirect unusable with TLS.

The spec.tls.hosts entry in my ingress has both www.foo.com, foo.com, and a 3rd domain, and the resulting cert does have all 3 domains.

So, the problem definitely seems to be in the generated nginx config.

@thomascooper
Copy link

thomascooper commented Jun 6, 2018

I am having the same issue using nginx-ingress-controller:0.14.0. If I visit the root domain i receive a cert error (its serving the Kubernetes Ingress Controller Fake Certificate).

I use https://github.com/jetstack/cert-manager (very similar and based on kube-lego, same as the commenter above).

Here is my ingress config:

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: group-1
  annotations:
    kubernetes.io/ingress.class: "group1-ingress"
    kubernetes.io/tls-acme: "true"
    [ cert-manager annotations ]
    nginx.ingress.kubernetes.io/ssl-redirect: "True"
    nginx.ingress.kubernetes.io/from-to-www-redirect: "True"
spec:
  tls:
  - hosts:
    - www.foo.com
    - foo.com
    secretName: prd-live-group-1
  rules:
  - host: www.foo.com
    http:
      paths:
      - backend:
          serviceName: foo-service
          servicePort: 80

@thomascooper
Copy link

thomascooper commented Jun 6, 2018

For those interested in a short term work around, I add the following annotation to get it to work as I need for now:

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

Then I add the root foo.com to the rules:

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: group-1
  annotations:
    kubernetes.io/ingress.class: "group1-ingress"
    kubernetes.io/tls-acme: "true"
    [ cert-manager annotations ]
    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($host = 'foo.com' ) {
        rewrite ^ https://www.foo.com$request_uri permanent;
      }
    nginx.ingress.kubernetes.io/ssl-redirect: "True"
    nginx.ingress.kubernetes.io/from-to-www-redirect: "True"
spec:
  tls:
  - hosts:
    - www.foo.com
    - foo.com
    secretName: prd-live-group-1
  rules:
  - host: www.foo.com
    http:
      paths:
      - backend:
          serviceName: foo-service
          servicePort: 80
  - host: foo.com
    http:
      paths:
      - backend:
          serviceName: foo-service
          servicePort: 80

@AmbroiseCouissin
Copy link

thanks @thomascooper !

This worked for me:

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

@fejta-bot
Copy link

Issues go stale after 90d of inactivity.
Mark the issue as fresh with /remove-lifecycle stale.
Stale issues rot after an additional 30d of inactivity and eventually close.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/lifecycle stale

@k8s-ci-robot k8s-ci-robot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Sep 16, 2018
@MarkPare
Copy link

@thomascooper's solution also works for redirecting www to non-www. Something like:

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

I could not get this to work without using this snippet. I'm sure the developers on this project have their hands full, but the current behavior outlined in this thread seems unexpected and the issue warrants staying open.

@quorak
Copy link

quorak commented Sep 23, 2018

issue still exists with nginx-ingress-0.20.3
/remove-lifecycle stale

@k8s-ci-robot k8s-ci-robot removed the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Sep 23, 2018
@AlecTroemel
Copy link

any status update on this? I'm experiencing the same issue... if nothing else my comment will hopefully keep this issue open

@jurgenweber
Copy link

jurgenweber commented Oct 23, 2018

yeah, I have this in 0.20.0. The issue as I see it is the following taken from one of my ingress pods nginx.conf. This is the code I assume nginx.ingress.kubernetes.io/from-to-www-redirect triggers.

	server {

		listen 80 proxy_protocol;
		listen 443 proxy_protocol ssl;

		listen [::]:80 proxy_protocol;
		listen [::]:443 proxy_protocol;

		server_name example.com.au;

		return 308 $scheme://www.example.com.au$request_uri;

	}

this does not have a valid ssl cert to serve.

@honi
Copy link

honi commented Oct 29, 2018

Same here, have to use the configuration-snippet annotation to configure the redirect in nginx, as well as listing all domains in the rules section.

If you have additional hosts in the tls section (for example www or non www version of your domain), you still have to list them in the rules section and map them to some service because it seems like the generated nginx config only reads the listed hosts in the rules section.

@scaccogatto
Copy link

Watching this!

@pnull
Copy link

pnull commented Jan 3, 2019

Long time no change. it's very sad that this issue isn't resolved after nearly one year :-(

@lgelfan
Copy link

lgelfan commented Jan 5, 2019

Still seems like a problem for https://foo.com when you want to use from-to-www-redirect. Similar to above, this seems to work for us, in the www.foo.com ingress:

    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($host = 'foo.com' ) {
        rewrite ^ https://www.foo.com$request_uri permanent;
      }
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/server-alias: foo.com

We're using a wildcard cert for *.foo.com

@FredericLatour
Copy link

Is this supposed to work now?
Just want to know if there is something wrong on my side of if the problem still exist.

@pavel-khritonenko
Copy link

The same - doesn't work for me, ingress handles both addresses, www and naked one.

@pavel-khritonenko
Copy link

nvm, you have to specify both domains in Certificate and only domains you want to serve in Ingress.

@corpulent
Copy link

corpulent commented Feb 5, 2020

I have a wildcard certificate "CN": "*.foo.com" nothing in this thread worked for me. Just trying to redirect www to non-www. Still serving the fake certificate.

My ingress configuration.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($host = 'www.demo.foo.com' ) {
        rewrite ^ https://demo.foo.com$request_uri permanent;
      }
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/server-alias: demo.foo.com
spec:
  rules:
    - host: "demo.foo.com"
      http:
        paths:
          - path: /
            backend:
              serviceName: demo-service
              servicePort: 5678
  tls:
    - hosts:
      - "demo.foo.com"
      secretName: wildcard-foo-com

@SeriousJul
Copy link

@corpulent
Your problem is that there is no ingress rule for www.demo.foo.com. Whenever a request come from this domain, k8s does not know that you want to execute this ingress since it is not written there.

Update your rules as follow:

  rules:
    - host: "demo.foo.com"
      http:
        paths:
          - path: /
            backend:
              serviceName: demo-service
              servicePort: 5678
    - host: "www.demo.foo.com"
      http:
        paths:
          - path: /
            backend:
              serviceName: demo-service
              servicePort: 5678
  tls:
    - hosts:
      - "demo.foo.com"
      - "www.demo.foo.com"
      secretName: wildcard-foo-com

@corpulent
Copy link

@SeriousJul Thanks, that worked! And the other issues I had also resolved from the comments in this issue.

@JustDoItSascha
Copy link

Sorry if I have to ask: But is this problem now solved? And if yes, in which version. Because I'm facing the issue right now with version 0.10.2 and it would be nice to know to which version I have to upgrade.

@honi
Copy link

honi commented Aug 19, 2020

This is working for me with this config. I'm using cert-manager to obtain the certificates. I noticed you have to list both hosts in the tls section and have a valid certificate for both as mentioned in other comments, but you shouldn't list the www host in the rules section for the from-to-www-redirect annotation to work.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: web
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer: "letsencrypt"
    nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
spec:
  tls:
  - hosts:
    - foo.com
    - www.foo.com
    secretName: web-tls
  rules:
  - host: foo.com
    http:
      paths:
      - path: /
        backend:
          serviceName: web
          servicePort: 8000

@JustDoItSascha
Copy link

JustDoItSascha commented Aug 19, 2020 via email

@honi
Copy link

honi commented Aug 19, 2020

@JustDoItSascha nginx-ingress-controller:0.32.0

@VengefulAncient
Copy link

This is working for me with this config. I'm using cert-manager to obtain the certificates. I noticed you have to list both hosts in the tls section and have a valid certificate for both as mentioned in other comments, but you shouldn't list the www host in the rules section for the from-to-www-redirect annotation to work.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: web
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer: "letsencrypt"
    nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
spec:
  tls:
  - hosts:
    - foo.com
    - www.foo.com
    secretName: web-tls
  rules:
  - host: foo.com
    http:
      paths:
      - path: /
        backend:
          serviceName: web
          servicePort: 8000

I am not sure how it works for you, because I'm using the exact same configuration (www. domain in TLS but not in rules) and I'm still experiencing this issue. Opened #7103 in case this is outdated/different/forgotten, but it's clearly the same problem - SSL redirect precedes from-to-www-redirect seemingly resulting in a non-existing backend being requested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.