Nginx: How do I pass Origin header through proxy? - nginx

Currently I have a service setup at https://example.com that, as part of its standard logging setup, logs the request origin. This is a public data API, it's open to any and every origin.
This service used to be at https://example_2.com, but I proxy that address to the new to ensure non-breaking service for everyone else. This is done in the following way:
server {
...
server_name example_2.com;
location / {
proxy_pass https://example.com;
add_header Access-Control-Allow-Origin *;
}
}
The problem is that the Origin header turns up as null at the proxy destination. I need the header to arrive intact so I can know where the request came from.
I tried adding proxy_pass_request_headers but that seemingly does nothing at all.

While I haven't fixed passing Origin through, I did learn that Nginx automatically populates Referer which will work for my purpose. It's not great, and I'd love to know how to get a non-null Origin, but I thought I'd post this as it might help others who similarly can get away with this as a "solution".

Related

disallow direct access to nginx location

I have two locations in nginx, where one redirect to another. I want to make next:
First one allow direct access in browser and redirect query to second location. Second location transform get to post, make proxy query and allowed only from first location.
First:
location /first/ {
rewrite ^ /second/ permanent;
}
Second:
location /second/ {
proxy_method POST;
proxy_set_body '{ "arg1": "$arg_arg1", "arg2": "$arg_arg2" }
proxy_pass https://some_api.com/
}
How could I check in second location if it redirected from first one (not direct access in browser) and show some 40x error if it was direct access?
Trying to use internal directive, but this rewrite does not fall into the category of internal redirects.
Redirect using to hide /first/ url in user browser
Thanx in advance
This is not about nginx, rather about HTTP protocol and user browser behavior. Whatever you are trying to do, I think you are trying to do it in a wrong way. Generally, you need to generate some one-time token at your first location and use it at the second one, but this is a web app job, nginx is just a web server and not a web framework (however it can be possible using third party modules like lua-nginx-module). If you want to do it using nginx itself, no matter what solution you'll finished up with, it will be possible to trace and spoof it.
Here is an idea in general:
location /first/ {
# set cookie with an access token
add_header Set-Cookie "my_token=my_super_password; path=/second/" always;
# do not use 301 redirect here or it will be cached by user browser, use 302 only!
rewrite ^ /second/ redirect;
}
location /second/ {
if ($cookie_my_token != "my_super_password") { return 403; }
# clear the token cookie on response
add_header Set-Cookie "my_token=deleted; path=/second/; expires=Thu, 01 Jan 1970 00:00:00 GMT" always;
... rest of configuration here
}

How to set exceptions for NGINX load balancer

Is it possible to configure NGINX loadbalancer in least_conn mode to make exception for certain paths?
I want to configure loadbalancer in such way that all requests required for single login operation are sent to the same backend application instance.
I have frontend app accessing duplicated backend app via nginx load balancer. All apps are deployed on Tomcat 8.5 and backend instances have configured session replication between Tomcats.
My problem is that when user is authenticated using OAuth-2.0 authorization_code grant method, frontend app gets authorization code but due to conneting to backend through load balancer it tries to obtain token using this code from another machine resulting in InvalidGrantException.
Using ip_hash mode or it's variations isn't solution for this problem as it is unstable when application is accessed through VPN.
Yes you can achieve what you want by declaring two locations and treat them differently. See example below and check this question where it explains how the priority works.
http {
upstream myapp1 {
least_conn;
server srv1.example.com;
server srv2.example.com;
server srv3.example.com;
}
server {
listen 80;
location / {
proxy_pass http://myapp1;
}
location /my-special-path/ {
proxy_pass http://srv1.example.com;
}
}
}
Above is a solution mainly based in your first statement that you want routing based on certain paths. If your problem is more complicated i.e these paths are dynamically created etc you can share an example to be easier to understand your specific situation.
UPDATE
Based on comment. I would really suggest to go troubleshoot your backend in order to be synced. That being said if you really want a solution for the exact problem from your nginx I would do the following:
On every response I would add a specific header which specific backend answered this request. add_header X-Upstream $upstream_addr;
On this specific path I would serve the request based on the value of that header. proxy_pass http://$http_x_upstream;
So the config would look like this:
http {
...
server {
...
location / {
add_header X-Upstream $upstream_addr always;
proxy_pass http://myapp1;
}
location /authorize/ {
add_header X-Upstream $upstream_addr always;
proxy_pass http://$http_x_upstream;
}
}
}
NOTE: Security. If you go down this path be careful that you are routing your requests based on a value that your client can manipulate. So be sure that you are at least validating this value. Check this answer for validating headers with nginx.

