Kubernetes: Ingress-nginx does not work well with subpaths in the /api route - nginx

I'm trying to access the subpaths in my backend, but it doesn't seem to work for anything, such as /api/hello, other than /api/.
This is my config:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/use-regex: "true"
# ADD ANNOTATION
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: example.com
http:
paths:
- path: /api(/|$)(.*)
# UPDATE PATH
pathType: Prefix
backend:
service:
name: server-srv
port:
number: 3000
- path: /
pathType: Prefix
backend:
service:
name: client-srv
port:
number: 3000
When trying to access the subpaths in /api, I will receive 404 not found.
Any help would be very appreciated ~!

Each path in an Ingress is required to have a corresponding path type. Paths that do not include an explicit pathType will fail validation.
The only supported wildcard character for the path field of an Ingress is the * character. The * character must follow a forward slash (/) and must be the last character in the pattern.
For example, /, /foo/, and /foo/bar/* are valid patterns, but , /foo/bar, and /foo//bar are not.A more specific pattern takes precedence over a less specific pattern. If you have both /foo/ and /foo/bar/, then /foo/bar/bat is taken to match /foo/bar/.For more information about path limitations and pattern matching, see the URL Maps documentation.
Suggestion : Try by mentioning the path as /api/*
Additional reference doc :
https://github.com/kubernetes/ingress-nginx/issues/1120#issuecomment-322007251
https://cloud.google.com/kubernetes-engine/docs/concepts/ingress#multiple_backend_services

Related

K8s nginx ingress path return 404

I am trying to setup my services inside nginx ingress. In this case, I want to have a mail service along the path test.io/mail/. I make a request to test.io/mail/send_mail/ the response comes 404.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
namespace: test-backend
spec:
ingressClassName: nginx
rules:
- host: test.io
http:
paths:
- path: /mail/
pathType: Exact
backend:
service:
name: email-test
port:
number: 80
If I change the settings and remove the prefix, then everything will work.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
namespace: test-backend
spec:
ingressClassName: nginx
rules:
- host: test.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: email-test
port:
number: 80
How do I properly set the prefix?
Please have ingress yaml as below to use "pathType: Prefix" instead of "pathType: Exact"
rules:
- host: test.io
http:
paths:
- path: /mail/
pathType: Prefix
Please refer official documentation for more details for
pathType: Prefix
Each path in an Ingress is required to have a corresponding path type. Paths that do not include an explicit pathType will fail validation.
The only supported wildcard character for the path field of an Ingress is the * character. The * character must follow a forward slash (/) and must be the last character in the pattern. For example, /, /foo/, and /foo/bar/* are valid patterns, but , /foo/bar, and /foo//bar are not.A more specific pattern takes precedence over a less specific pattern. If you have both /foo/ and /foo/bar/, then /foo/bar/bat is taken to match /foo/bar/.For more information about path limitations and pattern matching, see the URL Maps documentation.
Suggestion :
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
namespace: test-backend
spec:
ingressClassName: nginx
rules:
- host: test.io
http:
paths:
- path: /mail/*
pathType: Exact
backend:
service:
name: email-test
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: email-test
port:
number: 80
Additional reference doc :
Refer1
Refer2

Handle overlapping ingress paths

I have one ingress which handles all requests to my-domain.example.com/api and forwards them to backend-service/api:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: default-backend
spec:
ingressClassName: nginx
rules:
- host: my-domain.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 8080
Now I added this second ingress, which should only handle the sub-path my-domain.example.com/api/log and forward it to logger-service/api:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: logger
annotations:
ingress.kubernetes.io/rewrite-target: /log/$2
spec:
ingressClassName: nginx
rules:
- host: my-domain.example.com
http:
paths:
- path: /api/log(/|$)(.*)
pathType: Prefix
backend:
service:
name: logger-service
port:
number: 8080
But this doesn't work reliable. The log of the ingress controller tells me, that the request to my-domain.example.com/api/log was forwarded to the backend-service. I guess the overlapping paths make it unpredictable.
How do I have to configure my ingresses if I have one service which should handle all defaults (/api/) and some other services which should handle specific sub-paths (/api/log)?
Each path in an Ingress is required to have a corresponding path type. Paths that do not include an explicit pathType will fail validation.
The only supported wildcard character for the path field of an Ingress is the * character. The * character must follow a forward slash (/) and must be the last character in the pattern. For example, /, /foo/, and /foo/bar/* are valid patterns, but , /foo/bar, and /foo//bar are not.A more specific pattern takes precedence over a less specific pattern. If you have both /foo/ and /foo/bar/, then /foo/bar/bat is taken to match /foo/bar/.For more information about path limitations and pattern matching, see the URL Maps documentation.
Suggestion : For second ingress try by mentioning the path as /api/log .*
As shown below
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/rewrite-target: /
name: logger
spec:
ingressClassName: nginx
rules:
-
host: my-domain.example.com
http:
paths:
-
backend:
service:
name: logger-service
port:
number: 8080
path: /api/log*
pathType: Prefix
Above YAML is working, find below Screenshot :
Additional reference doc :
Link1
Link2

Kubernetes Multiple Path Rewrites

Alright, various permutations of this question have been asked and I feel terrible asking; I'm throwing the towel in and was curious if anyone could point me in the right direction (or point out where I'm wrong). I went ahead and tried a number of examples from the docs, but to no avail (see below).
I'm trying to route traffic to the appropriate location under Kubernetes using an Ingress controller.
Server Setup
I have a server, myserver.com and three services running at:
myserver.com/services/
myserver.com/services/service_1/
myserver.com/services/service_2/
Note that I'm not doing anything (purposefully) to myserver.com/.
At each of the three locations, there's a webapp running. For example, myserver.com/services/service_2 needs to load css files at myserver.com/services/service_2/static/css, etc...
Kubernetes Ingress
To manage the networking, I'm using a Kubernetes Ingress controller, which I've defined below. The CORS annotations aren't super relevant, but I've included them to clear up any confusion.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myServices
namespace: myServices
annotations:
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-origin: '$http_origin'
nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- myserver.com
rules:
- host: myserver.com
http:
paths:
- path: /services
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
- path: /services/service_1(/|$)
pathType: Prefix
backend:
service:
name: web-service-1
port:
number: 80
- path: /services/service_2(/|$)
pathType: Prefix
backend:
service:
name: web-service-2
port:
number: 80
Targets
I noticed that one helpful thing to do is give some path examples. From the examples below it looks like the paths aren't that complicated. I think this is what I'm after. Note that I'd like each service to be able to resolve its css and image files.
myserver.com/services -> myserver.com/services
myserver.com/services/xxx/xxx -> myserver.com/services/xxx/xxx
myserver.com/services/service_1 -> myserver.com/services/service_1
myserver.com/services/service_1/xxx/xxx -> myserver.com/services/service_1/xxx/xxx
myserver.com/services/service_2/xxx/xxx -> myserver.com/services/service_2/xxx/xxx
Attempts
I know that this issue has to do a lot with the nginx.ingress.kubernetes.io/rewrite-target rule and its interaction with the paths I've defined.
I know that I don't want nginx.ingress.kubernetes.io/rewrite-target: $1 because that gives a 500 when visiting myserver.com/services
I know that I don't want nginx.ingress.kubernetes.io/rewrite-target: $1/$2 because when I visit myserver.com/services/service_1 I actually get part of the content at myserver.com/services rendered on the page.
SO Attempt 1
I also attempted to replicate the accepted solution from this question.
In this attempt I set
nginx.ingress.kubernetes.io/rewrite-target: "/$1" and one of the service paths to
- path: /(services/service_1(?:/|$).*)
When I visit myserver.com/services/service_1/xyz, the HTML from myserver.com/services/service_1 gets rendered.
Concluding Thoughts
Something ain't quite right with the path rewrite and paths rules. Any suggestions?
The problem you reported in your most recent comment is resolved by looking at the rewrite example in the nginx-ingress documentation.
The rewrite-target annotation configures the ingress such that matching paths will be rewritten to that value. Since you've specified a static value of /, anything matching your ingress rules will get rewritten to /, which is exactly the behavior you're seeing.
The solution is to capture the portion of the path we care about, and then use that in the rewrite-target annotation. For example:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myservices
annotations:
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-origin: '$http_origin'
nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
ingressClassName: nginx
rules:
- host: myserver.com
http:
paths:
- path: /services/service_1(/|$)(.*)
pathType: Prefix
backend:
service:
name: webservice-service1
port:
number: 80
- path: /services/service_2(/|$)(.*)
pathType: Prefix
backend:
service:
name: webservice-service2
port:
number: 80
- path: /services(/|$)(.*)
pathType: Prefix
backend:
service:
name: webservice
port:
number: 80
Here, we've modified the match expression so that they look like:
- path: /services/service_1(/|$)(.*)
The second capture group (.*) captures everything after the path
portion that matches literally. We then use that capture group ($2,
because it's the second group) in the rewrite-target annotation:
nginx.ingress.kubernetes.io/rewrite-target: /$2
With this configuration in place, a request to /services/service_2
results in:
This is service2.
But a request to /services/service_2/foo/bar results in:
<html><head><title>404 Not Found</title></head><body>
<h1>Not Found</h1>
The URL you requested (/foo/bar) was not found.
<hr>
</body></html>
And looking at the backend server logs, we see:
10.42.0.32 - - [21/Jan/2022:20:33:23 +0000] "GET / HTTP/1.1" 200 211 "" "curl/7.79.1"
10.42.0.32 - - [21/Jan/2022:20:33:45 +0000] "GET /foo/bar HTTP/1.1" 404 311 "" "curl/7.79.1"
I've updated my example repository to match this configuration.

ISTIO HTTPS-HTTP 404 NR route_not_found

I am trying to configure TLS termination via Istio HTTPS -> HTTP.
HTTP 80 works fine.
HTTPS 443 works only for / path.
HTTP 200:
curl https://serviceA.example.com
HTTP 404:
curl https://serviceA.example.com/blabla
Istio access logs:
GET /blabla HTTP/2" 404 NR route_not_found
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: serviceA-gateway
namespace: default
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: HTTP
protocol: HTTP
hosts:
- "serviceA.example.com"
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: serviceA.example.com
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: serviceA-swearl
namespace: default
spec:
hosts:
- serviceA.example.com
gateways:
- serviceA-gateway
HTTP:
- route:
- destination:
host: serviceA.default.svc.cluster.local
port:
number: 80
I am not sure what I did wrong. By looking at the docs everything should be working. Setup is ISTIO operator on AWS EKS with NLB.
Also, I have a certificate - secret in the istio-system namespace.
Service and Deployment have required labels.
FIX: The issue was that I had on Ingress definition
pathType: ImplementationSpecific
It should be:
pathType: Prefix
Configure Ingress pathType ImplementationSpecific behavior #26883
Community wiki answer for better visibility.
As the OP mentioned in the question, problem is solved by setting
pathType: Prefix
in the ingress.
Original message:
FIX: The issue was that I had on Ingress definition
pathType: ImplementationSpecific
It should be pathType: Prefix
https://github.com/istio/istio/issues/26883
You can find an explanation in this official documentation:
Each path in an Ingress is required to have a corresponding path type. Paths that do not include an explicit pathType will fail validation. There are three supported path types:
ImplementationSpecific: With this path type, matching is up to the IngressClass. Implementations can treat this as a separate pathType or treat it identically to Prefix or Exact path types.
Exact: Matches the URL path exactly and with case sensitivity.
Prefix: Matches based on a URL path prefix split by /. Matching is case sensitive and done on a path element by element basis. A path element refers to the list of labels in the path split by the / separator. A request is a match for path p if every p is an element-wise prefix of p of the request path.

routing wildcard domain to different paths in nginx ingress

how do I achieve something similar to this.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
ingress.kubernetes.io/rewrite-target: /c/{word-maching-wildcard}
name: some-route
namespace: prod
spec:
rules:
- host: "*.example.com" # {hello}.example.com -> hello.example.com/hello
http:
paths:
- backend:
serviceName: svc
servicePort: 8080
path: /
Is there any way to capture the matching word in the subdomain and append it in the path before routing to the upstream service.
From the official doc:
Regular expressions and wild cards are not supported in the
spec.rules.host field. Full hostnames must be used.
See: nginx-ingress-matching.
However I have found similar problem which advice to write your own controller that writes out an nginx config that uses $http_host in the appropriate proxy_pass or redirect lines.
Read more: wildcard-url-mapping.

Resources