menu

Questions & Answers

Nginx create a map dynamically based on request/response values

I am looking for a way to accomplish my below goal within the constraints of Nginx syntax.

Scenario / Goal:

For various reasons, I need Nginx to validate some values that are returned from an external authentication system (further processing). This seemed to be very easy to accomplish by testing the values using an Nginx map, however it doesn't appear it's as simple as I imagined.

The entire nginx and auth system already works fine, and I have the header values I need.

GOAL: The only thing I'm I'm trying to do is just add a map of values based on some response header data.

Problem:

Nginx map cannot be used in location/server blocks, it can only be used in http blocks.

When I test the below config with nginx -t, it says:

nginx: [emerg] "map" directive is not allowed here in /etc/nginx/sites-enabled/test.conf:27

I realize it's not allowed in location/server blocks but I am not sure how to create the map based on values ran within a server block.

Here is my ideal config (that does not work):

The ONLY thing I'm changing from the existing working config is adding the map itself and the if() statement at the bottom

site.conf:

server {
    [...]

    # Trigger an auth request
    auth_request /auth;

    # THIS VALUE MUST BE SET BEFORE MAKING THE MAP!!
    auth_request_set $auth_resp_x_auth_user $upstream_http_x_auth_user;

    location = /auth {
      # stuff omitted
    }

    # Create a map based on the result of `auth_request_set` above
    map $auth_resp_x_auth_user $is_invalid {
      default "1";
      tom "0";
      olivia "0";
      joe "0";
    }

    # stuff omitted

    location / {
     # Return 403 if the user is not valid
     if ($is_invalid) {
       return 403; 
     }
    # stuff omitted
}

Questions:

  • How can I create this map of values based on my existing $auth_resp_x_auth_user?
  • If it's not possible, is there any workaround way to accomplish this goal?
Answers(1) :

I think you're missing the key point about variables created by map: they are lazily loaded. So there's nothing wrong to take your "ideal config" and simply put the map to the http {} block.

Indeed there's a problem with that: NGINX will check if the variable is defined during configuration parsing. You can try to make a dummy definition like this:

http {
    map $arg_dummy $auth_resp_x_auth_user {
        default "1";
    }
    # Create a map based on the result of `auth_request_set` above
    map "$auth_resp_x_auth_user" $is_invalid {
      default "1";
      tom "0";
      olivia "0";
      joe "0";
    }
    ...
}

The dummy definition is only there to satisfy NGINX configuration check stage. At run-time, it should use the value you set via auth_request_set.