Context Broker Preflight OPTIONS request - fetch

I'm trying to make a GET request to a Context Broker instance from a browser.
I've enabled CORS on the CB using the -corsOrigin __ALL flag when starting the app, and I can see that this has worked by making a request in POSTMAN and seeing this header in the response: access-control-allow-origin →*.
I need to specify the Fiware-Service header in my GET request in order to get the correct entities, which I believe is making the request not simple, triggering an OPTIONS HTTP request.
Inspecting the outgoing request, Chrome reports that these headers are sent:
Access-Control-Request-Headers: fiware-service
Access-Control-Request-Method: GET
The response I get from the Context Broker is:
Request URL: http://xxx.xxx.xxx.xxx:1026/v2/entities/
Request Method: OPTIONS
Status Code: 405 Method Not Allowed
A previous answer by McMutton, to a similar question stated:
"do the necessary changes on your js code to make sure your request
falls within the scope of simple requests."
Which was directed at removing non-standard headers from the request. However, for me I cannot see any non-standard headers being sent.
Reading the Fiware documentation on Access-Control-Allow-Headers, there is a link to the source code where the allowed headers are specified. There, I can see the Fiware-Service header defined, but it does not case-match the headers being sent from the browser (the browser has converted my headers to all lower case).
Does anyone know if "the headers check" in the Context Broker is case-sensitive?
If not, what else could be the issue?
Edit: this issue seems to have been reported here:
https://github.com/telefonicaid/fiware-orion/issues/3453

Based in the discussion on the associated github issue it seems the problem is due to Context Broker is pretty old (version 1.7.0) and that feature wasn't developed yet in that version.
The solution is to update Context Broker to the most recent version (2.2.0 at this moment).

