nginx k8s ingress - forcing www AND https? - nginx

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

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;
}

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

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

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.

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.

Related

Ingress rewrite to two different services from one DNS

I am trying to reach out to two services from my DNS.
Example:- myportal.com
myportal.com/app1
I have my SSO and SSL implementation on "myportal.com" and on the home page I have a button which calls a service "myportal.com/app1". I have tried multiple things but I am not able to configure both properly.
From below ingress config I am able to redirect to "myportal.com/app1" but its not working properly for "myportal.com". I have tried multiple thing including default backend and its not working for me. When I use default backend then "myportal.com" works but it stops redirecting to /app1.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myportal-ingress-test
namespace: appspace
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- myportal.com
secretName: secret
rules:
- host: myportal.com
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: portal-service
port:
number: 8097
- pathType: Prefix
path: /app1(/|$)(.*)
backend:
service:
name: app-ui-service
port:
number: 8087
I am fairly new to kubernetes and I am not sure if it's the correct way to implement such things. I am open to new implementation too if it's using ingress and routing to different services using same dns on different path.
Thanks in advance for the help!
I was able to resolve this issue. The redirect in an ingress file would work for all the paths, so I created a separate ingress for each path with individual redirects.
Ingress 1 - myportal.com (with SSO and SSL)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gcpportal-service-test
namespace: appspace
annotations:
nginx.ingress.kubernetes.io/x-forwarded-prefix: /myportal-service
nginx.ingress.kubernetes.io/ssl-redirect: 'true'
spec:
ingressClassName: nginx
tls:
- hosts:
- myportal.com
secretName: secret
defaultBackend:
service:
name: myportal-service
port:
number: 80
rules:
- host: myportal.com
Ingress 2 - myportal.com/app1 (application deployed)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gcpportal-service-test-1
namespace: appspace
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- myportal.com
secretName: secret
rules:
- host: myportal.com
- pathType: Prefix
path: /app1(/|$)(.*)
backend:
service:
name: app1-ui-service
port:
number: 8087
This helped me to resolve this issue. I also verified that this way is also a standard way and is provided in one of the documentation of nginx.
https://kubernetes.github.io/ingress-nginx/user-guide/ingress-path-matching/

Add proxy_pass like rule on ingress

I have the ingress configuration as below:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
rules:
- host: example.com
http:
paths:
- backend:
service:
name: my-app
port:
number: 80
pathType: ImplementationSpecific
tls:
- hosts:
- example.com
secretName: my-ingress-tls
This shows the content of the service my-app on port 80 when example.com is reached. However, I want to implement something else. When going to example.com/hello-world, I want to show content from some other website let's say from hello-world.com. In NGINX it can be achieved using proxy_pass. How can I achieve this in ingress?

nginx ingress controller routing doesn't work as expected

I have a kubernetes cluster with an application (deployment + ClusterIp service), nginx ingress controller, cert manager and letsencrypt issuer.
Here the service
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: mynamespace
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
This is the the ingress yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: mynamespace
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts:
- <myapp>.<myregion>.cloudapp.azure.com
secretName: tls-secret
rules:
- host: <myapp>.<myregion>.cloudapp.azure.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
It works correctly, responding to the url https://<myapp>.<myregion>.cloudapp.azure.com.
Now I need to change the path like in :
spec:
tls:
- hosts:
- <myapp>.<myregion>.cloudapp.azure.com
secretName: tls-secret
rules:
- host: <myapp>.<myregion>.cloudapp.azure.com
http:
paths:
- path: /sub
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
I would expect to browse my app at https://<myapp>.<myregion>.cloudapp.azure.com/sub.
Instead I get
This <myapp>.<myregion>.cloudapp.azure.com page can’t be found
What I am doing wrong?
I tried to find examples online, but couldn't find any that helped me understand what's wrong.
EDIT
What happens behind the scenes (dev tools) is:
The browser sends a request to /sub
The ingress routes to the correct service, rewriting the url to /
The application receives the request correctly
The application wants to redirect the browser to a login url (e.g. /login)
The browser receives a redirect (302) to /login and executes it
The ingress doesn't see the /sub in the redirect url, so it doesn't know what to do
I guess the redirect url should be /sub/login, not simply /login.
There should be an easy way to configure the ingress to fix this trivial issue. Can someone point me to the right direction?

Kubernetes nginx ingress https problems

I have set up an on-premises Kubernetes 1 node cluster on Ubuntu
with nginx-ingress and metalLB
I have 2 problems:
If I go to my site with https://www.example.com it works but it doesn't with https://example.com (bad certificate).
I would like to redirect HTTP to HTTPs (HTTP://example.com not working).
I have no problems with other subdomains (subdomain.example.com)
Here is my Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: clientweb-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$1
cert-manager.io/issuer: letsencrypt-prod
spec:
rules:
- host: www.example.com
http:
paths:
- path: /?(.*)
pathType: Prefix
backend:
service:
name: clientweb-service
port:
number: 80
tls:
- hosts:
- example.com
- www.example.com
secretName: ethernial-tls-secret

How to configure multiple ingresses in different namespaces on the same host

I have a single host cluster with k8s and I would like to configure a Ingress for each namespace in order to create separated environment: one for production, one for development.
I also took 2 different domains.
When I deploy the production ingress there are no problems, but when I deploy the second ingress the dev environment is unreachable, using port-forward everything seems fine.
The 2 ingresses configuration:
Dev
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
ingress.bluemix.net/redirect-to-https: "True"
name: dev-ingress
namespace: dev
spec:
rules:
- host: '*.dev.cloud'
http:
paths:
- backend:
serviceName: web-service
servicePort: 80
path: /
tls:
- hosts:
- '*.dev.cloud'
secretName: dev-cert
Production
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
ingress.bluemix.net/redirect-to-https: "True"
name: prod-ingress
spec:
rules:
- host: '*.prod.cloud'
http:
paths:
- backend:
serviceName: web-service
servicePort: 80
path: /
- host: "k8s-host"
http:
paths:
- path: /path/to/api
backend:
serviceName: web-api
servicePort: 3000
tls:
- hosts:
- '*.prod.cloud'
secretName: prod-cert
- hosts:
- "k8s-host"
secretName: k8s-host-cert
I also edited the CNAME record of the 2 domains in order to redirect to the k8s host.
I was expecting that the request from a subdomain of one of the domain would be redirected to the ingress matching the domain.
For example:
https://abc.dev.cloud -> dev-ingress
https://abc.prod.cloud -> prod-ingress

Resources