HTTP404 for resources when using url rewriting on Istio gateway - url-rewriting

I'm trying to deploy a service to a specific url address on AKS. The following yaml let's me access the service at desired address, like xxxx.europe.cloudapp.azure.com/service-a. This works great, i've managed to hide the entire service under desired url:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: istio-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: istio
spec:
hosts:
- "*"
gateways:
- istio-gateway
http:
- match:
- uri:
prefix: /service-a
rewrite:
uri: /
route:
- destination:
host: service-a.default.svc.cluster.local
However when the welcome page is displayed, i only see text. No css/javascript/image files are loaded. Everything that this page is trying to load still has original url address without any rewriting being made by my gateway configuration. So the home page requests this:
http://xxxxx.europe.cloudapp.azure.com/icon.jpg
instead of this:
http://xxxxx.europe.cloudapp.azure.com/service-a/icon.jpg
What is the best way to handle rewriting urls for resources and links on a page? Do i have to manually change the urls on a home page?
EDIT:
To be more clear.
Rewriting of the url works as expected, the address is exactly how i want (the entire service is hidden under "xxxx.europe.cloudapp.azure.com/service-a".
Once i enter the "xxxx.europe.cloudapp.azure.com/service-a" i see service's welcome page, but without any css/jpegs/js loaded. Also the links visible on the welcome page don't work.
for example, "icon.jpg" is not loaded. Page wants to load it from http://xxxx.europe.cloudapp.azure.com/icon.jpg but it's not there anymore. Due to rewriting it's available at http://xxxx.europe.cloudapp.azure.com/service-a/icon.jpg which is as expected.
I kind of expected that the http://xxxx.europe.cloudapp.azure.com/icon.jpg request will be automatically rewritten to http://xxxx.europe.cloudapp.azure.com/service-a/icon.jpg. But obviously i was mistaken. So i wonder how i can handle the links within the service itself in a manageable way - i mean i can modify every possible link within the app, but what if we change the url again (from /service-a to /service-b). The service is written with ASP.NET Core, i will look for some kind of internal rewriting solution that is maintainable.

The rewriting is happening because of this part of the config:
- match:
- uri:
prefix: /service-a
rewrite:
uri: /
Which results that the matched prefix is replaced with the value of the rewrite.uri property.
Example 1: (virtual service is activated)
Original: http://www.page.com/service-a/icon.jpg
^--------^
Rewritten: http://www.page.com/icon.jpg
Example 2: (this virtual service is activated)
Original: http://www.page.com/service-a/service-a/icon.jpg
^--------^
Rewritten: http://www.page.com/service-a/icon.jpg
Example 3: (this virtual service is not activated, fall back on some other virtual service, or on a default route, or blackhole which returns 404)
Original: http://www.page.com/icon.jpg
Rewriting: DOESN'T HAPPEN
For rewriting there are no recommendations and cannot be, it's dependent on your services. Istio's docs for rewriting props can be found here
If every subdomain will have it's own service then this would be an option:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: istio
spec:
hosts:
- "service-a.domain.com"
gateways:
- istio-gateway
http:
- match:
- uri:
prefix: /
rewrite:
uri: /service-a
route:
- destination:
host: service-a.default.svc.cluster.local

Related

Azure WAF Rewrite rules for updating port numbers

I have a server in Azure running two web apps, one on port 443 (IIS), another on 1024 (Apache). Both are https. I have an Azure Application Gateway (WAF v2) in place. I would like to allow requests for subdomain1.domain.com to go through on 443 (which is set-up and working) and requests for subdomain2.domain.com to be re-written to port 1024 internally.
I have tried various combinations of conditions and actions, but cannot get anything to do anything at all, good bad or indifferent!
My current Condition is as follows
Type of variable to check: HTTP Header
Header type: Response Header
Header name: Common Header
Common header: Location
Case-sensitive: No
Operator: =
Pattern to match: (https?):\/\/.*subdomain2.domain.com(.*)$
My current action is:
Re-write type: Response Header
Action type: Set
Header name: Common header
Common header: Location
Header value: https://backendservername.domain.com:1024{http_resp_Location_2}
I can't find a combination that does anything at all, nor any examples that show port updates. I've tried using request headers and the host value, but unfortunately that conflicts with the host rewrite in the HTTP Settings that was necessary to get any end to end SSL working.
Thanks in advance.
Matt.

nestjs + socket.io serving websockets from microservice at nested path instead of the root of the host always returns 404 Not Found

I'm using the socket.io implementation with nestjs. My overall in-production architecture is relying on a load balancer to route requests on paths to microservice containers.
One of them contains a websocket that interacts with user data and is nested on mydomain.com/apis/user
On my gateway I've configured it to use this path:
#WebSocketGateway({ path: "/apis/user/gateway", namespace: "/apis/user/gateway" })
and I've tried variations without the namespace there and with just the path.
I also have a global prefix: app.setGlobalPrefix("apis/user"); removing this makes no difference, it seems I need to define the path to the gateway either way prefix or not.
Then on the client I'm trying to just connect to it via either adding the path in the url, or to the options object, as such:
const endpoint = "https://example.com/apis/user/gateway";
socket = io(endpoint, {
secure: true,
path: "/apis/user/gateway",
});
This works for the path and handleConnection triggers on the gateway nested there, however configuring polling on the backend does not work, the client still throws:
https://example.com/apis/user/gateway/?EIO=4&transport=polling&t=NXVpMfB 404 Not Found
I had the same problem, all the various options are confusing. I got it sorted though, here is what worked:
For the nestjs:
#WebSocketGateway({
cors: {
origin: '*',
},
namespace: '/api/v1/ws',
path: '/api/v1/ws/socket.io',
})
For socket.io:
const newSocket = io("http://localhost:3000/api/v1/ws", {
path: "/api/v1/ws/socket.io",
});

