Nginx Ingress path rewriting does not forward to specified endpoint in Kubernetes Cluster - kubernetes

In our Kubernetes Cluster we have a micro frontend, which is accessable under image-self-service:8080 without any additional path. To be able to use it in a bigger application I want to use an Nginx-Ingress to reroute from url-to-site/profile to image-self-service:8080/.
I have found and read this SO post, thats basically asking the same thing:
Remove routing path from Kubernetes ingress and the linked documentation here:
https://github.com/kubernetes/ingress-nginx/blob/main/docs/examples/rewrite/README.md
which resulted in me using a .yaml for my Ingress that looks like this: (sensitive Information removed)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS, DELETE"
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,X-CustomHeader,X-LANG,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Api-Key,X-Device-Id,Access-Control-Allow-Origin"
nginx.ingress.kubernetes.io/rewrite-target: /
name: intranet-backend-ingress
namespace: intranet
spec:
rules:
- host: xxx-test.xxx.xxx
http:
paths:
- backend:
service:
name: image-self-service
port:
number: 8080
path: /profile(/|$)(.*)
pathType: Prefix
- backend:
service:
name: intranet-ui
port:
number: 8080
path: /
pathType: Prefix
...
For readability I also removed other endpoints which are more specific as I don't think those interfere.
I have tried using nginx.ingress.kubernetes.io/rewrite-target: / and nginx.ingress.kubernetes.io/rewrite-target: /$2
If I remove the rewrite-target and add a specific path, the forwarding works but with the additional path, so it results in a 404 from the underlying webservice. The Response I get using the rewriting is simply: 404 page not found which I believe to be an Ingress message.

Related

Unable to access ingress k8s resources after using rewrite-target annotation

I have a K8s cluster, with two services - one service serving the front end static files and the other for backend. To differentiate between the two, I am using an ingress and a rest call to xyz.com/ is directed to the frontend service and rest calls to xyz.com/rest/abc are directed to the backend service (which is an api gateway and collection of services behind it). When creating an ingress, if i use the rewrite target annotation to rewrite xyz.com/rest/abc to xyz/com/abc, then i start getting errors when trying to access the front end service..
The errors that I have faced are
Did not parse stylesheet .... because non css mime types are not allowed in strict mode
Syntax error: Unexpected token < (while parsing bundle.js)
but if i am not using the rewrite annotation, then all files are transferred without any issue and the page is rendered as it should
annotation being used:
nginx.ingress.kubernetes.io/rewrite-target: /$2
using nginx ingress controller
Not sure why this is happening, could anyone help, any pointers, or any error that I am making.
Instead of using the path: "/" for the frontend service, i tried giving a more exact path "/ui" but it still gives the same issue.
New to K8s and ingress, so stuck here, and not sure what to do.
Ingress ->
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sample-ingress
labels:
name: sample-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
rules:
- host: xyz.com
http:
paths:
- pathType: Prefix
path: /rest(/|$)(.*)
backend:
service:
name: backend-service
port:
number: 8080
- pathType: Prefix
path: /()
backend:
service:
name: frontend-service
port:
number: 3000
For me it's still not clear what the desired behavior is. If you want to have
/rest/restpath -> backend-service/restpath
and
/uipath -> frontend-service/uipath
then your ingress could look like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sample-ingress
labels:
name: sample-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
rules:
- host: xyz.com
http:
paths:
- pathType: Prefix
path: /rest/(.*)
backend:
service:
name: backend-service
port:
number: 8080
- pathType: Prefix
path: /(.*)
backend:
service:
name: frontend-service
port:
number: 3000

K8S traffic to one service via two separate ingress (http + https)

So I have a bunch of services running in a cluster, all exposed via HTTP only ingress object, example:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: some-ingress
spec:
ingressClassName: nginx
rules:
- http:
paths:
- backend:
service:
name: some-svc
port:
number: 80
path: /some-svc(/|$)(.*)
pathType: Prefix
They are accessed by http://<CLUSTER_EXTERNAL_IP>/some-svc, and it works ofc.
Now I want to create an additional ingress object for every service which will force SSL connections and allow the use of a domain instead of an IP address.
The problem is that the newer SSL ingresses always return 404 while testing the connection.
The manifests are as follows:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: "some-ingress-ssl"
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
ingress.kubernetes.io/app-root: "/some-svc"
spec:
tls:
- hosts:
- foobar.com
secretName: foobar-tls
rules:
- host: foobar.com
http:
paths:
- path: /some-svc(/|$)(.*)
pathType: Prefix
backend:
service:
name: some-svc
port:
number: 80
tests (foobar.com point to CLUSTER_EXTERNAL_IP):
> curl -I http://<CLUSTER_EXTERNAL_IP>/some-svc
HTTP/1.1 200 OK
> curl -I https://foobar.com/some-svc
HTTP/2 404
Is it possible to have both ingresses simultaneously? (one enforcing SSL, the other not)
If so what am I doing wrong here?
Figured out I was missing this annotation:
nginx.ingress.kubernetes.io/rewrite-target: /$2
in SSL ingress...
works like a charm now, maybe someone will find this usefull

K8S Ingress controller redirect to the wrong path

