Kubernetes Ingress Gateway for Response Content Manipulation - kubernetes

In our Kubernetes Cluster, We have a requirement to do a string replace in our Response body. Is there such a thing in Istio for response content modification. I could only find header manipulation.
Can Nginx Ingress Controller do this?
The respone contains html content with hrefs as: <>"/static/myimages/logo.png"<>
We would like to modify this response to prefix it with <>"/myapp/static/myimages/logo.png"<>
Does anyone have a recommendation for a gateway that can do this content rewrite.
Do you have samples to share?
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: resource-manager
spec:
hosts:
- "*"
gateways:
- myapp-gateway
http:
- match:
- uri:
prefix: /myapp/
rewrite:
uri: "/"
route:
- destination:
host: myapp.voting.svc.cluster.local
port:
number: 9099
headers:
response:
add:
foo: bar

I started looking at other Ingress Controllers and found that NGINX-Ingress can do response manipulation. Here is an ingress route I setup which does content manipulation. Sharing that here:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: app1-ingress
namespace: app1
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
sub_filter '<title>My title' '<title>My updated title'; #This changes title that Chrome Shows on Top
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- http:
paths:
- backend:
serviceName: app1
servicePort: 8080
path: /app1/(.*)

AFAIK, istio does not offer body modification by default. It might not even be possible with custom envoy filters.

Related

kubernetes ingress returns incorrect mimetype on rewrite-target

I am having a problems with content response from a k8s version: 1.20.15, the ingress redirects to 2 applications based on the same domain (domains bellow are hypothetical). One service being an API and the other a website.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/backend-protocol: HTTP
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
labels:
app.kubernetes.io/name: frontend-explorer
name: frontend-explorer
spec:
rules:
- host: dev-explorer.test.org
http:
paths:
- path: /(.*)
pathType: Prefix
backend:
service:
name: frontend-explorer-svc
port:
number: 8081
- path: /api/(.*)
backend:
service:
name: backend-explorer-svc
port:
number: 3000
A request like dev-explorer.test.org/api/settings will be forward to the backend-explorer-svc and its url will become dev-explorer.test.org/, to the backend service
The services seem to working without any problems, but when accessing https://dev-explorer.test.org, there are errors like:
Loading module from “https://dev-explorer.test.org/main-es2015.3c58a0782b71050b1782.js”
was blocked because of a disallowed MIME type (“text/html”).
You can use a curl to fetch the content, but the ingress responds with
content-type: text/html instead of content-type: application/js, forcing the browser to block the website
Not certain if this is a problem on the ingress, or the JS code it self. Any ideas on how to get the correct content-type ?
Somehow the ingress was responding with content-type: text/html as if it was dealing with a pre-flight CORS request The solution was to disable CORS on ingress and configure it on the app.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
...
nginx.ingress.kubernetes.io/enable-cors: "false"
...

K8S Ingress controller redirect to the wrong path

