Nginx ingress rule for changing path on the fly - nginx

I would like to change the path of request coming to ingress-nginx on the fly to match what different backend services expect. For example, imagine these are the two services I have got:
foo service:
http://foo:8080/api/v1
bar service:
http://bar:8080/api/v1
I would like to expose foo service as http://test.com/foo/api and bar service as http://test.com/bar/api. However, I am not sure how I can change the path on the fly to match what the underlying service expects.
Example ingress.yaml file:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
spec:
rules:
- host: test.com
http:
paths:
- path: /foo/api/
backend:
serviceName: foo
servicePort: 8080
- path: /bar/api
backend:
serviceName: bar
servicePort: 8080
- path: /
backend:
serviceName: ui
servicePort: 80

You can try with rewrite annotations.
Look at the example provided in this documentation:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: rewrite
namespace: default
spec:
rules:
- host: rewrite.bar.com
http:
paths:
- backend:
serviceName: http-svc
servicePort: 80
path: /something(/|$)(.*)
In this ingress definition, any characters captured by (.*) will be assigned to the placeholder $2, which is then used as a parameter in the rewrite-target annotation.
For example, the ingress definition above will result in the following rewrites:
rewrite.bar.com/something rewrites to rewrite.bar.com/
rewrite.bar.com/something/ rewrites to rewrite.bar.com/
rewrite.bar.com/something/new rewrites to rewrite.bar.com/new
Just adjust the path and other variables with your data.
Please let me know if that helped.

Related

Per-path rewrite in an Kubernetes NGINX reverse proxy ingress controller

I'm setting up a Kubernetes cluster which contains three applications, each running within their own respective pod and service. The web frontends of these applications should be accesible at ports 80, 9000 and 15672. There are also a number of Backend-APIs running in their own pods and services, which should be accesible at their respective ports. The cluster is accessed through a NGINX reverse proxy with the following ingress-definition:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
namespace: my-namespace
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
tls:
- hosts:
- myapp.com
secretName: my-certificat
rules:
- host: myapp.com
http:
paths:
- backend:
serviceName: my-service-1
servicePort: 8092
path: /api/someroute/(.*)
- backend:
serviceName: my-service-2
servicePort: 30003
path: /api/someotherroute/(.*)
- backend:
serviceName: my-other-frontend
servicePort: 9000
path: /other/(.*)
- backend:
serviceName: my-yetanother-frontend
servicePort: 15672
path: /yetanother/(.*)
- backend:
serviceName: my-main-frontend
servicePort: 80
path: /(.*)
This works for the api-services, but not for the frontends. Was I to enter a URI like myapp.com/other/ in my browser it would make a call to my-other-frontend:9000/other/, instead of my-other-frontend:9000/. This can of course be solved with a rewrite annotation like nginx.ingress.kubernetes.io/rewrite-target: /$1, the problem is, that this would also apply to the api-services, and those actually need the complete route in their call, so a general rewrite rule would break them.
So my question is: Is it possible to define rewrite rules that only apply to specific paths?
I would advise to split your ingress into 2 based on your different annotations.
1) First one for api-services, called my-api-ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-api-ingress
namespace: my-namespace
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
tls:
- hosts:
- myapp.com
secretName: my-certificat
rules:
- host: myapp.com
http:
paths:
- backend:
serviceName: my-service-1
servicePort: 8092
path: /api/someroute/(.*)
- backend:
serviceName: my-service-2
servicePort: 30003
path: /api/someotherroute/(.*)
2) And second one, lets say my-front-ingress with nginx.ingress.kubernetes.io/rewrite-target: /$1 annotation for rest of your frontends. Based on your question I believe you dont need the detailed instruction for ingress creation with rewrite annotation.
EDIT1:
Yes, its possible to create multiple ingresses with the same host. But please pay attention which exactly nginx ingress. Refer to #LazerBass answer with his investigations.
In short, based on nginx ingress comparation table,
The nginxinc controller does not support merging Ingress rules with the same host. You can use it only with Mergeable Ingresses
The easiest way is to use regular kubernetes/ingress-nginx
Hope that helps.

We hosted a website in AWS Kubernetes Service. niginx-ingress-path issue

