Forward request headers from nginx proxy server - nginx

I'm using Nginx as a proxy to filter requests to my application. With the help of the "http_geoip_module" I'm creating a country code http-header, and I want to pass it as a request header using "headers-more-nginx-module". This is the location block in the Nginx configuration:
location / {
proxy_pass http://mysite.com;
proxy_set_header Host http://mysite.com;;
proxy_pass_request_headers on;
more_set_headers 'HTTP_Country-Code: $geoip_country_code';
}
But this only sets the header in the response. I tried using "more_set_input_headers" instead of "more_set_headers" but then the header isn't even passed to the response.
What am I missing here?

If you want to pass the variable to your proxy backend, you have to set it with the proxy module.
location / {
proxy_pass http://example.com;
proxy_set_header Host example.com;
proxy_set_header HTTP_Country-Code $geoip_country_code;
proxy_pass_request_headers on;
}
And now it's passed to the proxy backend.

The problem is that '_' underscores are not valid in header attribute. If removing the underscore is not an option you can add to the server block:
underscores_in_headers on;
This is basically a copy and paste from #kishorer747 comment on #Fleshgrinder answer, and solution is from: https://serverfault.com/questions/586970/nginx-is-not-forwarding-a-header-value-when-using-proxy-pass/586997#586997
I added it here as in my case the application behind nginx was working perfectly fine, but as soon ngix was between my flask app and the client, my flask app would not see the headers any longer. It was kind of time consuming to debug.

You may pass ALL headers by adding this:
ignore_invalid_headers off;
But please consider security issues by doing this.

Related

nginx dynamic proxy_pass based on url pa

I currently have the following proxy pass definition in my nginx config file :
location /pass/ {
proxy_pass http://localhost:9999/pass/;
proxy_redirect off;
proxy_set_header Host $host;
}
This is working as expected - /pass requests are forwarded to the app running on port 9999.
Now, what I want to do is make the port forwarding part dynamic as follows :
location /pass/<input> {
{a $port variable here that is evaluated via a script (php?)}
proxy_pass http://localhost:$port/pass/;
proxy_redirect off;
proxy_set_header Host $host;
}
Requests to /pass/ABCD1234 should be forwarded to port 9898 and Requests to /pass/ABCD5678 should be forwarded to port 9797.
Note that the flow is dynamic - so, the mapping from ABCD1234 to 9898 should happen through some sort of scripting (PHP maybe?) and based on the output of the script (a port) the proxy_pass should forward the request to that port.
Please Help in this regard.
UPDATE :
Instead of getting the proxy_pass port from URI input, I would like to get this going with a cookie. So, here is the updated code block :
location /pass/ {
add_header X-debug-message $host always;
add_header X-debug-message $cookie_sdmport;
set $proxyurl http://127.0.0.1:$cookie_theport/pass/;
add_header X-debug-message $proxyurl;
proxy_pass $proxyurl;
proxy_redirect off;
proxy_set_header Host $host;
}
With this code, there is looping 301 redirect back to the browser. The moment I switch back to static port, it works again! strange! The $proxyurl in the X-debug-message looks correct on the browser. So, wondering why proxy_pass is doing a 301!
UPDATE 2:
Finally got the forwarding working with following setup:
set $targetIP 127.0.0.1;
set $targetPort $cookie_passport;
proxy_pass http://$targetIP:$targetPort$request_uri;
Not sure why the solution as posted in above keeps spinning with a 301 - I guess nginx does not like mixing dynamic and static parts in the proxy_pass param
Thank You.
You can do this using the auth_request module. It's not built by default though, you can find out if you have it by running the following:
nginx -V 2>&1 | grep -qF -- --with-http_auth_request_module && echo ":)" || echo ":("
If you see a smiley face then you are good to go.
location ~* /pass/(.*) { <- regex capture for your variable
auth_request /auth; <- location to process request
auth_request_set $proxyurl http://localhost:$upstream_http_x_port/pass/; <- set $proxyurl using value returned in x-port header of your php script
add_header x-my-variable $1; <- Pass variable from regex capture to auth location
proxy_pass $proxyurl;
}
Then a location to handle the auth subrequests:
location /auth {
internal; <- make location only accessible to internal requests from Nginx
proxy_set_header x-my-variable $http_x_my_variable; <- pass variable to php
proxy_pass_request_body off; <- No point sending body to php
proxy_set_header Content-Length "";
proxy_pass http://your-php-script/file.php;
}
This module is actually meant for access control, so if your php script returns response code 200 then client will be allowed access, if it returns 401 or 403 then access will be denied. Is you dont care about that then just set it to always return 200.
Do whatever evaluation you need and have your php return the port in the header defined earlier:
header('X-Port: 9999');
This now sets the variable for your proxy_pass directive port number and Nginx does the rest.

