0

What I want

For my /admin location, whitelist several trusted IP addresses, and block everyone else.

What I have

http {
    map $remote_addr $admin_block_path {
        default "/admin";
        1.1.1.1 "/not-admin";
        8.8.8.8 "/not-admin";
    }

    # …

    server {
        add_header X-Test-Admin "$admin_block_path";
        location $admin_block_path {
            return 403;
        }
    }

}

The idea is to use map do conditionally determine a path to block from a visitor IP address. Store it in a variable, and use that variable in location. If the request comes from a non-trusted IP, the variable will contain /admin, and therefore the location will block /admin.

What is the problem

The location does not block access to /admin.

  • I can confirm using add_header that the variable is set based on the IP address correctly. When visiting from a non-trusted IP I do get /admin, and respectively /non-admin when visiting from a trusted IP.
  • If I replace location $admin_block_path with hardcoded location /admin, the request does get blocked
  • Tried playing with double quotes, no effect
  • Tried taking the slash outside of the variable, no effect

Note

Unfortunately, I can't simply hardcode allow/deny into some /admin location because that would overwrite PHP pass configuration I have elsewhere in the config. Because of this, I need to have a dedicated /admin location only if I know I'll reject the request anyway, and no PHP will be needed.

Nginx version: 1.20.1

mehov
  • 568
  • 1
  • 5
  • 14

1 Answers1

3

All the locations in the nginx config are compiled at the startup. You can't use variables for the location directive parameter (as well as you can't use variables inside the regex patterns or with some other directives). Moreover when nginx directive documentation doesn't explicitly states that you can use variables for the parameter of some directive, generally it means you cannot. You should use some other technique to achieve what you want to. One of solutions can be the following:

map $remote_addr $admin_block_path {
    1.1.1.1 "";
    8.8.8.8 "";
    default 1;
}

server {
    if ($admin_block_path) {
        rewrite ^/admin /blocked;
    }
    location = /blocked {
        internal;
        return 403;
    }
    ...
}

An exact match location have a greater priority over the regex one so it should not break your PHP handler configuration. Making that location internal you can assured it won't be accessible directly (only via rewrite rule).

Ivan Shatsky
  • 2,360
  • 2
  • 6
  • 17