Capturing groups in istio - istio

How can I use a part of matched URI in destination rule in istio?
Trying to achieve something like this:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
spec:
http:
- match:
- uri:
regex: "^/foo/(.+)/?$"
route:
- destination:
host: bar-$1
port:
number: 80

As far as I know this is not possible at all.
Cant provide you any confirmation link on this.
The similar question was here in the past:
can istio support route to different service by dynamic part of uri path

Related

Istio: Multiple query parameters with the same key

I'd like to re-direct queries with Istio based on the query parameter ?key=value. That works with the following VirtualService (with re-directing replaced by returning a different string):
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- "httpbin.example.com"
gateways:
- httpbin-gateway
http:
- match:
- queryParams:
key:
exact: "value"
directResponse:
status: 301
body:
string: "redirected"
- directResponse:
status: 200
body:
string: "standard"
The issue is that sometimes I get the query ?key=foo&key=value for which I would also like to match and re-direct, but that doesn't work, the first "key" matches, the value param "foo" doesn't, so the whole match fails. Is there a way to match one of multiple keys with different values? "value" is fixed, "foo" can also be something else, except it is not "value".

Istio VirtualService rule with header and url matching

How could I write rule for my VirtuelService such that traffic with url "/v1/myservice" and header "x-client-id: test" should route to "my-service-v2-dev", otherwise traffic with url "/v1/myservice" and with any header should route to "my-service-dev"
Below is my code which is not working as expected and all traffic is going to "my-service-v2-dev".
Can anybody help me and let me know what mistake I am doing here?
Thanks In advance.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: my-public-dev
namespace: my-dev
spec:
gateways:
- my-public-dev
hosts:
- my-public-dev.com.digital
http:
- match:
- headers:
x-client-id:
exact: test
- uri:
prefix: /v1/myservice/
name: myservice-v2-route
route:
- destination:
host: my-service-v2-dev
port:
number: 8080
- match:
- uri:
prefix: /v1/myservice/
name: myservice-v1-route
route:
- destination:
host: my-service-dev
port:
number: 8888
The match in the first route means you have a list of two conditions, combined with logical OR.. If you want to use AND, you have to move that to one condition, which can include a header and a uri condition and is combined with AND.
See
https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPMatchRequest
https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPRoute
(in response to comments)
For more complex matching, you can split the conditions with logical operations, e.g. first match url1 AND header, second match, url2 AND header, third url1, fourth url2, fifth catch all for the rest.
Or match the urls with a Regex, so multiple URLs could also be represented by that Regex.

Nginx Ingress rules not find a match

