Regex path matching for Ingress-Nginx - nginx

I am working with ingress-nginx in kubernetes to set up a server.
The issue is that the paths are not routing at all and I get a 404 error from the nginx server on any request I make.
Below is my code for ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-srv
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
# defaultBackend:
# service:
# name: auth-srv
# port:
# number: 3000
rules:
- host: app.dev
- http:
paths:
- pathType: Prefix
path: /api/auth/?(.*)
backend:
service:
name: auth-srv
port:
number: 3000
- path: /api/coms/?(.*)
pathType: Prefix
backend:
service:
name: coms-srv
port:
number: 3000
If I uncomment the default backend service I get a response but as soon as I remove it I get the 404 nginx error. So I know its connecting to the services I set.
I don't know where I'm going wrong how to go about fixing this as I'm copying straight from the docs. Any help or insight would be great. Thank you in advance!
Edit 1: I removed the regex from the path and commented out the /api/auth path so no requests should be going to the auth-srv. For some reason, all requests route to the auth-srv even though there is no mapping to it. NOTE: Both the auth and coms pods/services are running in the background, just ingress-nginx still isn't routing properly.

So the reason why this wasn't routing properly was because of the:
- host: app.dev
- http:
The "-" in front of the "http" made the controller think it was its own ruleset so the following routes had a host of "*". After I Removed the "-" in front of the "http", the rules were set to the proper host of app.dev and it started routing accordingly.
Thank you for your help everyone! What a long day it has been :')

Related

Sub-subdomain with nginx ingress on Kubernetes (GKE)

I have a domain at Cloudflare and some wildcards for subdomains
which both point to the load balancer of an nginx ingress of a Kubernetes cluster (GKE) of the GCP. Now, we have two pods and services running each (echo1 and echo2, which are essentially identical) and when I apply an ingress
kind: Ingress
metadata:
name: echo-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- host: "echo1.eu3.example.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: echo1
port:
number: 80
- host: "echo2.example.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: echo2
port:
number: 80
I can reach echo2 under echo2.example.com, but not echo1.eu3.example.com. My question is how I can make the second one reachable as well.
I can advise you to make some check.
Just set the Proxy status for "echo1.eu3.example.com" as DNS only. Then check the access. If ok - install certificates in kubernetes via cert manager. We faced some times with this issue and resolved by using 3 deep domains. For instance "echo1-eu3.example.com". It seems cloudfront does not like such domains :) Of course if someone write a solution how to work with deep domains in cloudfront - it would be good practice for us :)

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.

CSRF cookie not set - possibly blocked by kubernetes ingress

I use Axios in React and Django Rest Framework with dj-rest-auth. After migrating from GCP to Azure and removing the unmaintained django-rest-auth, I got some new CSRF issues.
Initially I removed django-rest-auth and created my own LoginView from Django.contrib.auth.views. Noticed that this also gave the CSRF error in development. So I added dj-rest-auth, which solved my issue locally. Pushed to the AKS, but there the cookie still does not appear. I'm suspecting my ingress to be the problem, which is able to set INGRESSCOOKIE for both my backend and frontend, but no CSRF.
I know there's a million tickets about this topic, my Django settings are fine, the set-cookie resopnse header is set. Also use the right axios settings to make sure that if the cookie is there, it's used for requests. The problem is really with the fact that the set-cookie is not coming through, as it is being created in Django.
I use an Nginx controller with TLS on a static IP with and have my ingress defined as follows:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: basic-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rewrite-target: /
kubernetes.io/ingress.allow-http: "true"
spec:
tls:
- hosts:
- XXXXX
secretName: tls-secret
rules:
- host: XXXXXXXX
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
- path: /api/
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
The problems was indeed at the ingress, not so much with the settings, but with the routing. The routing used above, rewrites the targets to / for all requests. My backend actually has a page there, which is CSRF protected. It went to that page instead of the login page. Changing the ingress to below yielded better results.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: basic-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rewrite-target: /$1
kubernetes.io/ingress.allow-http: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- XXXXXXXXXX
secretName: tls-secret
rules:
- host: XXXXXX
http:
paths:
- path: /?(.*)
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
- path: /api/?(.*)
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080

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.

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