how to write dynamic route in nginx location proxy config

for example I want anyone hits localhost/api to be redirected to a proxy of 127.0.0.1/api and whatever after it, for example, if I got localhost/api/getMyName then the config redirect it to 127.0.0.1/api/getMyName. or if someone hits localhost/api/getSomeone/1, it will proxy to 127.0.0.1/api/getSomeone/1
I tried something like
location /api {
proxy_pass http://127.0.0.1/api;
}
But the nginx just not responding at or, and adding /* or * after them just do not do the work... what should it actually be written to match the scenario I want above?

Edit a header value in nginx

Background
So I've got a server running a tomcat application hidden behind an Apache proxy. The proxy provides a more user friendly url as well as SSL encryption with automatic redirects so that the app is only accessible on https.
I'm busy migrating this to an nginx proxy.
One of the issues I've had is that upon login, my app sends back a "LocationAfterLogon" header in the http response in the form of
http://192.168.x.x:8080/myapp/index.jsp.
That IP address returned is from the proxied server not visible on the internet. So then the browser gets a connection error trying to navigate to it.
As a workaround, I've used nginx directives:
proxy_hide_header: to hide the LocationAfterLogin header coming back from the proxied server
add_header: to add a new LocationAfterLogin url.
So my config looks as follows
#header for location after logon of demo app
add_header LocationAfterLogon http://example.com/demo/index.jsp;
#hide the real LocationAfterLogon
proxy_hide_header LocationAfterLogon;
The Problem
I need to be able to do a regex replace or similar on LocationAfterLogon because it won't always be to index.jsp, depending on which url was intercepted by the login page.
I am aware that I can also rewrite the tomcat app to send back a relative URL instead, but I'd like to do it all in nginx config.
I've also read about nginx more_set_headers. Haven't tried it yet. Does it allow me to edit the headers?
Apache has the Header edit directive which I was using previously, so I'm looking for something like that.
TL;DR
Is is possible to edit a header location using regex replace or similar in Nginx?
You can use the map directive to rewrite your header:
map $upstream_http_locationafterlogon $new_location {
~regexp new_value;
}
proxy_hide_header LocationAfterLogon;
add_header LocationAfterLogon $new_location;
See the documentation: http://nginx.org/en/docs/http/ngx_http_map_module.html

NGinx : How to test if a cookie is set or not without using 'if'?

I am using the following configuration for NGinx currently to test my app :
location / {
# see if the 'id' cookie is set, if yes, pass to that server.
if ($cookie_id){
proxy_pass http://${cookie_id}/$request_uri;
break;
}
# if the cookie isn't set, then send him to somewhere else
proxy_pass http://localhost:99/index.php/setUserCookie;
}
But they say "IFisEvil". Can anyone show me a way how to do the same job without using "if"?
And also, is my usage of "if" is buggy?
There are two reasons why 'if is evil' as far as nginx is concerned. One is that many howtos found on the internet will directly translate htaccess rewrite rules into a series of ifs, when separate servers or locations would be a better choice. Secondly, nginx's if statement doesn't behave the way most people expect it to. It acts more like a nested location, and some settings don't inherit as you would expect. Its behavior is explained here.
That said, checking things like cookies must be done with ifs. Just be sure you read and understand how ifs work (especially regarding directive inheritance) and you should be ok.
You may want to rethink blindly proxying to whatever host is set in the cookie. Perhaps combine the cookie with a map to limit the backends.
EDIT: If you use names instead of ip addresses in the id cookie, you'll also need a resolver defined so nginx can look up the address of the backend. Also, your default proxy_pass will append the request onto the end of the setUserCookie. If you want to proxy to exactly that url, you replace that default proxy_pass with:
rewrite ^ /index.php/setUserCookie break;
proxy_pass http://localhost:99;

Resources