Kuberentes Ingress rate lmiting altering rewrite-target base - nginx

I have an Ingress file which contains only one annotation:
---
apiVersion: "extensions/v1beta1"
kind: "Ingress"
metadata:
name: "logging-microservice-ingress"
namespace: "000000001"
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
-
host: "ms-shared-nad.techmahindra.com"
http:
paths:
-
backend:
serviceName: "logging-microservice-000000001"
servicePort: 3000
path: "/logging-microservice"
When I call https://example.com/logging-microservice/logs backend service generates,
GET /logs --> 200
When I added two more annotations like
nginx.ingress.kubernetes.io/limit-connections: 1
nginx.ingress.kubernetes.io/limit-rps: 1
backend gives
GET /logging-microservice/logs --> 404
I don't have access to see the actual nginx configuration being generated there. But, wondering how applying rate limiting can alter the rewrite base.
I have tried rate limiting in open source nginx server and it works as expected. What could be the change to remove path from the url which is passed to the upstream api backend?
Update :
I have accessed the cluster and record the changes happening:
location ~* "^/logging-microservice" {
rewrite "(?i)/logging-microservice" / break;
}
location ~* "^/" {
}
is changing to
location /logging-microservice {
}
location / {
}
when rate limiting annotations are added.

Even if you were given with Ingress.yaml with
nginx.ingress.kubernetes.io/rewrite-target: /
and everything was working fine, you need it like
nginx.ingress.kubernetes.io/rewrite-target: "/"
Other thing is, order of annotations matters;
it can't be :
nginx.ingress.kubernetes.io/rewrite-target: "/"
nginx.ingress.kubernetes.io/limit-connections: "1"
nginx.ingress.kubernetes.io/limit-rps: "1"
it should be :
nginx.ingress.kubernetes.io/limit-connections: "1"
nginx.ingress.kubernetes.io/limit-rps: "1"
nginx.ingress.kubernetes.io/rewrite-target: "/"
learned the hard way!

Related

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

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

ingress-nginx: How to insert access_by_lua_block{} only for specific location?

I have multiple location blocks under a single host:, something like this:
apiVersion: networking.k8s.io/v1
kind: ingress
metadata:
name: ingress-nginx
annotations:
kubernetes.io/ingress.calass: nginx
ngnx.ingress.kubernetes.io/use-regex: "true"
ngnx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: ingress.mydomain.org.local
http:
paths:
- path: /app1(/|$)(.*)
pathType: Prefix
backend:
service:
name: app1-service
port:
number: 5678
- path: /api(/|$)(.*)
pathType: Prefix
backend:
service:
name: api-service
port:
number: 5678
I need to insert access_by_lua_block{...} only for one location, say: /api - how do I do that? I tried with ngnx.ingress.kubernetes.io\configuration-snippet, like this:
annotations:
ngnx.ingress.kubernetes.io\configuration-snippet: |
location ~* "^/api(/|$)(.*)" {
access_by_lua_block {
....
}
}
but that's just adding a nested location ~* "^/api(/|$)(.*)" block under every other location entries. Is there any way that can be achieved?
As suggested by #mdaniel ,the answer is always to create a 2nd Ingress resource when you need to apply annotations to just one of them.
The smooth coexistence of multiple Ingress Controllers in one cluster is provided by the Ingress class concept, which mandates the following:
Every Ingress Controller must only handle Ingress resources for its particular class.
Ingress resources should have the ingressClassName field set to the value, which corresponds to the class of the Ingress Controller the user wants to use.
VirtualServer, VirtualServerRoute, Policy and TransportServer resources should have the ingressClassName field set to the value, which corresponds to the class of the Ingress Controller the user wants to use.
Refer to this documentation for more information on Ingress resources.

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.

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.

basic auth / ip filter only for path that contain special character in Ingress NGINX

I want my Ingress (NGINX) to filter by source IP address and show a basic auth before proxying to a service. While this is straightforward, the complicated part is, that I want it to do this only, if the URL contains a special character in the path.
Lets say I want to secure all paths that start with a "+" before proxying them to the correct service. On the other hand I still want that paths that do not start with a "+" will be routed (without basic auth) to the same service. It should also not change the URL that the service will see.
Examples would be:
/serviceA/what/ever -> http://192.168.0.2/what/ever
/serviceA/what/+ever -> BASIC_AUTH -> http://192.168.0.2/what/+ever
/serviceB/what/ever -> http://192.168.0.3/what/ever
/serviceB/+what/ever -> BASIC_AUTH -> http://192.168.0.3/+what/ever
Is it possible to achieve this either in Ingress or at least in a NGINX config?
The regex for the URL path is also quite simple in NGINX but is it possible without duplicating all path entries and also without adding a second proxy nginx in front?
The ideal solution would be in Ingress yml config but I'm more familar with NGINX, so here is an example what I want to achieve in NGINX-Syntax:
Location ~ /+ {
auth_basic ...;
auth_basic_user_file ...;
< route it somehow to the similar location as it would have no +, but don't cut out the + >
}
Location /serviceA {
proxy_pass ...;
}
... more Locations ...
Or in Ingress something similar with path-entries.
First of all, your:
location ~ /+ {
auth_basic ...;
auth_basic_user_file ...;
< route it somehow to the similar location as it would have no +, but don't cut out the + >
}
Would only match servicex/+something , not the servicex/something/+nice
The regex you are searching is something like:
location ~ ^/(.*)\+(.*) for the "+" to be anywhere
location ~ ^(.*)\/\+(.*) for the "+" to be only after a "/"
For the part:
< route it somehow to the similar location as it would have no +, but don't cut out the + >
Like this you'll send the uri exactly like it came:
proxy_pass http://192.168.0.2$request_uri;
And like this you'd take out the "+"
proxy_pass http://192.168.0.2$1/$2;
Where $1 is the (.*) before the /+ and $2 is everything after, and we add the lacking / in the middle.
I think that will help you on what I think you want to do.
If you have any questions ask them, I feel I'm lost at some part of your explanation and my answer is not 100% on point.
Hope I helped.
You can use nginx regexes in yaml path fields.
To have auth in one and not in the other, you have to create 2 ingresses (since the auth annotations are per-ingress, not per-path). Having 2 ingresses for the same host is perfectly OK, nginx-ingress will merge them.
Something like this should work:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: without-auth
spec:
rules:
- host: example.com
http:
paths:
- path: /
backend:
serviceName: your-backend-service
servicePort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: with-auth
annotations:
# type of authentication
nginx.ingress.kubernetes.io/auth-type: basic
# name of the secret that contains the user/password definitions
nginx.ingress.kubernetes.io/auth-secret: basic-auth
# message to display with an appropriate context why the authentication is required
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
rules:
- host: example.com
http:
paths:
- path: ^/+
backend:
serviceName: your-backend-service
servicePort: 80
To debug if it doesn't work, or to check the result matches your expectations, you can view the generated nginx config like this (replace namespace and pod name with yours).
kubectl -n nginx-ingress exec nginx-ingress-controller-76458655df-sddg2 -- cat /etc/nginx/nginx.conf

Resources