Score:0

The `location` directive has no effect when it contains a variable

no flag

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

Score:3
gr flag

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).

mehov avatar
no flag
Thank you for your help, this approach worked
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.