How to hide server descriptions in Swagger UI?

I have an OpenAPI 3.0 definition with multiple servers:
servers:
- url: https://development.gigantic-server.com/v1
description: Development server
- url: https://staging.gigantic-server.com/v1
description: Staging server
- url: https://api.gigantic-server.com/v1
description: Production server
When this definition is rendered in Swagger UI, the "Servers" dropdown shows the description of each server:
Is it possible to hide the server descriptions from this dropdown?
Swagger UI always displays the server description if it's provided, this is hard-coded:
https://github.com/swagger-api/swagger-ui/blob/master/src/core/plugins/oas3/components/servers.jsx#L125
As a workaround you can modify the API definition dynamically after it's loaded and remove server descriptions. To do that, edit your Swagger UI's swagger-initializer.js or index.html file and add the following onComplete function to the SwaggerUIBundle initialization code:
const ui = SwaggerUIBundle({
url: "https://path/to/your/openapi.json",
...
onComplete: function() {
let spec = ui.specSelectors.specJson().toJS();
let servers = spec.servers || [];
for (let i in servers) {
servers[i].description = ""
}
ui.specActions.updateJsonSpec(spec);
}
})
They haven't provided any option to replace this server's description in another place, but they have mentioned the description is optional in swagger specification of object representing a Server.
Swagger UI have not provided any rendering option for this.
The best use of description is define in a single word, like production, development, api, staging, etc..
If you really don't want in dropdown then you can remove it from your server list.
servers:
- url: https://development.gigantic-server.com/v1
- url: https://staging.gigantic-server.com/v1
- url: https://api.gigantic-server.com/v1
This part i am writing for your information, about how to use oas-servers,
I observed your server urls, these can be easily define in single url, how? using server variables.
servers:
- url: https://{environment}.gigantic-server.com/{version}
variables:
environment:
enum:
- 'development'
- 'staging'
- 'api'
version:
enum:
- 'v1'
Hope this help.

kubernetes ingress nginx rewrite partially working - sometimes wrong context path