Thanks #fgalan, yes the feature is included in the latest Context Broker version. However, our system is currently quite fragile, so until we can confidently re-build and migrate to the newer version I'm going to mock the HTTP response for the options request using NGINX.
This configuration listens for requests on a different port to the Context Broker, and sends a success response when OPTIONS HTTP requests arrive.
If it's not an OPTIONS HTTP request, NGINX forwards the request to the Context Broker instance.
server {
listen 1885;
listen [::]:1885;
location / {
if ($request_method = OPTIONS ) {
add_header Content-Length 0;
add_header Content-Type text/plain;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Fiware-Service';
return 204;
}
proxy_pass http://xxx.xxx.xxx.xxx:1026;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}

Related

Setting multiple cookies using nginx subrequest authentication

I am using nginx-ingress in my cluster to expose certain services. I have an "auth" service that handles authentication, which I am trying to setup through nginx. Currently the service has a very simple GET endpoint, that always responds with a UserId header and tries to set two cookies:
// This is implemented on Nest.js which uses express.js
#Get('*')
auth(#Res() res: Response): void {
res.header('UserId', '1')
res.cookie('key', 'value')
res.cookie('x', 'y')
res.status(200).send('hello')
}
I can confirm that both cookies are being set when I manually send a request to that endpoint, but when I set it as an annotation to the ingress:
nginx.ingress.kubernetes.io/auth-url: http://auth.dev.svc.cluster.local
and send a request through the ingress, only one of the cookies is forwarded to the Response (the first one key=value). I am not familiar with the nginx configuration, is there something I am supposed to change to make this work, so that both cookies are set?
I found this issue on GitHub, but it seems to be about OAuth2 there is no clear explanation on what I am supposed to change.
I couldn't find a way to make this work with the Set-Cookie header. Not sure if there is a better way, but here is a workaround:
I added a snippet for the location block that converts two headers to cookies:
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $auth_cookie1 $upstream_http_x_header1;
auth_request_set $auth_cookie2 $upstream_http_x_header2;
add_header Set-Cookie $auth_cookie1;
add_header Set-Cookie $auth_cookie2;
And the auth() endpoint now responds with the X-Header1 and X-Header2 headers:
import { serialize } from 'cookie'
#Get('*')
auth(#Res() res: Response): void {
res.header('UserId', '1')
res.header('X-Header1', serialize('key', 'value'))
res.header('X-Header2', serialize('x', 'y'))
res.status(200).send('hello')
}
Everything seems to be working well and this solution is similar to how nginx is adding the Set-Cookie header which doesn't support multiple cookies. The code below is copied from the nginx.conf file in the nginx-controller pod that nginx-ingress creates.
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;

CORS Allow Origin Not Matching Origin error through NGINX

I have one application on one domain that talks to another application through browser making API calls. Its getting below error in the devtools logs
CORS Allow Origin Not Matching Origin
The route it takes is through 3 servers before it hits the application which is hosted in AKS.
First it hits an nginx TLS server and here I have enabled CORS like so underneath the location block
add_header 'Access-Control-Allow-Origin' 'https://mysite.domain';
add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, PUT, DELETE";
add_header "Access-Control-Allow-Headers" "Authorization, Content-Type";
Next it gets forwarded on to an nginx reverse proxy server which has the same config defined under the location block
Next it hits the service mesh (istio) in AKS where the virtual service it hits has the below configuration for CORS
- corsPolicy:
allowCredentials: true
allowHeaders:
- authorization
- content-type
allowMethods:
- GET
- POST
- OPTIONS
- PUT
- DELETE
allowOrigins:
- exact: '*'
If I bypass my nginx servers then I dont have any issue but when I target the service through the nginx servers I get the above CORS error. Looking at the devtool logs and the failure in detail it makes no sense as I can see the CORS policy of all three hops is identical and the access-control-allow-origin is the same for all of them and it exactly matches the Origin field in the same request.
I have tried many combinations including allowing ALL on the nginx servers like below
add_header 'Access-Control-Allow-Origin' '*';
And I also tried locking it down to the URL on the virtual service in K8S but that didnt work either

Override nginx request headers with uwsgi_pass

Is there any way I can get nginx to not forward a specific request header to uwsgi?
I want to enable nginx basic auth, but if the Authorization header gets forwarded to my app it breaks things (for reasons, I won't go into). If it was just a simple proxy_pass I would be able to do proxy_set_header Authorization ""; but I don't think this works with uwsgi_pass and there's no equivalent uwsgi_set_header as far as I can see.
Thanks.
Try hide header and ignore header directives:
uwsgi_hide_header
Syntax: uwsgi_hide_header field; Default: — Context: http, server,
location
By default, nginx does not pass the header fields “Status” and
“X-Accel-...” from the response of a uwsgi server to a client. The
uwsgi_hide_header directive sets additional fields that will not be
passed. If, on the contrary, the passing of fields needs to be
permitted, the uwsgi_pass_header directive can be used.
uwsgi_ignore_headers
Syntax: uwsgi_ignore_headers field ...; Default: —
Context: http, server, location Disables processing of certain
response header fields from the uwsgi server. The following fields can
be ignored: “X-Accel-Redirect”, “X-Accel-Expires”,
“X-Accel-Limit-Rate” (1.1.6), “X-Accel-Buffering” (1.1.6),
“X-Accel-Charset” (1.1.6), “Expires”, “Cache-Control”, “Set-Cookie”
(0.8.44), and “Vary” (1.7.7).
If not disabled, processing of these header fields has the following
effect:
“X-Accel-Expires”, “Expires”, “Cache-Control”, “Set-Cookie”, and
“Vary” set the parameters of response caching; “X-Accel-Redirect”
performs an internal redirect to the specified URI;
“X-Accel-Limit-Rate” sets the rate limit for transmission of a
response to a client; “X-Accel-Buffering” enables or disables
buffering of a response; “X-Accel-Charset” sets the desired charset of
a response.
It's probably too late for you but for anyone who would have the same problem, this answer provides a valid solution.
In this case the Authorization header could be passed by using the following directive:
uwsgi_param HTTP_Authorization "";

Avoiding preflight OPTIONS requests with CORS

I am building an Angular app that interacts with an API built with ASP.NET Web API 2. I am using Basic Authentication by sending an Authorization header with each request that requires authentication:
Angular snippet:
$http.defaults.headers.common['Authorization'] = authHeader;
Request:
Accept:application/json, text/javascript
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Max-Age:1728000
Authorization:Basic [base64 encoded credential couplet here]
Connection:keep-alive
DNT:1
Host: blah.com
Origin:http://localhost:9000
Referer:http://localhost:9000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/53
This all works OKAY, but a preflight OPTIONS request is sent with every GET or POST request. This is majorly impacting the perceived speed of the application. I have done lots of reading on CORS "Simple Requests" and it seems that in order to avoid the dreaded preflight OPTIONS request is to avoid adding any custom headers in my requests. I've tried lots of other stuff like sending a Content-Type of text/plain, but it seems that the Authorization header is the thing that is violating the CORS "Simple request" requirement.
So it seems that I may have to move the API over to use token based authentication/authorization. In order to avoid preflight requests, it seems that I will need to place the token in the query string. This is okay as it is only a small internal web app which will only be accessed by a couple of users anyway. I intend to implement caching on controller responses. As each request to an controller action will have a different token in the querystring based on the currently authenticated user, will this render cacheing useless?
So:
How do I avoid preflight requests (using custom Authorization headers if at all possible)
If 1.) is not possible, and I move to token based auth, will I be unable to cache API responses for controller actions
What are the most widely used methods to avoid preflight requests but also to auth users securely?
n.b I know there are a couple of other threads on SO and elsewhere on the web regarding this, but none of them tend to provide a definitive answer on whether it is possible to avoid preflight requests for GETs and POSTs when using custom HTTP authorization headers.
I think this post (How to apply CORS preflight cache to an entire domain) pretty much says it all - there is not much you can do about this
The one simple solution is to add a reverse proxy to the proxy/webserver serving your angular app (e.g. nginx) to route your RESTful calls via the same domain, e.g. appdomain.com/api --> apidomain.com.
Another solution that seems to be working OK for me. Instead of setting up a proxy and needing to route to the same domain, it is possible to return the preflight request directly from nginx and therefore reducing the time required by the preflight request down to just a couple of milliseconds.
Here is a simple snippet that can be used with nginx.
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
}
Once the preflight request is successful, it is then possible to simple add "Access-Control-Allow-Origin" and other necessary stuff to the 'GET','POST' requests, etc...

How to make nginx CORS configuration work when server returns error?

I want to add CORS to my server.
I have configured my nginx according to this: https://michielkalkman.com/snippets/nginx-cors-open-configuration.html
It seems to work fine when the server returns 200.
However, if the server returns something else, like 400 when the request is wrong, or 500 if internal error, the browser shows the No 'Access-Control-Allow-Origin' header instead of reaching the error handler like it should.
What configuration am I missing to make it work?
Since version 1.7.5 you can use the always keyword to return the headers regardless of the response code:
add_header 'Access-Control-Allow-Origin' '*' always;
http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header
This has been answered before: https://serverfault.com/questions/431274/nginx-services-fails-for-cross-domain-requests-if-the-service-returns-error.
add-header doesn't work with HTTP errors, but the optional headers_more module can be used to workaround this limitation.

Resources