Setting multiple cookies using nginx subrequest authentication - nginx

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;

Related

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

Rewriting Authorization header, fixing typo before jwt validation in Nginx Plus

I have an app-facing Nginx Plus (R22) gateway, which is validating JWT token in Authorization header. Lately I found one of our legacy mobile apps had a bug in which the authorization header has a typo: it was missing a space between the bearer keyword and the token. (example: bearereyJ...)
I used a simple map to make sure I add a space, and set it inside $authorization variable, which works fine:
map "$http_authorization" $authorization {
~*^bearer(?<token>(.*))$ "bearer $token";
default $http_authorization;
}
I also set the Authorization header in my location, but my request is still getting rejected and I keep getting 401, even though upon reviewing, the token is valid.
location ~ ...{
proxy_set_header Authorization $authorization;
proxy_pass ...;
}
How can I make sure I rewrite the header before the JWT validation happens?
Having asked that, my current approach as a workaround would be to set up two locations, one would rewrite the header and will not validate the token, then proxy to another location which will check the modified header and proxy to its destination. Is it a good approach?
Thanks in advance!
Here's the workaround I had to do in order to make this work:
Setup header rewrite inside http block, to make sure there's a space between the bearer word and the token:
map "$http_authorization" $authorization {
~*^bearer(\s*)(?<token>(.*))$ "bearer $token";
default $http_authorization;
}
proxy from one location to another, one unauthenticated that rewrites the header, then proxy to another location that actually authenticates:
location ~ ... {
auth_jwt off;
proxy_set_header Authorization $authorization;
proxy_pass http://$upstream/reauthenticate/$request_uri;
}
location ~ /reauthenticate/(?<original_uri>(.*)){
proxy_pass http://$upstream/$original_uri;
}
While we did not end up using this solution, I do think it's better to have this somewhere just in case someone will be looking for it in the future. This is the Better solution I was looking for, and would avoid the 499 status code:
map $http_authorization $token {
"~^Bearer\s?(.+)$" $1;
}
...
auth_jwt "test" token=$token;

Context Broker Preflight OPTIONS request

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;
}
}

Return the incoming header as is | Nginx

We have a downstream application which is setting some custom headers to the requests from browser before hitting nginx. nginx serves only static contents.
ie browser >> application A >> nginx
The requirement is that the nginx should be able to return all the headers which it receives as is to the downstream server which would give it back to the browser. by default its returning only the generic headers ( cookies etc, expiry etc ) and not retuning the custom ones sent by the downstream server.
For instance, there is a header with name appnumber which nginx receives with value app01. I tried to explicitly set it with the following rule to set it manually if it exist, but did not help as it throws error that variables are not allowed.
if ($appnumber) {
add_header appnumber $appnumber;
}
Can someone please guide me here?
The request headers are stored under $http_ variable. You could try something like
if ($appnumber) {
add_header appnumber $http_appnumber;
}
Refer http://nginx.org/en/docs/http/ngx_http_core_module.html and nginx - read custom header from upstream server

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 "";

Resources