I'm setting up simple Kubernetes cluster using GKE. I've set up Java Spring boot app as Kubernetes Deployment and Service (with type Load Balancer, exposed on port 80). This app is working perfectly fine when accessed directly using external IP for the service.
I've also installed nginx-ingress for GKE and provided following Ingress resource:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/enable-rewrite-log: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$1
name: rewrite
namespace: default
spec:
rules:
- host: "myhost.ddns.net"
http:
paths:
- backend:
serviceName: app-java
servicePort: 80
path: /app-java/?(.*)
From now on, funny things occur:
Problem 1: if I access site at http://myhost.ddns.net/app-java - I get only HTML file, all other resources have 404 (download attempt from http://myhost.ddns.net/foo.js instead of http://myhost.ddns.net/app-java/foo.js.
Problem 2: If I access site at http://myhost.ddns.net/app-java/ - I get both HTML file and all scripts and images. However, when I dig deeper, sending form content via POST fails. It's HTML code is:
<form action="/select" method="post">
Which tries to access http://myhost.ddns.net/select instead of http://myhost.ddns.net/app-java/select.
I found out that it might me strongly connected to this question: Thymeleaf template (in Spring boot application) behind reverse proxy not forming url's correctly. SOlution provided there is to write following nginx reverse proxy configuration:
location /app-java {
proxy_pass http://10.0.0.0:80;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Prefix /app-java;
}
However, I have no idea how to translate it to ingress resource yaml.
How can I fix this url-rewrite problem using ingress?
Edit: After hints from #HelloWorld I've reorganized application to serve content on /app-java context path (using Spring Boot 2 property server.servlet.context-path=/app-java). After that, I've changed rewrite-target to:
nginx.ingress.kubernetes.io/rewrite-target: /app-java/$1
And now it's working fine.
I see you are trying to make your browser to add a prefix to all paths in flight.
I believe you must have misunderstood the idea of X-Forwarded-Prefix and whatever was described in Thymeleaf template (in Spring boot application) behind reverse proxy not forming url's correctly, where community member described how to pass this header to your application. If you want to try it and see how it works you can achieve this by simply adding this annotation to your ingress object:
nginx.ingress.kubernetes.io/x-forwarded-prefix: "/app-java"
but notice this is not going to solve your problem.
Of course you can also add these headers to the response from application so that they get sent to your browser (what I believe you were expecting to achieve) but browsers don't care about these headers so there is no point in doing it.
If you still want to try it yourself and prove it, you can add this annotation to the ingress object:
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Forwarded-Prefix: /app-java";
The only way as far as I know to solve this issue is to modify your application code so that it's responding with correct paths.
You can find that many applications use customizable base path, configurable either by env variables, config files or by cli arguments.

Serverless Framework, setting up rest api lambda

I wanted to make a lambda available at dev-api.example.com/auth/*.
The lambda will act like an auth service. So it will have urls like
dev-api.example.com/auth/register
dev-api.example.com/auth/login
and more ...
Like wise more lambdas will be hooked to single ApiGateway.
With that design decision, I wrote following serverless.yml file.
// serverless.yml
...
custom:
customDomain:
domainName: dev-api.example.com
stage: prod
basePath: ''
...
functions:
auth:
handler: src/index.handler
events:
- http:
method: ANY
path: /{auth+}
It does not seem to work. Whenever I visit dev-api.example.com/auth/register it returns Not Found error.
AWS API Gateway only accepts {proxy+} syntax (Link), then I think serverless fw just support {proxy+} and {any+}.
If you want to just create a function to handle 2 api endpoint, in this case, the endpoints are
POST /auth/register (I think so)
POST /auth/login
Then you have setting in serverless.yml like
...
functions:
auth:
handler: src/index.handler
events:
- http:
method: ANY
path: auth/{any+} # this matches any path, the token 'any' doesn't mean anything special
...
Thanks #hoangdv , your suggestion almost fixed the problem.
The issue was with path. It should have been path: auth/{proxy+} instead of path: /{auth+}
functions:
auth:
handler: src/index.handler
events:
- http:
method: ANY
path: auth/{proxy+}

Resources