Kubernetes rewrite-target paths for appending path to match - kubernetes

I'm using the OSS ingress-nginx Ingress controller and trying to create a rewrite-target rule such that I can append a path string before my string match.
If I wanted to create a rewrite rule with regex that matches /matched/path and rewrites that to /prefix/matched/path, how might I be able to do that?
I've tried something like the following but it's no good, and I'm just confused about the syntax of this ingress definition:
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- path: /(/prefix/)(/|$)(/matched/path)(.*)
backend:
serviceName: webapp1

If I wanted to create a rewrite rule with regex that matches
/matched/path and rewrites that to /prefix/matched/path, how might
I be able to do that?
In order to achieve this you have add /prefix into your rewrite-target.
Here's a working example with ingress syntax from k8s v1.18:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-ingress-v118
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /prefix/$1
spec:
rules:
http:
paths:
- path: /(matched/path/?.*)
backend:
serviceName: test
servicePort: 80
Since the syntax for the new ingress changed in 1.19 (see release notes and some small info at the end) I`m placing also an example with it:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress-v119
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /prefix/$1
spec:
rules:
- http:
paths:
- path: /(matched/path/?.*)
pathType: Prefix
backend:
service:
name: test
port:
number: 80
Here is a test with http echo server:
➜ ~ curl 172.17.0.4/matched/path
{
"path": "/prefix/matched/path",
"headers": {
"host": "172.17.0.4",
"x-request-id": "011585443ebc6adcf913db1c506abbe6",
"x-real-ip": "172.17.0.1",
"x-forwarded-for": "172.17.0.1",
"x-forwarded-host": "172.17.0.4",
"x-forwarded-port": "80",
"x-forwarded-proto": "http",
"x-scheme": "http",
"user-agent": "curl/7.52.1",
"accept": "*/*"
},
This rule will also ignore the / at the end of the request:
➜ ~ curl 172.17.0.4/matched/path/
{
"path": "/prefix/matched/path/",
"headers": {
"host": "172.17.0.4",
"x-request-id": "0575e9022d814ba07457395f78dbe0fb",
"x-real-ip": "172.17.0.1",
"x-forwarded-for": "172.17.0.1",
"x-forwarded-host": "172.17.0.4",
"x-forwarded-port": "80",
"x-forwarded-proto": "http",
"x-scheme": "http",
"user-agent": "curl/7.52.1",
"accept": "*/*"
},
Worth to mention some notable differences/changes in the new ingress syntax:
spec.backend -> spec.defaultBackend
serviceName -> service.name
servicePort -> service.port.name (for string values)
servicePort -> service.port.number (for numeric values) pathType no longer has a default value in v1; "Exact", "Prefix", or
"ImplementationSpecific" must be specified Other Ingress API updates
backends can now be resource or service backends
path is no longer required to be a valid regular expression (#89778,
#cmluciano) [SIG API Machinery, Apps, CLI, Network and Testing]

Related

access forbidden by rule, on ingress nginx log

which ingress rule is blocking the request coming in.
app : based on drupal.
any suggestions would help.
ingress log :
2022/08/11 10:00:59 [error] 20516#20516: *159406145 access forbidden by rule, client: 2a02:9b0:3d:54a2:40bf:a951:e203:79a5, server: example.com, request: "GET / HTTP/1.1", host: "example.com"
{"time": "2022-08-11T10:00:59+00:00", "remote_addr": "-", "x_forward_for": "2a02:9b0:3d:54a2:40bf:a951:e203:79a5, 172.70.156.137, 2a02:9b0:3d:54a2:40bf:a951:e203:79a5", "request_id": "d8d07bd09c84b802af91a60adbf46a73", "remote_user": "-", "bytes_sent": 583, "request_time": 0.000, "status": 403, "vhost": "example.com", "request_proto": "HTTP/1.1", "path": "/", "request_query": "-", "request_length": 647, "duration": 0.000,"method": "GET", "http_referrer": "-", "http_user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Mobile/15E148 Safari/604.1", "auth_apikey": "-", "auth_authorization": "-", "auth_X-Api-caps": "-", "auth_uid": "-"}
ingress.yaml
apiVersion: v1
items:
- apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
meta.helm.sh/release-name: example-production
meta.helm.sh/release-namespace: example
nginx.ingress.kubernetes.io/configuration-snippet: |
more_clear_headers "Server";
more_set_headers "X-Content-Type-Options: nosniff";
more_set_headers "X-XSS-Protection: 1";
more_set_headers "X-Request-Id: $request_id";
more_set_headers "X-Router-Id: example-portal-anonymous";
more_set_headers "Strict-Transport-Security: max-age=15768000";
more_set_headers "Content-Security-Policy: frame-ancestors 'self'";
nginx.ingress.kubernetes.io/cors-allow-headers: X-Forwarded-For, Authorization
nginx.ingress.kubernetes.io/cors-allow-methods: GET, POST, PUT, PATCH, DELETE,
OPTIONS
nginx.ingress.kubernetes.io/cors-allow-origin: example.com
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/enable-modsecurity: "true"
nginx.ingress.kubernetes.io/enable-owasp-core-rules: "false"
nginx.ingress.kubernetes.io/limit-rpm: "0"
nginx.ingress.kubernetes.io/modsecurity-transaction-id: $request_id
nginx.ingress.kubernetes.io/proxy-buffer-size: 20k
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/whitelist-source-range: 0.0.0.0/0
creationTimestamp: "2022-08-10T11:19:21Z"
generation: 1
labels:
app.kubernetes.io/managed-by: example
name: portal-anonymous
namespace: example
resourceVersion: "523173759"
selfLink: /apis/extensions/v1beta1/namespaces/example/ingresses/portal-anonymous
uid: 9cdb29b2-a463-4851-b4c4-b5a58be28580
spec:
rules:
- host: example.com
http:
paths:
- backend:
serviceName: portal
servicePort: 80
path: /
pathType: Prefix
status:
loadBalancer: {}
kind: List
metadata:
resourceVersion: ""
selfLink: ""
I suspect the problem is actually revealed in your config:
nginx.ingress.kubernetes.io/whitelist-source-range: 0.0.0.0/0
You are allowing all IPv4 IPs with this range, but in your log, you have this:
"x_forward_for": "2a02:9b0:3d:54a2:40bf:a951:e203:79a5, 172.70.156.137, 2a02:9b0:3d:54a2:40bf:a951:e203:79a5"
So this means you're using IPv6 as well, so you might want to add ::/0 to the whitelist-source-range to allow all IPv6 as well as all IPv4 ranges. OR just omit the annotation entirely if you want to allow anything in.

k8s ingress setup - invalid type for io.k8s.api.networking.v1.ServiceBackendPort.number: got "string", expected

I'm trying to setup an ingress in kubernetes for my frontend, the code is as below.
datahub-frontend:
enabled: true
image:
repository: linkedin/datahub-frontend-react
tag: "v0.8.31"
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: instance
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-southeast-2:601628467906:certificate/xxxxxxxxxxxxx
alb.ingress.kubernetes.io/inbound-cidrs: 0.0.0.0/0
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
hosts:
- host: xxxxx.com
redirectPaths:
- path: /*
name: ssl-redirect
port: use-annotation
paths:
- /*
This is giving me the following error.
Error: UPGRADE FAILED: error validating "": error validating data: ValidationError(Ingress.spec.rules[0].http.paths[0].backend.service.port.number): invalid type for io.k8s.api.networking.v1.ServiceBackendPort.number: got "string", expected "integer"
seems like the ingress definition you posted is incorrect. It is missing the required spec and rules according to kubernetes doc.
This contains an example ingress for ssl-redirect
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: default
name: ingress
annotations:
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-west-2:xxxx:certificate/xxxxxx
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/ssl-redirect: '443'
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /users/*
pathType: ImplementationSpecific
backend:
service:
name: user-service
port:
number: 80
- path: /*
pathType: ImplementationSpecific
backend:
service:
name: default-service
port:
number: 80
I see that you are using actions annotation, you need to specify Ingress.spec.rules[0].http.paths[0].backend.service.port.name with use-annotation. Make sure Ingress.spec.rules[0].http.paths[0].backend.service.port.number is not used.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
...
...
alb.ingress.kubernetes.io/actions.my-rule: '{"type":"fixed-response","fixedResponseConfig":{"contentType":"text/plain","statusCode":"200","messageBody":"hello world"}}'
spec:
rules:
- http:
paths:
- path: /*
pathType: ImplementationSpecific
backend:
service:
name: my-rule
port:
name: use-annotation

Setting up kubernets Ingress proxy-body-size based on request method

I've been trying to set up a max body size in the Ingress controller based on the HTTP method of a given path.
Basically the POST method should allow 3m as max size and all the other methods should allow 1m.
Right now my main idea was to do something like:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-custom-service
namespace: development
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
kubernetes.io/ingress.class: "nginx-dev"
nginx.ingress.kubernetes.io/configuration-snippet: |
internal;
rewrite ^ $original_uri break;
nginx.ingress.kubernetes.io/server-snippet: |
location /api/v1/my-endpoint {
if ( $request_method = POST) {
set $target_destination '/_post';
client_max_body_size 3M;
}
if ( $request_method != POST) {
set $target_destination '/_not_post';
client_max_body_size 1M;
}
set $original_uri $uri;
rewrite ^ $target_destination last;
}
spec:
tls:
rules:
- host: my-host.com
http:
paths:
- path: /_post
backend:
serviceName: my-service
servicePort: 8080
- path: /_not_post
backend:
serviceName: my-service
servicePort: 8080
But then I'm getting the following error in the pod:
Is there any way I can correctly set-up the max body size via the ingress controller?
Try changing your annotations with the configuration-snippet
nginx.ingress.kubernetes.io/configuration-snippet: |
location /upload-path {
client_max_body_size 8M;
}
Read more at : https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md#configuration-snippet

access kibana with traefik ingress

I would like to configure access to kibana with ingress.
I'm using Traefik reverse proxy.
I have this configuration, and can't get it to work.
What is the right configuration to make it work?
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
namespace: kube-system
name: rule-001
annotations:
kubernetes.io/ingress.class: traefik
#traefik.ingress.kubernetes.io/rewrite-target: "/app/kibana#/"
traefik.ingress.kubernetes.io/rewrite-target: "/"
#ingress.kubernetes.io/referrer-policy: /
#traefik.ingress.kubernetes.io/request-modifier: "AddPrefix: /app/kibana"
traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip
#traefik.ingress.kubernetes.io/rule-type: PathStrip
#traefik.ingress.kubernetes.io/rule-type: PathPrefix
#traefik.ingress.kubernetes.io/rule-type: Path
traefik.ingress.kubernetes.io/app-root: "/"
traefik.ingress.kubernetes.io/preserve-host: "true"
spec:
rules:
- host:
http:
paths:
- backend:
serviceName: kibana-logging
servicePort: 5601
path: /kibana
#path: /app/kibana
Here the are the access via nodeport logs (works):
{
"type":"response",
"#timestamp":"2020-06-04T11:12:26Z",
"tags":[
],
"pid":6,
"method":"get",
"statusCode":200,
"req":{
"url":"/ui/fonts/inter_ui/Inter-UI-Bold.woff2",
"method":"get",
"headers":{
"host":"3.221.49.216:30346",
"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:77.0) Gecko/20100101 Firefox/77.0",
"accept":"application/font-woff2;q=1.0,application/font-woff;q=0.9,*/*;q=0.8",
"accept-language":"en-US,en;q=0.5",
"accept-encoding":"identity",
"dnt":"1",
"connection":"keep-alive",
"referer":"http://3.221.49.216:30346/app/kibana"
},
"remoteAddress":"172.31.55.73",
"userAgent":"172.31.55.73",
"referer":"http://3.221.49.216:30346/app/kibana"
},
"res":{
"statusCode":200,
"responseTime":240,
"contentLength":9
},
"message":"GET /ui/fonts/inter_ui/Inter-UI-Bold.woff2 200 240ms - 9.0B"
}
Here are the access via ingress logs (wont work):
{
"type":"response",
"#timestamp":"2020-06-04T11:15:29Z",
"tags":[
],
"pid":6,
"method":"get",
"statusCode":200,
"req":{
"url":"/app/kibana",
"method":"get",
"headers":{
"host":"3.221.49.216",
"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 OPR/68.0.3618.125",
"accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-encoding":"gzip, deflate",
"accept-language":"en-GB,en-US;q=0.9,en;q=0.8",
"cache-control":"max-age=0",
"upgrade-insecure-requests":"1",
"x-forwarded-for":"148.71.30.119",
"x-forwarded-host":"3.221.49.216",
"x-forwarded-port":"80",
"x-forwarded-prefix":"/kibana",
"x-forwarded-proto":"http",
"x-forwarded-server":"node1",
"x-real-ip":"148.71.30.119"
},
"remoteAddress":"172.31.55.73",
"userAgent":"172.31.55.73"
},
"res":{
"statusCode":200,
"responseTime":10,
"contentLength":9
},
"message":"GET /app/kibana 200 10ms - 9.0B"
}
Using host configuration in ingress works, but I would like to use the path.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
namespace: kube-system
name: rule-001
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: kibana.host.com
http:
paths:
- backend:
serviceName: kibana-logging
servicePort: 5601
path: /

How to pass the part of request uri as custom header in Kubernetes ingress controller

I've the below configuration in ingress.yaml which forwards the requests with uris like /default/demoservice/health or /custom/demoservice/health to backend demoservice. I would want to retrieve the first part of uri (i.e default or custom in the example above)from the uri and pass as custom header to upstream.
I've deployed the ingress configmap with custom header
X-MyVariable-Path: ${request_uri}
but this sends the full request uri. How can I split?
- path: "/(.*?)/(demoservice.*)$"
backend:
serviceName: demoservice
servicePort: 80
I have found a solution, tested it and it works.
All you need is to add following annotations to your ingress object :
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header X-MyVariable-Path $1;
Where $1 is referencing whatever is captured in first group of regexp expression in path: field.
I've reproduced your scenario using the following yaml:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header X-MyVariable-Path $1;
nginx.ingress.kubernetes.io/use-regex: "true"
name: foo-bar-ingress
spec:
rules:
- http:
paths:
- backend:
serviceName: echo
servicePort: 80
path: /(.*?)/(demoservice.*)$
---
apiVersion: v1
kind: Service
metadata:
labels:
run: echo
name: echo
spec:
ports:
- port: 80
targetPort: 80
selector:
run: echo
---
apiVersion: v1
kind: Pod
metadata:
labels:
run: echo
name: echo
spec:
containers:
- image: mendhak/http-https-echo
imagePullPolicy: Always
name: echo
You can test using curl:
curl -k https://<your_ip>/default/demoservice/healthz
Output:
{
"path": "/default/demoservice/healthz",
"headers": {
"host": "192.168.39.129",
"x-request-id": "dfcc67a80f5b02e6fe6c647c8bf8cdf0",
"x-real-ip": "192.168.39.1",
"x-forwarded-for": "192.168.39.1",
"x-forwarded-host": "192.168.39.129",
"x-forwarded-port": "443",
"x-forwarded-proto": "https",
"x-scheme": "https",
"x-myvariable-path": "default", # your variable here
"user-agent": "curl/7.52.1",
"accept": "*/*"
},
"method": "GET",
"body": "",
"fresh": false,
"hostname": "192.168.39.129",
"ip": "::ffff:172.17.0.4",
"ips": [],
"protocol": "http",
"query": {},
"subdomains": [],
"xhr": false,
"os": {
"hostname": "echo"
}
}
I hope it helps =)
I found two ways to achieve this.
One is by using regular expression in your configmap to parse the first part of request_uri.
To do the above, you need to add nginx.ingress.kubernetes.io/use-regex: "true" annotation to your ingress, before you do so. As this is set to false by default.
Another approach is defining that particular header in the annotation itself and add something like $1 below is the example.
ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-MyVariable-Path: "$1;
Not entirely sure, that would work for you. But I got this logic from Rewrite example here.
And add the path as /customheader(/|$)(.*) something like this will create a capture group.
Hope it works and helpful.