nginx how to proxy_pass to a proxy - nginx

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

Related

NGINX proxy_pass or proxy_redirect

Need help on Nginx proxy_pass.
From outside Nginx URL will be hit like this:
http://some-IP:8080/v2/platform/general/activity/plan?.....
my downstream service looks like this:
http://another-IP:8080/activity/plan?...
I want to get rid of
/v2/platform/general
from original public url and call my downstream service like above.
In Nginx, how do I redirect public access URL to downstream service?
I tried this:
location /v2/platform/general/ {
rewrite ^/(.*) /$1 break;
proxy_redirect off;
proxy_pass http://another-IP:8080;
proxy_set_header Host $host;
But it didn't work, any help appreciated.
proxy_pass and proxy_redirect have totally different functions. The proxy_redirect directive is only involved with changing the Location response header in a 3xx status message. See the NGINX proxy_redirect docs for details.
Your rewrite statement does nothing other than prevent further modification of the URI. This line needs to be deleted otherwise it will inhibit proxy_pass from mapping the URI. See below.
The proxy_pass directive can map the URI (e.g. from /v2/platform/general/foo to /foo) by appending a URI value to the proxy_pass value, which works in conjunction with the location value. See this document for details.
For example:
location /v2/platform/general/ {
...
proxy_pass http://another-IP:8080/;
}
You may need to set the Host header only if your upstream server does not respond correctly to the value another-IP:8080.
You may need to add one or more proxy_redirect statements if your upstream server generates 3xx status responses with an incorrect value for the Location header value.

Nginx reverse proxy - Ask for basic auth credentials and pass them on to the target API

I wrote a REST API in Haskell delivering HTML to be viewed in a browser and am currently trying to host it using Nginx's reverse proxy.
My backend however requires Basic Auth credentials, which the Nginx server doesn't provide.
How can I configure the reverse proxy, so that it asks for credentials when a GET request is made via the browser, but doesn't validate them and passes them on to the backend?
I have tried about half a dozen suggestions on stackoverflow, reddit etc. but haven't found a working solution.
This is my current config:
server {
listen 80;
location / {
proxy_pass http://127.0.0.1:8000/;
auth_basic "user-realm";
proxy_set_header X-Forwarded-User $http_authorization;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
proxy_pass_header Accept;
proxy_pass_header Server;
proxy_http_version 1.1;
proxy_set_header Authorization $http_authorization;
#proxy_pass_header Authorization;
proxy_set_header ns_server-ui yes;
}
}
A few of the articles I tried or read are the following:
https://www.reddit.com/r/couchbase/comments/2wksmj/authorization_headers_when_using_nginx_as_a/
https://serverfault.com/questions/511206/nginx-forward-http-auth-user
https://serverfault.com/questions/230749/how-to-use-nginx-to-proxy-to-a-host-requiring-authentication
All or most seem to focus on how to let Nginx take over the authorization, however I want it to only pass on the credentials entered in the browser.
After several hours of trying I found out that my config isn't even used since for some reason it always matches with the default config.
I didn't figure out how to solve this, but I got the proxy to work as I intended it when I changed this
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
to this
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
proxy_pass http://localhost:8000;
}
Nginx then automatically asks for the credentials and passes them on to the backend.
(So in the end my config simply wasn't being used and therefore nothing rerouted to the backend)
Hopefully this saves someone some searching time in the future :)

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.

Forward request headers from nginx proxy server

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.

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