Proxy pass remote_user with nginx

I have a firewall that is the SSL terminator and sets the remote_user header. This header should be passed onto an application, however we have an nginx proxy sitting in the middle.
Browser over SSL -> Firewall proxy -> Nginx proxy -> App
I cannot for the life of me figure out how to pass the remote_user header to the App from the Firewall. Nginx seems to swallow it. $remote_user doesn't have it (which makes sense). $http_remote_user doesn't have it (which doesn't make sense). I tried to find it in $proxy_add_* but couldn't.
How do I proxy pass the remote_user header when the SSL terminator isn't nginx?
EDIT1: Other things I have tried:
proxy_pass_request_headers on;
proxy_pass_header remote_user;
proxy_set_header other_user $http_remote_user;
proxy_pass http://scooterlabs.com/echo;
You need to use a combination of proxy_pass_request_headers on and underscores_in_headers on since your header contains an underscore.
underscores_in_headers needs to be placed in your http block.
See: http://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers
OLD ANSWER
You are looking for proxy_pass_header
See: Module ngx_http_proxy_module

nginx how to proxy_pass to a proxy

I have a nginx server running. I want to issue some request to a corporate proxy and return the result to the client.
Let us say the client issues a request to http://ip:port/redirect/google.com, the server should issue the request to https://username:password#ip_proxy with the requested url as parameter.
I have found questions that are close to my problem:
http://serverfault.com/questions/732063/nginx-proxy-pass-reverse-proxying-behind-corporate-firewall
and
http://stackoverflow.com/questions/11865119/how-to-configure-nginx-behind-a-corporate-proxy
assume 205.100.100.100:80 is the proxy URI and
XXVzYTQzMjXXXDpCb25qXXyMQ== the credential for the proxy
one says we should rewrite the url and then pass it to proxy_pass directive
location ^~ /redirect/ {
rewrite ^/redirect/(.*) https://google.com;
proxy_pass_header on;
proxy_set_header Proxy-Authorization "XXVzYTQzMjXXXDpCb25qXXyMQ==";
proxy_pass https://205.100.100.100:80;
}
I think it does not work as intended cause it is showing as a redirection (http code 301)
Another one says that we should use the Host header and specify original url with it:
location ^~ /redirect/ {
proxy_pass_header on;
proxy_set_header Host "https://google.com";
proxy_set_header Proxy-Authorization "XXVzYTQzMXXXpCb25XXyMQ==";
proxy_pass https://205.100.100.100:80;
}
Doesn't work. They also say that a proxy read the url specified as an url parameter something like: http://proxy:port/url_requested so:
location /redirect {
rewrite ^/redirect/(.*)"http://205.100.100.100:80/https://google.com" break;
proxy_pass_header on;
proxy_set_header Proxy-Authorization "XXVzYTQzMXXXpCb25XXyMQ==";
proxy_pass http://corporate-proxy.mycorp.com:8080;
}
Should work ?
As you can see I do not know how to specify the username, password to the proxy. I tried a Proxy-Authorization header with "XXVzYTQzMjXXXDpCb25qXXyMQ==" with no result.
I tried the http_upstream module too, no result. I cannot use proxy_pass http://user:pwd#205.100.100.100:80 because nginx use ":" for parsing the port, it gives me error specifying port.
How should I proceed ?
Maybe it comes from the entire configuration or they way I use the proxy? Don't know.
This isn't possible, nginx isn't able to talk to forward proxy.
See also this thread in the official mailing list: http://mailman.nginx.org/pipermail/nginx-devel/2013-March/003534.html

NGINX Reverse Proxy - Completely "shadowing" an URL subtree

I have two servers, an application server running (backend) listening at example.com:8000 and an NGINX as reverse proxy server (frontend) listening at example.com:443.
The easy part is to configure NGINX so that all requests get proxied through to the backend system:
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:8000;
}
This works like a charm; however there is another additional requirement, which I could not figure out how to realize yet. There is subtree of URLs at the backend system considered "ugly", say example.com:8000/my/ugly/path/, that should never be seen by the end user but instead "replaced" by example.com:443/pretty-path/. The problem is, that the backend system does not know about the "shadowing" of this subtree and generates URLs containing /my/ugly/path/ (both in HTTP headers and HTML content). So what is the best way to make /my/ugly/path/ transparently disappear?
You can use the sub_filter directive. Note that nginx must be built to support this feature; use nginx -V to display configured options, look for --with-http_sub_module, or test your config file with nginx -t. Untested sample config below!
location / {
sub_filter 'www.example.com/my/ugly/path'
'www.example.com/pretty/path';
sub_filter_once off;
}
Ref. nginx sub_filter documentation here.