I'm struggling on an Ingress configuration in yaml because the pattern matching seems not to work.
I would like the frontend-lb ClusterIP Service for the frontend deployment to respond to any of these:
https://example.com
https://example.com/home
https://example.com/login
... any other without /api/
And the backend-lb ClusterIP Service for the backend deployment to respond to any of these:
https://example.com/api/...
The yaml for the ingress rules is the following:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls:
- secretName: example-com-tls
hosts:
- example.com
rules:
- host: example.com
http:
paths:
- path: /api
backend:
serviceName: gateway-lb
servicePort: 80
- path: /
backend:
serviceName: frontend-lb
servicePort: 80
The result is that every backend url is recognized as a frontend url and gives back 404 Not Found.
I've tried many other regexp and also I've tried to exclude /api in the frontend path (!?api).* but with no success.
UPDATE:
What I see with the logs it's like in the backend the url path gets blanked because if I call:
https://example.com/api/javalin-api-gateway/login
I get this error:
Not found. Request is below context-path (context-path:
'/javalin-api-gateway')
While when I call the frontend with a specific url path:
https://example.com/home
The /home controller is effectively called (it doesn't get blanked).
If I call the backend service directly (if the service is a LoadBalancer) with the same url:
http://192.168.64.17:31186/javalin-api-gateway/login
I get the right response, signal that the backend part is working properly.
How is possible that only the backend service doesn't receive the complete path?

Hosting django on aks behind nginx-ingress

I am trying to host a django website on Azure kubernetes service behide nginx-ingress, and I would like my django web show under a path.
e.g. when access the default admin site, I would like to access it at http://example.com/django/admin instead of http://example.com/admin
I tried the configure below, when I access http://example.com/django/admin it will forward me to http://example.com/admin and show me 404 error from default ingress backend, as I set django debug to ture I assume this mean ingress did not send my request to my django service
# path example
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- backend:
serviceName: django-service
servicePort: 80
path: /django(/|$)(.*)
so I try to curl -I -k http://example.com/django/admin, and it show something like below
HTTP/1.1 301 Moved Permanently
Server: openresty/1.15.8.2
Date: Wed, 06 Nov 2019 04:14:14 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Location: /admin/
The same thing happen to any valid page in the site, if I curl -I -k http://example.com/django/any_valid_page it show below:
HTTP/1.1 301 Moved Permanently
Server: openresty/1.15.8.2
Date: Wed, 06 Nov 2019 04:14:14 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Location: /any_valid_page/
I wonder it is caused by I am doing the test with the default django development web server? (i.e. python manage.py runserver)
If I try to host it at root like below, everything is fine...
# root example
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- backend:
serviceName: django-service
servicePort: 80
path: /
Trying adding this
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
ingress.kubernetes.io/rewrite-target: /django
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- backend:
serviceName: django-service
servicePort: 80
path: /django
Starting in Version 0.22.0, ingress definitions using the annotation
nginx.ingress.kubernetes.io/rewrite-target are not backwards
compatible with previous versions. In Version 0.22.0 and beyond, any
substrings within the request URI that need to be passed to the
rewritten path must explicitly be defined in a capture group. So make
sure you have right version.
When using SSL offloading outside of cluster it may be useful to enforce a redirect to HTTPS even when there is no TLS certificate available. This can be achieved by using the nginx.ingress.kubernetes.io/force-ssl-redirect: "true" annotation in the particular resource.
I think your Ingress configuration file should look like this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: example.com
http:
paths:
- path: /django(/|$)(.*)
backend:
serviceName: django-service
servicePort: 80
If you get 404 error, there is possible solution:
Please change https to http in the curl command?:
curl --resolve your-host:80:xx.xxx.xx.xxx http://my-host:80
To get the IP from kubectl get ing command, it is necessary to
enable the reporting Ingress status feature. Take a look on: reporting-ingress-status.
There is the default server in the Ingress controller. It returns
the Not Found page with the 404 status code for all requests for
domains for which there are no Ingress rules defined. Those requests
are not shown in the access log.
Since you're getting a 404, this means that the host header of your
requests doesn't match with the host field in the Ingress resource.
To set the host header in curl, please see previous curl
commands. Optionally, you can also do:
curl http://<ip> -H "host: example.com"
Please take a look on ngnix-ingress, server-side-https-enforcement-nginx.
This is a problem from Django's side. Whenever the admin is not logged in, the /django/admin results in a redirect to /admin/. In this case, if you just replace /django/admin/ with /django/admin/ in the browser URL field it will work and open django admin login.
So basically Django's built-in redirect conflicts with the Ingress's rewrite module.

How to configure nginx.ingress.kubernetes.io/rewrite-target and spec.rules.http.paths.path to satisfy the following URI patterns

How can I configure nginx.ingress.kubernetes.io/rewrite-target and spec.rules.http.paths.path to satisfy the following URI patterns?
/aa/bb-aa/coolapp
/aa/bb-aa/coolapp/cc
Legend:
a = Any letter between a-z. Lowercase. Exactly 2 letters - no more,
no less.
b = Any letter between a-z. Lowercase. Exactly 2 letters - no more, no less.
c = any valid URI character. Lowercase. Of variable length - think slug.
Example URI:s that should match the above pattern:
/us/en-us/coolapp
/us/en-us/coolapp/faq
/us/en-us/coolapp/privacy-policy
Attention
Starting in Version 0.22.0, ingress definitions using the annotation nginx.ingress.kubernetes.io/rewrite-target are not backwards compatible with previous versions. In Version 0.22.0 and beyond, any substrings within the request URI that need to be passed to the rewritten path must explicitly be defined in a capture group.
Note
Captured groups are saved in numbered placeholders, chronologically, in the form $1, $2 ... $n. These placeholders can be used as parameters in the rewrite-target annotation.
References:
https://kubernetes.github.io/ingress-nginx/examples/rewrite/
https://github.com/kubernetes/ingress-nginx/pull/3174
The nginx.ingress.kubernetes.io/rewrite-target annotation is used to indicate the target URI where the traffic must be redirected. As per how I understand your question, you only want to match the URI patterns that you specified without redirecting the traffic. In order to achieve this, you can set the nginx.ingress.kubernetes.io/use-regex annotation to true, thus enabling regular expressions in the spec.rules.http.paths.path field.
Let's now take a look at the regex that you will need to match your URI patterns.
First of all, the regex engine used by ingress-nginx doesn't support backreferences, therefore a regex like this one will not work. This is not a problem as you can match the /aa-bb/aa part without forcing the two aas to be equal since you will —presumably— still have to check the correctness of the URI later on in your service (e.g /us/en-us may be accepted whereas /ab/cd-ab may not).
You can use this regex to match the specified URI patterns:
/[a-z]{2}/[a-z]{2}-[a-z]{2}/coolapp(/.*)?
If you want to only match URL slugs in the cc part of the pattern you specified, you can use this regex instead:
/[a-z]{2}/[a-z]{2}-[a-z]{2}/coolapp(/[a-z0-9]+([a-z0-9]+)*)?
Lastly, as the nginx.ingress.kubernetes.io/use-regex enforces case insensitive regex, using [A-Z] instead of [a-z] would lead to the same result.
Follows an ingress example definition using the use-regex annotation:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-regex
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
rules:
- host: test.com
http:
paths:
- path: /[a-z]{2}/[a-z]{2}-[a-z]{2}/coolapp(/.*)?
backend:
serviceName: test
servicePort: 80
You can find more information about Ingress Path Matching in the official user guide.
Came up with the following configuration - it is working for all of my test routes / requirements so far.
The regex is almost the same as the one posted by #Gilgames.
I based mine on the official docs rewrite example: https://kubernetes.github.io/ingress-nginx/examples/rewrite/#rewrite-target
Apart from that I took a quick course at https://www.regular-expressions.info/ 😁
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ing
namespace: some-ns
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$1/$2-$3/$5
certmanager.k8s.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "INGRESSCOOKIE"
nginx.ingress.kubernetes.io/session-cookie-path: /
nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($host = 'example.com')
{
rewrite ^ https://www.example.com$request_uri permanent;
}
spec:
tls:
- hosts:
- www.example.com
- example.com
secretName: tls-secret-test
rules:
- host: www.example.com
http:
paths:
- path: /([a-z]{2})/([a-z]{2})-([a-z]{2})/coolapp(/|$)(.*)
backend:
serviceName: coolapp-svc
servicePort: 80
- host: example.com
http:
paths:
- path: /([a-z]{2})/([a-z]{2})-([a-z]{2})/coolapp(/|$)(.*)
backend:
serviceName: coolapp-svc
servicePort: 80