I have an Nginx ingress controller running on one k8s namespace,
and on another k8s namespace, I defined a pod, a service, and an ingress resource.
this is the ingress resource definition:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: general-internal
rules:
- host: example.com
http:
paths:
- path: "/my-app(/|$)(.*)"
backend:
serviceName: my-app-svc
servicePort: 443
now, when I access this link:
http://example.com/my-app/some-path/
then everything is ok because the "my-app-svc" knows the path "/some-path/" and returns a 200 response (the forwarding is to http://my-app-svc/some-path and that's great because my-app-svc doesn't and shouldn't know or care for the /my-app prefix that exists only for the nginx ingress controller so it will know to forward that request to "my-app-svc" internally).
but when I access this link (notice no "/" in the end):
http://example.com/my-app/some-path
I get a redirection response from the "my-app-svc" service, and the "Location" header contains "/some-path/", so the redirection is to:
http://example.com/some-path/
which does not lead to the "my-app-svc" service because it doesn't have the "/my-app" prefix.
If the Location header was "/my-app/some-path/" instead of "/some-path/" then everything was ok because the redirection would be to:
http://example.com/my-app/some-path/
which would give me the 200 response.
the question is, how can I do that the ingress controller will add a "my-app" prefix to the Location header when it returns the redirection response to the client?
Thanks
Thanks to my co-worker, we found a solution for the problem:
The solution is to add these annotations to the ingess resource:
nginx.ingress.kubernetes.io/proxy-redirect-from: /
nginx.ingress.kubernetes.io/proxy-redirect-to: /my-app/
meaning:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/proxy-redirect-from: /
nginx.ingress.kubernetes.io/proxy-redirect-to: /my-app/
spec:
ingressClassName: general-internal
rules:
- host: example.com
http:
paths:
- path: "/my-app(/|$)(.*)"
backend:
serviceName: my-app-svc
servicePort: 443
It seems that the annotations above check the "Location" header in the redirection response to the client, and replace the first / with /my-app/ and only after this change - the redirection response is sent to the client.
For anyone landing here and finding the above solution not working (like myself), you might be using nginx-based ingress https://www.nginx.com/blog/guide-to-choosing-ingress-controller-part-4-nginx-ingress-controller-options/ which needs different annotation to your ingress resource:
nginx.org/rewrites: "serviceName=service1 rewrite=rewrite1[;serviceName=service2 rewrite=rewrite2;...]"
which worked for me.
A complete declaration from https://github.com/nginxinc/kubernetes-ingress/tree/v2.3.0/examples/rewrites copied here:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cafe-ingress
annotations:
nginx.org/rewrites: "serviceName=tea-svc rewrite=/;serviceName=coffee-svc rewrite=/beans/"
spec:
rules:
- host: cafe.example.com
http:
paths:
- path: /tea/
pathType: Prefix
backend:
service:
name: tea-svc
port:
number: 80
- path: /coffee/
pathType: Prefix
backend:
service:
name: coffee-svc
port:
number: 80

How to properly configure k8s nginx ingress base url and path to handle vuejs client side routing and nodejs server side api

I'm trying to deploy my frontend client application and backend API on the same domain and I want the frontend on the base path: /
However, I realized that the frontend and the backend need two different rewrite-target to accomplish this.
front-end works with:
nginx.ingress.kubernetes.io/rewrite-target: /
while the backend works with:
nginx.ingress.kubernetes.io/rewrite-target: /$2
I tried using two different ingress services in order to accommodate different rewrite-target, but that fails because the host was the same domain.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress
namespace: test
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-staging
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/add-base-url: "true"
nginx.ingress.kubernetes.io/service-upstream: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
nginx.ingress.kubernetes.io/proxy-buffering: "on"
nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "server: hide";
more_set_headers "X-Content-Type-Options: nosniff";
more_set_headers "X-Xss-Protection: 1";
spec:
tls:
- hosts:
- test.eastus.cloudapp.azure.com
secretName: tls-secret
rules:
- host: test.eastus.cloudapp.azure.com
http:
paths:
- backend:
serviceName: webapp
servicePort: 80
path: /
- backend:
serviceName: api
servicePort: 80
path: /api(/|$)(.*)
I know I can make both work with the same rewrite-target /$2 if I change the frontend path to path: /app(/|$)(.*) but I don't want to that except it is the only option.
Is there a way for me to better configure a single ingress to work for 2 different rewrite-target?
Ingress with API version networking.k8s.io/v1 with k8s version 1.19+ can solve your problem. I have given an example below.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-fanout-example
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 8080
According to kubernetes documentation:
In some cases, multiple paths within an Ingress will match a request. In those cases precedence will be given first to the longest matching path. If two paths are still equally matched, precedence will be given to paths with an exact path type over prefix path type.
So, when you are looking for a resource located at path "/home.html", it only matches with your frontend service. But when you are looking for a resource located at path "/api/something" then it matches both service. But it will go to backend service always because of maximum path match stated above.
I don't know would you take it or not but it can a be solution. You can use two ingress for achieving your target. Each one will use different annotations. It will work fine.
Just as like your current ingress yaml, make another yaml and put two different annotations in them and also put your front-end and back-end in them separately. host (host: test.eastus.cloudapp.azure.com) will be same as you provided here.

Minikube | Ingress Service - Bad Request

I'm working on a single-node cluster which works fine with docker-compose but the reconfiguration of the same setup using Minikube Ingress Controller gives me a Bad Request response.
Bad Request
Your browser sent a request that this server could not understand.
Reason: You're speaking plain HTTP to an SSL-enabled server port.
Instead use the HTTPS scheme to access this URL, please.
My Ingress looks like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- path: /?(.*)
pathType: Prefix
backend:
service:
name: emr-cluster-ip-service
port:
number: 443
- path: /?(.*)
pathType: Prefix
backend:
service:
name: erp-cluster-ip-service
port:
number: 8069
How to fix this?
You are exposing HTTPS service on HTTP ingress, which is not the right thing to do. You might want to do one of the following:
Configure TLS-enabled ingress.
Configure TLS passthough on ingress object.
In both cases you also need to set nginx.ingress.kubernetes.io/ssl-redirect: "true"