Nginx - proxy pass subpaths only - nginx

I would like to proxy the subpaths of my website to another service:
http://some-web-site.com/friends/ - renders /friends/index.html
http://some-web-site.com/friends/ [not empty request path] - proxy to another service.
Currently I have the following Nginx configuration:
location /programming/ {
(...)
proxy_pass http://tomcat:8080/friends;
}
But unfortunately this proxies /programming/ to http://tomcat:8080/friends.

Use an exact match location block to extract specific URIs for special handling:
location = /programming/ {
...
}
location /programming/ {
...
proxy_pass http://tomcat:8080/friends;
}
See this document for details.

Related

How to drop path in rewrite and proxy pass the args in nginx

Example request - http://localhost/iframe?ip=192.168.0.237
I want to proxy pass the request to the value of IP and remove the path and args after localhost/ .
Ideally the proxy_pass should point to 192.168.0.237 and the URL should be http://localhost/.
localhost /iframe {
rewrite ^/(iframe/.*)$ http://localhost/ permanent;
proxy_pass $arg_ip;
}
I'm not sure whether rewrite is the proper way to address this problem.
I would use the argument ip and a rewrite to remove the iframe location
server {
listen 8085;
location /iframe {
rewrite ^/iframe(.*)$ /$1 break;
proxy_pass http://$arg_ip;
}
}
server {
listen 8080;
location / { return 200 "$host$uri"; }
}
Security Notice
I just have a feeling you should whilelist the upstream servers accepted as arguments. If not this will be a wildcard proxy to every single http-server reachable in the network. This is a easy to use SSRF attack vector. So please add some extra layer of security.
SSRF Explained:
Let's say we use this configuration without any further security. Given the folowing NGINX config:
server {
listen 8085;
location /iframe {
rewrite ^/iframe(.*)$ /$1 break;
proxy_pass http://$arg_ip;
}
}
# Server for iframe service
server {
listen 8080;
root /usr/share/nginx/;
location / { return 200 "$host$uri\n"; }
}
# Private Server Section here!
server {
listen 8086;
allow 127.0.0.1;
deny all;
.....
location / {
index welcome.html;
}
}
Trying to reach the secret server directly
curl -v EXTERNALIP:8086
will fail with HTTP 403.
The NGINX will just allow connections form localhost/127.0.0.1 as defined in the allow/deny directives.
But lets try the iframe with the ip argument.
$# curl localhost:8085/iframe?ip=127.0.0.1:8086
Welcome to our very secure server! Internals only!
It prints the content of the secret server. Whitlisting a proxy-pass like this is never a good idea regardless its working or not.

Nginx - Decode URL query parameter and forward it as request header