We hosted a website in AWS Kubernetes Service. We are having following issue.
issue1.css not working
issue2:Redirection also not working
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: myapp1-alerts-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/rewrite-target: "/"
spec:
tls:
- hosts:
- dev-api.myapp1.solutions
secretName: myapp1-alerts-tls
rules:
- host: dev-api.myapp1.solutions
http:
paths:
- path: /alerts
backend:
serviceName: myapp1-alerts
servicePort: 80
enter image description here
Take a look at the official documentation. There you will find an example below:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: rewrite
namespace: default
spec:
rules:
- host: rewrite.bar.com
http:
paths:
- backend:
serviceName: http-svc
servicePort: 80
path: /something(/|$)(.*)
| kubectl create -f -
In this ingress definition, any characters captured by (.*) will be
assigned to the placeholder $2, which is then used as a parameter in
the rewrite-target annotation.
For example, the ingress definition above will result in the following
rewrites:
rewrite.bar.com/something rewrites to rewrite.bar.com/
rewrite.bar.com/something/ rewrites to rewrite.bar.com/
rewrite.bar.com/something/new rewrites to rewrite.bar.com/new
Adjust your config accordingly and please let me know if that helped.

K8S - Ingress - Route 2 different applications

In my minikube k8s cluster, I have 2 applications/services.
This is my ingress controller for the services I have.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-tutorial
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
backend:
serviceName: default-http-backend
servicePort: 80
rules:
- host: mysite.com
http:
paths:
- path: /sva
backend:
serviceName: app-a
servicePort: 8080
- path: /svb
backend:
serviceName: app-b
servicePort: 8150
When i type mysite.com/sva or mysite.com/svb it routes to appropriate services. However, none of the static files (js, css, images etc) is loaded as they seem to request for the resources from mysite.com instead of mysite.com/sva/
How can I make it look for the resources under specific service?
Try to Add the following annotation in ingress
ingress.kubernetes.io/add-base-url: "true"
will solves this problem.
You can also review : https://github.com/kubernetes/ingress-nginx/issues/333
Based on Harsh Manvar answer, Looks like rewrite does not work on the static resources as of now.
This is a workaround I am planning to follow to route diff apps like sva.mysite.com or svb.mysite.com. It works fine. Just adding this as an answer if somebody faces similar problem.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-tutorial
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
backend:
serviceName: default-http-backend
servicePort: 80
rules:
- host: sva.mysite.com
http:
paths:
- path: /
backend:
serviceName: app-a
servicePort: 8080
- host: svb.mysite.com
http:
paths:
- path: /
backend:
serviceName: app-b
servicePort: 8150

nginx ingress sub path redirection