Nginx proxy_pass with $remote_addr

I'm trying to include $remote_addr or $http_remote_addr on my proxy_pass without success.
The rewrite rule works
location ^~ /freegeoip/ {
rewrite ^ http://freegeoip.net/json/$remote_addr last;
}
The proxy_pass without the $remote_addr works, but freegeoip does not read the x-Real-IP
location ^~ /freegeoip/ {
proxy_pass http://freegeoip.net/json/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
Then, I'm adding the ip to the end of the request, like this:
location ^~ /freegeoip/ {
proxy_pass http://freegeoip.net/json/$remote_addr;
}
but nginx report this error: no resolver defined to resolve freegeoip.net
If the proxy_pass statement has no variables in it, then it will use the "gethostbyaddr" system call during start-up or reload and will cache that value permanently.
if there are any variables, such as using either of the following:
set $originaddr http://origin.example.com;
proxy_pass $originaddr;
# or even
proxy_pass http://origin.example.com$request_uri;
Then nginx will use a built-in resolver, and the "resolver" directive must be present. "resolver" is probably a misnomer; think of it as "what DNS server will the built-in resolver use". Since nginx 1.1.9 the built-in resolver will honour DNS TTL values. Before then it used a fixed value of 5 minutes.
It seems a bit strange that nginx is failing to resolve the domain name at runtime rather than at configuration time (since the domain name is hard coded). Adding a resolver declaration to the location block usually fixes dns issues experienced at runtime. So your location block might look like:
location ^~ /freegeoip/ {
#use google as dns
resolver 8.8.8.8;
proxy_pass http://freegeoip.net/json/$remote_addr;
}
This solution is based on an article I read a while back - Proxy pass and resolver. Would be worth a read.
If anyone is stll experiencing trouble, for me it helped to move the proxy_pass host to a seperate upstream, so I come up with something like this
upstream backend-server {
server backend.service.consul;
}
server {
listen 80;
server_name frontend.test.me;
location ~/api(.*)$ {
proxy_pass http://backend-server$1;
}
location / {
# this works mystically! backend doesn't...
proxy_pass http://frontend.service.consul/;
}
}
Another way is that you can provide your host ip address and port number instead of host URL in proxy_pass like below:
Your Code:
proxy_pass http://freegeoip.net/json/;
Updated Code
proxy_pass http://10.45.45.10:2290/json/;
So you want this to work:
location ^~ /freegeoip/ {
proxy_pass http://freegeoip.net/json/$remote_addr;
}
but as pointed out by Chris Cogdon, it fails because at run-time, nginx does not have the domain name resolved, and so proxy_pass requires a resolver (see also, proxy_pass docs).
Something that seems to work (though may be a hack!) is to do something that triggers nginx to resolve the domain name at start-up and cache this.
So, change the config to include a location (for the same host) without any variables (as well as your location with variables):
location = /freegeoip/this-location-is-just-a-hack {
# A dummy location. Use any path, but keep the hostname.
proxy_pass http://freegeoip.net/this-path-need-not-exist;
}
location ^~ /freegeoip/ {
proxy_pass http://freegeoip.net/json/$remote_addr;
}
The host will now be resolved at start-up (and its cached value will be used in the second location at run-time). You can verify this start-up resolution behavior by changing hostname (freegeoip.net) to something that doesn't exist, and when you run nginx -t or nginx -s reload you'll have an emergency error ([emerg] host not found in upstream). And of course you can verify that the cached value is used at run-time by leaving out any resolver, hitting your location block, and observing no errors in the logs.
You could also mention your nginx server port in the proxy_pass uri. This solved the issue for me.
Try to use dig / nslookup to make sure this is still nginx issue.
In my case the issue was that one of dns server between my nginx and root dns was not respecting TTL values that are as small as 1 second.
Nginx does not re-resolve DNS names in Docker

Resources