I need to send some basic auth credentials (es. user:pass) to nginx in the form of query parameter (es. http://example.com?BASIC_AUTH=dXNlcjpwYXNz) and being able to forward them in the more usual Authorization: Basic dXNlcjpwYXNz header form to a target server behind the proxy.
I'm already able to retrieve the value of the encoded auth string with a regular expression. The problem is that very often that value may contain some character that need to be percent-encoded in the URL. Es. user:pass! -> ?BASIC_AUTH=dXNlcjpwYXNzIQ== becomes ?BASIC_AUTH=dXNlcjpwYXNzIQ%3D%3D
Therefore, when I forward the request to the target server, I end up specifing Authorization: Basic dXNlcjpwYXNzIQ%3D%3D which the target server will reject, giving a 401 Unauthorized.
How can I force nginx to decode the auth string before setting the Authorization header? Thanks in advance for your help.
Note: I can't send the auth string in the Authorization header in the first place due to some application-specific constraints.
"Pure" nginx solution
Unfortunately nginx does not provide a rich string operations set. I think there isn't a way to do global search-and-replace through some string (which can be a solution if we could replace all %2B with +, %2F with / and %3D with =). However there are circumstances under which nginx performs an urldecoding of some string - when this string becomes a part of an URI which will be forwarded to an upstream proxy server.
So we can add a value of a BASIC_AUTH request argument to the URI and make a proxy request to ourself:
# Main server block
server {
listen 80 default_server;
...
location / {
if ($arg_basic_auth) {
# "basic_auth" request argument is present,
# append "/decode_basic_auth/<BASE64_token>" to the URI
# and go to the next location block
rewrite ^(.*)$ /decode_basic_auth/$arg_basic_auth$1 last;
}
# No "basic_auth" request argument present,
# can do a proxy call from here without setting authorization headers
...
}
location /decode_basic_auth/ {
# This will be an internal location only
internal;
# Remove "basic_auth" request argument from the list of arguments
if ($args ~* (.*)(^|&)basic_auth=[^&]*(\2|$)&?(.*)) {
set $args $1$3$4;
}
# Some hostname for processing proxy subrequests
proxy_set_header Host internal.basic.auth.localhost;
# Do a subrequest to ourselfs, preserving other request arguments
proxy_pass http://127.0.0.1$uri$is_args$args;
}
}
# Additional server block for proxy subrequests processing
server {
listen 80;
server_name internal.basic.auth.localhost;
# Got URI in form "/decode_basic_auth/<BASE64_token>/<Original_URI>"
location ~ ^/decode_basic_auth/([^/]+)(/.*)$ {
proxy_set_header Authorization "Basic $1";
# Setup other HTTP headers here
...
proxy_pass http://<upstream_server>$2$is_args$args;
}
# Do not serve other requests
location / {
return 444;
}
}
Maybe this is not a very elegant solution, but it is tested and works.
OpenResty / ngx_http_lua_module
This can be easily solved with openresty or ngx_http_lua_module using ngx.escape_uri function:
server {
listen 80 default_server;
...
location / {
set $auth $arg_basic_auth;
if ($args ~* (.*)(^|&)basic_auth=[^&]*(\2|$)&?(.*)) {
set $args $1$3$4;
}
rewrite_by_lua_block {
ngx.var.auth = ngx.unescape_uri(ngx.var.auth)
}
proxy_set_header Authorization "Basic $auth";
# Setup other HTTP headers here
...
proxy_pass http://<upstream_server>;
}
}

Nginx permanent redirect to different url for certain subdirectory

I have 2 Nginx servers serving static files from 2 different subdomains of an unknown parent domain, let's say <env>.foo.<domain>.com and <env>.bar.<domain>.com.
I want to configure the nginx server for <env>.foo.<domain>.com so that if the url has the subdirectory cat or dog I want to redirect to <env>.bar.<domain>.com/<subdirectory>/<rest of url>.
E.g.
http://dev.foo.mydomain1.com/cat/22 -> http://dev.bar.mydomain1.com/cat/22
http://dev.foo.mydomain1.com/dog/22 -> http://dev.bar.mydomain2.com/dog/22
http://dev.foo.mydomain2.com/dog/22 -> http://dev.bar.mydomain2.com/dog/22
http://dev.foo.mydomain1.com/bird/22 -> [no redirect]
The <env> and <domain> portions of the domain are dynamic depending to the environment to which the servers are deployed, but are common between the 2 nginx boxes.
I imagine it being something like:
server {
location ??? /(cat|dog) {
return 301 $scheme://???/$1$is_args$query_string;
}
}
But my nginx skills are not quite there...
rewrite ^/oldlocation$ http://www.newdomain.com/newlocation permanent;
Refer digital oceans's article on "How To Create Temporary and Permanent Redirects with Nginx" for further details.
What you can do is below
http {
map $http_host $host_to_send_to {
default $http_host;
dev.foo.mydomain1.com dev.bar.mydomain1.com;
dev.foo.mydomain2.com dev.bar.mydomain2.com;
}
server {
server_name dev.foo.mydomain1.com;
location /(cat|dog) {
return 301 $scheme://$host_to_send_to$request_uri$is_args$query_string;
}
}
}
You will need to add it to each server block which needs to handle the redirect.
Edit-1: Dynamic host name handling
You can handling host names dynamically also user re patterns and groups
map $hostsname $host_to_send_to {
default $http_host;
~(.*).foo.mydomain1.com $1.bar.mydomain1.com;
~(.*).foo.mydomain2.com $1.bar.mydomain2.com;
}

nginx reverse proxy docker - filter by location

I am following this article http://jasonwilder.com/blog/2014/03/25/automated-nginx-reverse-proxy-for-docker/ and I am wondering how "filtering" of requests by location (inspecting URL) can be set up.
For instance when request comes to www.example.com/a it will go to container A, when request comes to www.example.com/b it be redirected to container B.
You can configure two upstreams and redirect from /a to first one and from /b to second one. Something like this:
upstream first {
server 172.17.0.4:5000;
server 172.17.0.3:5000;
}
upstream second {
server 172.17.0.5:5000;
}
server {
...
location /a {
proxy_pass http://first;
include /etc/nginx/proxy_params;
}
location /b {
proxy_pass http://second;
include /etc/nginx/proxy_params;
}
}

How to use nginx as reverse proxy to direct localhost:9292 to a sub domain foo.localhost/?

I've learned how to pass localhost:9292 to localhost/foo with the following directive:
location /foo {
proxy_pass http://localhost:9292;
}
but I want do something like
foo.localhost -> localhost:9292
Is there a way I can do that?
If foo.localhost is your sub domain name and you want to proxy pass sub-domain to main-domain, you can use proxy_pass and you can learn a little more about server directive if needed. An example:
server {
listem 8080;
host sub.main.com;
...
location / {
proxy_pass http://main.com;
break;
}
}
server {
listen 8081;
host main.com;
...
location / {
//do something
}
}
This is proxy pass, means when access sub.main.com, actually it finally dealt by main.com, but the client side still shows sub.main.com. If you want client side shows main.com, here should use redirect but not proxy_pass.

Resources