I have an ingress controller and ingress resource running with all /devops mapped to devopsservice in the backend. When I try to hit "http://hostname/devops" things work and I get a page (although without CSS and styles) with a set of hyperlinks for e.g one of them is "logs".
When I click on the "logs" hyperlink, it is redirecting me to http://hostname/logs whereas I need it to be http://hostname/devops/logs.
Any idea what I can do?
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
namespace: ingress-nginx
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/add-base-url : "true"
spec:
rules:
- host: master1.dev.local
http:
paths:
- backend:
serviceName: devops1
servicePort: 10311
path: /devops
Looks like your ingress is not serving anything /devops/*. Try adding another path /devops/* with the same backend. Basically this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
namespace: ingress-nginx
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/add-base-url : "true"
spec:
rules:
- host: master1.dev.local
http:
paths:
- backend:
serviceName: devops1
servicePort: 10311
path: /devops/*
- backend:
serviceName: devops1
servicePort: 10311
path: /devops
Update: the above has been deprecated in favor of something like this:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: rewrite
namespace: default
spec:
rules:
- host: master1.dev.local
http:
paths:
- backend:
serviceName: devops1
servicePort: 10311
path: /devops(/|$)(.*)
If you access http://hostname/devops/logs directly from your browser, certainly you will get what you want. But since you click the hyperlink in the homepage, then you can only get http://hostname/logs, which will be certainly failed.
So, you need /logs backend configured in your ingress yaml to get it processed, and configure nginx.ingress.kubernetes.io/configuration-snippet to ensure /logs not get rewrote, like this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
namespace: ingress-nginx
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/add-base-url : "true"
nginx.ingress.kubernetes.io/configuration-snippet: |
rewrite ^/logs /logs break;
spec:
rules:
- host: master1.dev.local
http:
paths:
- backend:
serviceName: devops1
servicePort: 10311
path: /logs
- backend:
serviceName: devops1
servicePort: 10311
path: /devops
I met the similar issue recently.
Assuming the "logs" hyperlink in your html uses a relative path, which means the hyperlink doesn't start with '/', then I think you can try to hit the page at http://hostname/devops/ instead of http://hostname/devops. (Note a / suffix in the first url.).
Then the "logs" hyperlink will be formed as http://hostname/devops/logs.
I think it's about how the browser identifies the 'base' url. With accessing 'http://hostname/devops/, if there is no 'base' tag in the html header, the 'base' url will be figured out to be http://hostname/devops; while with 'http://hostname/devops', the base url will be http://hostname.
If it's not the case, there is discussion at https://github.com/kubernetes/ingress-nginx/issues/4149. It's suggested a workaround to use the nginx directive subs_filter with a configuration-snippet to make the href attributes relative and also add the base tag in the html header.
The ingress used is as below,
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: rewrite
namespace: default
spec:
rules:
- host: master1.dev.local
http:
paths:
- backend:
serviceName: devops1
servicePort: 10311
path: /devops(/|$)(.*)
The nginx.ingress.kubernetes.io/x-forwarded-prefix annotation can be used for this purpose.
It adds x-forwarded-prefix header to http request with a value from this annotation. You can use it, if your backend support such header.
For example, Spring Boot application can handle it by using property:
server.forward-headers-strategy=framework
In your case ingress would look like the following:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
namespace: ingress-nginx
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/x-forwarded-prefix: /devops
spec:
rules:
- host: master1.dev.local
http:
paths:
- backend:
serviceName: devops1
servicePort: 10311
path: /devops
This solution has its downsides. It forces you to declare each service in separate ingress.

nginx-ingress works well for /, but not for subpaths

I have setup a name based ingress controller, but it doesn't seem to work for anything other than /.
So http://metabase.domain.com works but http://metabase.domain.com/style/app.css does not.
This is my config:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
namespace: domain
annotations:
kubernetes.io/ingress.global-static-ip-name: "domain"
name: domain-ingress
spec:
rules:
- host: metabase.domain.com
http:
paths:
- path: /
backend:
serviceName: metabase
servicePort: 80
- host: jenkins.domain.com
http:
paths:
- path: /
backend:
serviceName: jenkins
servicePort: 80
From the nginx.conf in the everything looks normal too. For some reason the nginx access and error logs are also empty so can't find anything from there too
As you mentioned, there is no error in the log files, and everything looks normal from
your perspective. I may suggest to tune up ingress using annotations tags.
I've checked documentation of ingress-nginx and found that below annotations may help a bit.
In some scenarios, the exposed URL in the backend service differs from the specified path
in the Ingress rule. Without a rewrite, any request will return 404.
Set the annotation
nginx.ingress.kubernetes.io/rewrite-target
to the path expected by the service.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
name: rewrite
namespace: default
spec:
rules:
- host: rewrite.bar.com
http:
paths:
- backend:
serviceName: http-svc
servicePort: 80
path: /something
If the Application Root is exposed in a different path and needs to be redirected, set the annotation
nginx.ingress.kubernetes.io/app-root
to redirect requests for /.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/app-root: /app1
name: approot
namespace: default
spec:
rules:
- host: approot.bar.com
http:
paths:
- backend:
serviceName: http-svc
servicePort: 80
path: /
If the application contains relative links, it is possible to add an additional annotation
nginx.ingress.kubernetes.io/add-base-url
that will prepend a base tag in the header of the returned HTML from the backend.
I got around to solving this, turns out somehow there was another ingress on my namespace that was a very old version of the nginx controller. I cleared my cluster and relaunched my ingress. Things worked out. Thanks for all the help everyone!

Resources