I have an Nginx config with the redirect to holding pages:
location / {
...
if ($setholdingpage = 'True') {
rewrite (^.*$) /holding-page last;
}
proxy_pass $backend;
}
Also, I have a list of IPs that should be whitelisted and not redirected to holding pages. How it's possible to do?
You can use the Nginx geo module to create a variable based upon client IP address, you can specify individual IP addresses or CIDR ranges:
geo $bypassip {
default 0;
64.233.160.0/19 1;
66.102.0.0/20 1;
}
Then override your variable if the IP matches one in your list:
if ($bypassip = 1){
set $setholdingpage False;
}
I use a similar setup to block certain geographic regions but still allow Google crawlers to access my site.
You can make use of the allow deny directives.
If I get you correct the whitelist will be your $setholdingpage variable in some sort?
try this
server {
error_page 403=#holding;
location / {
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
proxy_pass http://backend;
}
location /#holding {
root /path/to/your/holding/html;
index holdingv1.html;
}
}
This will send the non-whitelisted IPs to the error-page you specified. The error_page can be in the location as well.
Not tested but this should do the trick.
References:
http://nginx.org/en/docs/http/ngx_http_access_module.html#allow
Related
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.
I'm setting up an NGINX server and need to configure it to allow only certain IPs access to the root of a react application, but allow all to a certain subfolder (react route). Basically I need to allow all traffic to /sub/ but only a handful of IPs to the home directory /.
I tried
location /sub/* { allow all;}
location / {
allow x.x.x.x;
deny all;}
but was getting a 403 error when using any other IP address except the 'x.x.x.x'.
What's the correct way to achieve this?
Thanks.
Here is what you can try to do:
map $uri $disallow_by_route {
~^/subroute/ ""; # allow /subroute/... for all
default 1;
}
map $remote_addr $disallow {
x.x.x.x ""; # some allowed IP
y.y.y.y ""; # another allowed IP
default $disallow_by_route;
}
server {
...
location / {
if ($disallow) { return 403; }
...
}
}
However if your allowed pages used some assets (js, css, images etc.) from some other path than /subroute/... this config won't let them to load on restricted IPs. You can try to allow them checking the value of HTTP Referer header with a more complex map blocks chain:
map $http_referer $disallow_by_referer {
# use a regex for your actual domain here
~^https?://example\.com/subroute/ "";
default 1;
}
map $uri $disallow_by_route {
~^/subroute/ "";
# list all the other possible assets extensions (png, gif, svg, webp etc.) here
~\.(?:js|css)$ $disallow_by_referer;
default 1;
}
map $remote_addr $disallow {
x.x.x.x ""; # some allowed IP
y.y.y.y ""; # another allowed IP
default $disallow_by_route;
}
server {
...
location / {
if ($disallow) { return 403; }
...
}
}
Please note that this solution won't work if your server configuration (or react app itself) sets the referer policy to no-referer.
I need to route traffic based on the http request origin. I have two environments and we need to redirect every http request for "/us-en" to Environment1 and others to Environment2 using "$http_referer".
Redirection based on location works.
location ~ /us-en {
proxy_pass Environment1;
proxy_set_header Host Environment1;
}
With '$http_referer' the below option does not work. Request your suggestion on the same.
if ($http_referer ~ ^https?://dev.xyz.com/us-en){
rewrite ^/us-en(/*)$ HOME_PAGE$1 break;
proxy_pass Environment1;
}
Error: nginx: [emerg] "proxy_pass" directive is not allowed here in /opt/nginx/conf/nginx.conf.
Note: By default all the traffic goes to Environment2 as an upstream configuration is present.
# needed if your proxy destination specified with domain name instead of IP address
resolver 8.8.8.8;
location /home/ {
proxy_set_header Host HOST1;
# setup other proxied headers if needed
if ($http_referer ~ ^https?://dev.xyz.com/home) {
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
proxy_pass https://HOST1:8080; # this can be specified by IP address
}
}
With such configuration requests to your_domain.com/home/path/file from dev.xyz.com/home/... (but not from dev.xyz.com/any/other/path!) will be proxied to https://HOST1:8080/HOME_PAGE/path/file. If you specify your proxy destination with domain name instead of IP address, you'll need to specify the additional parameter resolver in your server config. You can use your local name server if you have one, or use something external like Google public DNS (8.8.8.8) or DNS provided for you by your ISP. Anyway such configuration leads to additional DNS lookups, so if you can, specify your proxy destination with IP address.
Update
There is another way to do it with the valid_referers directive:
# needed if your proxy destination specified with domain name instead of IP address
resolver 8.8.8.8;
location /home/ {
proxy_set_header Host HOST1;
# setup other proxied headers if needed
valid_referers example.com/home;
if ($invalid_referer = "") {
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
proxy_pass https://HOST1:8080; # this can be specified by IP address
}
}
Update # 2020.11.11
Besides this answer somehow achieved a score of 5, the given solution has an extremely bad design (it isn't a good approach to have different content handlers in the location and the nested if block; moreover, having an if block with any directive other than from the nginx rewrite module should be avoided if possible) and won't work at all on early nginx versions (I wanna cry when I look at some of my early answers). An original OP question was
The logic should be like below but has some syntax mistakes.
if ($http_origin ~ '^http?://(dev.xyz.com/home)') {
set $flag 'true';
}
if ($flag = 'true') {
location /home/ {
proxy_pass "https://HOST1:8080/HOME PAGE/";
}
}else{
Do Not proxy pass
}
It is unclear what do not proxy pass means. If it means returning some HTTP error (for example, HTTP 403 Forbidden), it can be done with the following configuration:
location /home/ {
if ($http_referer !~ ^https?://dev.xyz.com/home) {
return 403;
}
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
proxy_set_header Host HOST1;
# setup other proxied headers if needed
proxy_pass https://HOST1:8080; # this can be specified by IP address
}
If do not proxy pass means to serve the request locally, the solution is more complex:
map $http_referer $loc {
~^https?://dev.xyz.com/home loc_proxy;
default loc_local;
}
server {
...
location /home/ {
try_files /dev/null #$loc;
}
location #loc_proxy {
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
proxy_set_header Host HOST1;
# setup other proxied headers if needed
proxy_pass https://HOST1:8080;
}
location #loc_local {
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
root /path/to/required/page;
...
}
The try_files /dev/null #the_named_location; trick is taken from this excellent answer.
However now the edited OP's question states for a different requirements, which also could be achieved with the map directive help:
map $http_referer $environment {
~^https?://dev.xyz.com/home Environment1;
default Environment2;
}
server {
...
location /home/ {
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
proxy_set_header Host $environment;
# setup other proxied headers if needed
proxy_pass https://$environment;
}
I'm suffering a brute force attack, I need to look the access log and get the routes to block:
location /foo/{ deny all;}
location /foo2/{deny all;}
Since there are infinite routes I would need to spend a good time looking all the access log to block connections at those specific routes.
Instead of denying specific routes could I configure nginx to block connections on all other routes that don't exists in my application?
[]'s
Pedro
Make a list of first level folders and then you can block access to all not of them with a access required
events {
worker_connections 1024;
}
http {
map $request_uri $allowed_routes {
~^/(css|assets|js)/ "off";
/ "off";
default "Not allowed";
}
server {
auth_basic $allowed_routes;
auth_basic_user_file /var/www/html/.htpasswd;
location / {
echo "You can reach API $uri and $args, $arg_hash_folder";
}
}
}
You might consider other approaches also, but this one works well for me
I am trying to deny all access on a certain server block.
server {
listen 8080;
server_name myserver;
root /srv/nkosl;
deny all;
location /mydistro {
return 302 https://www.ubuntu.com/;
}
location /files {
autoindex on;
}
}
This denies access on files location (and successfully returns 403 forbidden message).
However I can still access myserver:8080/mydistro and it redirects me to ubuntu page.
How can I deny the /mydistro location as well?
this answer describes why allow/deny does not work with return/rewrite.
one way to work around it is indeed using ifs, however ifs can be tricky in nginx in certain cases and allow/deny can also take a network mask...
Try to completely remove the /mydistro location. Or, at least, comment the line return 302 https://www.ubuntu.com/;.
To redirect selectively, try this:
location /mydistro {
if ($remote_addr != 127.0.0.1) {
rewrite ^ https://www.ubuntu.com;
}
}