skip to Main Content

How can I deny access to nginx if the path contains /local or /local-int to all networks except the local one?
For example https://example.com/api/local/settings. I tried this, but when accessed locally, the request goes to /etc/nginx/html/api/local/settings,and not to the desired backend

location = (local|local-int) {
    allow 10.150.0.0/16;
    allow 10.160.0.0/16;
    allow 10.170.0.0/16;
    deny all;
}

I have about 20 such sites, and I’m trying to come up with a solution that would not be tied to a specific location

I summarize: if I access a site from allowed ip, then it should show the page to which I am accessing, and if from a deny list, then 403

Config example:

server {
    listen          ip:80;
    listen          ip:443 ssl;

    server_name test.com;
    if_modified_since off;
   

    location /api {
        proxy_pass https://api.example.com;
    }
    location ~ (/local) {
    allow 10.150.0.0/16;
    allow 10.160.0.0/16;
    allow 10.170.0.0/16;
    deny all;
    }
}

2

Answers


  1. This will simply work with both of your locations, since both starts with /local

    location ~ (/local) {
        allow 10.150.0.0/16;
        allow 10.160.0.0/16;
        allow 10.170.0.0/16;
        deny all;
    }
    
    Login or Signup to reply.
  2. Nginx takes a = location modifier as an exact match (docs are here). If you want to make a location that will catch every URI containing /local substring (obviously including /local-int), you can use a regex one:

    location ~ /local {
        ...
    }
    

    The ^~ modifier makes the location block in @user973254 answer (original answer version, already fixed) a prefix one with the greater priority than any regex locations, so it will overtake only the URIs starting with /local (obviously not including /api/local/settings from your example).

    However if your web backend requires an additional URI processing (which is a most common case nowadays), you’ll need at least to replicate your main location behavior with this new location. Fortunately, there is a way to avoid such a problems, and can be easily applied to an arbitrary number of sites as you ask for in your original question. You can check required conditions to make a decision for blocking the request or not using the (very powerful) map block feature. And since we want to match address against a list of subnets, we will use a chain of map and geo blocks. To use regexes (PRCE/PCRE2 syntax) for a map block match use a ~ string prefix (~* for case-insensitive match), strings containing some special characters (e.g. curly braces) should be single- or double-qouted. Here is a generic example (you’ll need only the first line of the following map block to fulfill your question requirements):

    map $uri $restricted {
        ~/local                1; # regex to match any URI containing '/local' substring
        ~^/private/            1; # regex to match any URI starting with '/private'
        ~*.jpe?g$             1; # regex to match any URI ending with '.jpg' or '.jpeg' (case-insensitive)
        /some/protected/page/  1; # exact URI match (string isn't starting with '~')
        ... any number of additional rules here
        default                0;
    }
    geo $deny {
        10.150.0.0/16          0;
        10.160.0.0/16          0;
        10.170.0.0/16          0;
        default                $restricted;
    }
    server {
        ...
        if ($deny) { return 403; }
        ...
    }
    

    You can swap the logic to check the URI first (it can be some performance impact since the regex matching will be performed for every request including requests from the non-restricted networks, however if the majority of requests come from public addresses, there will be no significant difference). That way you can have a common non-restricted subnes list and per-site URI lists:

    geo $restricted {
        10.150.0.0/16          0;
        10.160.0.0/16          0;
        10.170.0.0/16          0;
        default                1;
    }
    map $uri $deny1 {
        ~/local                $restricted;
        default                0;
    }
    map $uri $deny2 {
        ~^/admin               $restricted;
        default                0;
    }
    server {
        server_name site1.com;
        if ($deny1) { return 403; }
        ...
    }
    server {
        server_name site2.com;
        if ($deny2) { return 403; }
        ...
    }
    

    Of course, you are not limited to use 403 return code using this solution (which is the case when you are using allow/deny directives). It also has nothing to do with the famous "If is evil" article since this if is used in server context.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search