I have an Nginx ingress controller running on one k8s namespace,
and on another k8s namespace, I defined a pod, a service, and an ingress resource.
this is the ingress resource definition:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: general-internal
rules:
- host: example.com
http:
paths:
- path: "/my-app(/|$)(.*)"
backend:
serviceName: my-app-svc
servicePort: 443
now, when I access this link:
http://example.com/my-app/some-path/
then everything is ok because the "my-app-svc" knows the path "/some-path/" and returns a 200 response (the forwarding is to http://my-app-svc/some-path and that's great because my-app-svc doesn't and shouldn't know or care for the /my-app prefix that exists only for the nginx ingress controller so it will know to forward that request to "my-app-svc" internally).
but when I access this link (notice no "/" in the end):
http://example.com/my-app/some-path
I get a redirection response from the "my-app-svc" service, and the "Location" header contains "/some-path/", so the redirection is to:
http://example.com/some-path/
which does not lead to the "my-app-svc" service because it doesn't have the "/my-app" prefix.
If the Location header was "/my-app/some-path/" instead of "/some-path/" then everything was ok because the redirection would be to:
http://example.com/my-app/some-path/
which would give me the 200 response.
the question is, how can I do that the ingress controller will add a "my-app" prefix to the Location header when it returns the redirection response to the client?
Thanks
Thanks to my co-worker, we found a solution for the problem:
The solution is to add these annotations to the ingess resource:
nginx.ingress.kubernetes.io/proxy-redirect-from: /
nginx.ingress.kubernetes.io/proxy-redirect-to: /my-app/
meaning:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/proxy-redirect-from: /
nginx.ingress.kubernetes.io/proxy-redirect-to: /my-app/
spec:
ingressClassName: general-internal
rules:
- host: example.com
http:
paths:
- path: "/my-app(/|$)(.*)"
backend:
serviceName: my-app-svc
servicePort: 443
It seems that the annotations above check the "Location" header in the redirection response to the client, and replace the first / with /my-app/ and only after this change - the redirection response is sent to the client.
For anyone landing here and finding the above solution not working (like myself), you might be using nginx-based ingress https://www.nginx.com/blog/guide-to-choosing-ingress-controller-part-4-nginx-ingress-controller-options/ which needs different annotation to your ingress resource:
nginx.org/rewrites: "serviceName=service1 rewrite=rewrite1[;serviceName=service2 rewrite=rewrite2;...]"
which worked for me.
A complete declaration from https://github.com/nginxinc/kubernetes-ingress/tree/v2.3.0/examples/rewrites copied here:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cafe-ingress
annotations:
nginx.org/rewrites: "serviceName=tea-svc rewrite=/;serviceName=coffee-svc rewrite=/beans/"
spec:
rules:
- host: cafe.example.com
http:
paths:
- path: /tea/
pathType: Prefix
backend:
service:
name: tea-svc
port:
number: 80
- path: /coffee/
pathType: Prefix
backend:
service:
name: coffee-svc
port:
number: 80

Kubernetes ingress-nginx not returning http response header

im encountering an issue whereby ingress-nginx(reverse proxy) is not returning http response header (ETag) from application container.
It works fine without ingress.
Missing http response header
Any help will be greatly appreciated.
The following is a snippet of Ingress yaml file
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ng-api-ngress
namespace: ng-prod
labels:
app: numbergenerator
annotations:
#nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-headers: etag
nginx.ingress.kubernetes.io/cors-expose-headers: etag
#nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
spec:
rules:
- host: kubernetes.docker.internal.api
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: ng-backend
port:
number: 80
You can add this Nginx ingress annotation :
nginx.ingress.kubernetes.io/server-snippet: |
etag on;
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "server: hide";
if Gzip is enabled Nginx ingress won't add or pass etag.
As much as i know etag wont generated by Nginx in reverse proxy configuration however if require you can generate and manage from backend code.

Kubernetes Ingress needs Reverse Proxy setting

In my Kubernetes Cluster i have some challenges with the Ingress. As example i installed NodeRed und the Nginx-ingress via Helm. NodeRed is available via
FQDN: http://my.server.name:31827
IP: http://10.x.x.x:31827
Now i created an Ingress:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nr-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls:
- secretName: tls-secret1
hosts:
- my.server.name
rules:
- host: my.server.name
http:
paths:
- path: /nr
backend:
serviceName: my-nodered-node-red
servicePort: 1880
When i do a Get http://my.server.name/nr i see only parts working, see the screenshot:
It looks to me, that i missed the reverse proxy settings. Normally i would place those things in a reverse proxy setting in the nginx like this. But this is not possible because i am using the Nginx-ingress.
location / {
proxy_pass http://localhost:1880/;
}
But i do not know how to do that in Kubernetes ? What do i miss ? kubernetes version is 1.14.1.
I haven't used it so I'm not sure if it helps, but you might want to try adding an annotation for Proxy redirect.
With the annotations nginx.ingress.kubernetes.io/proxy-redirect-from and nginx.ingress.kubernetes.io/proxy-redirect-to it is possible to set the text that should be changed in the Location and Refresh header fields of a proxied server response
Setting "off" or "default" in the annotation nginx.ingress.kubernetes.io/proxy-redirect-from disables nginx.ingress.kubernetes.io/proxy-redirect-to, otherwise, both annotations must be used in unison. Note that each annotation must be a string without spaces.
By default the value of each annotation is "off".
Also you can use ConfigMap for customizing your Nginx configuration.
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app: ingress-nginx
data:
http-snippet: |
location = / {
proxy_pass http://localhost:1880/;
}
Or server-snippets annotation for your Ingress:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nr-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/server-snippet: |
location = / {
proxy_pass http://localhost:1880/;
}
spec:
tls:
- secretName: tls-secret1
hosts:
- my.server.name
rules:
- host: my.server.name
http:
paths:
- path: /nr
backend:
serviceName: my-nodered-node-red
servicePort: 1880
I hope this helps.
Maybe too late for the answer, but I had the same problem and solve it:
1-changed httpRoot: '/nr' in node red settings.xml configuration file (in kubernetes, probably defined in a PV) (#vasili-angapov mentions)
2- set ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nodered-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- http:
paths:
- path: /nr
pathType: Prefix
backend:
service:
name: nodered
port:
number: 1880
It looks like your static content is still using root path prefix, you can verify that using browser developer console. Common applications should have a configuration to understand that they are running on non-root path prefix. You should find that configuration option in your application and configure it properly. Nginx ingress has nothing to do with this error.

traefik ingress custom error in kubernetes

I need to set a custom error in traefik ingress on kubernetes so that when there is no endpoint or when the status is "404", or "[500-600]" it redirects to another error service or another custom error message I used the annotation as it's in the documentation in the ingress file as this (Note: this a helm template output of passing the annotation as a yaml in the values.yaml file)
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: frontend
namespace: "default"
annotations:
external-dns.alpha.kubernetes.io/target: "domain.com"
kubernetes.io/ingress.class: "traefik"
traefik.ingress.kubernetes.io/error-pages: "map[/:map[backend:hello-world status:[502 503]]]"
spec:
rules:
- host: frontend.domain.com
http:
paths:
- backend:
serviceName: frontend
servicePort: 3000
path: /
The answer by ldez is correct, but there are a few caveats:
First off, these annotations only work for traefik >= 1.6.x (earlier versions may support error pages, but not for the kubernetes backend)
Second, the traefik backend must be configured through kubernetes. You cannot create a backend in a config file and use it with kubernetes, at least not in traefik 1.6.x
Here's how the complete thing looks like. foo is just a name, as explained in the other answer, and can be anything:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: frontend
namespace: "default"
annotations:
external-dns.alpha.kubernetes.io/target: "domain.com"
kubernetes.io/ingress.class: "traefik"
traefik.ingress.kubernetes.io/error-pages: |-
foo:
status:
- "404"
- "500"
# See below on where "error-pages" comes from
backend: error-pages
query: "/{{status}}.html"
spec:
rules:
# This creates an ingress on an non-existing host name,
# which binds to a service. As part of this a traefik
# backend "error-pages" will be created, which is the one
# we use above
- host: error-pages
http:
paths:
- backend:
serviceName: error-pages-service
servicePort: https
- host: frontend.domain.com
http:
# The configuration for your "real" Ingress goes here
# This is the service to back the ingress defined above
# Note that you can use anything for this, including an internal app
# Also: If you use https, the cert on the other side has to be valid
---
kind: Service
apiVersion: v1
metadata:
name: error-pages-service
namespace: default
spec:
ports:
- name: https
port: 443
type: ExternalName
externalName: my-awesome-errors.mydomain.test
If you use this configuration, and your app sends a 404, then https://my-awesome-errors.mydomain.test/404.html would be shown as the error page.
The correct syntax is:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: frontend
namespace: "default"
annotations:
external-dns.alpha.kubernetes.io/target: "domain.com"
kubernetes.io/ingress.class: "traefik"
traefik.ingress.kubernetes.io/error-pages: |-
foo:
status:
- "404"
backend: bar
query: /bar
fii:
status:
- "500-600"
backend: bar
query: /bir
spec:
rules:
- host: frontend.domain.com
http:
paths:
- backend:
serviceName: frontend
servicePort: 3000
path: /
https://docs.traefik.io/v1.6/configuration/backends/kubernetes/#general-annotations
Note that, currently, the Helm Charts doesn't support this feature.
Ingress does not support that annotations that you guys are using there!
That annotations are supported with Service only, Ingress is using host section.