0

I have a service being accessed through nginx and I wish to white list post requests only. I have written this in my nginx config file:

location / {
  if ( $request_method ~ ^(POST|PUT)$ ) {
    allow 127.0.0.1;
  }
  if ( $request_method !~ ^(GET|POST|PUT|HEAD)$ ) {
    return 405;
  }
}

This config gives me the following error -

nginx: [emerg] "allow" directive is not allowed here

On the other hand, if I write the allow directive out of the if block like this, it works.

location / {
  allow 127.0.0.1;
  if ( $request_method !~ ^(GET|POST|PUT|HEAD)$ ) {
    return 405;
  }
}

I believe this means that I can't use the allow directive in an if block. Am I doing something wrong here? If not, is there a workaround for achieving this?

Keshav Agarwal
  • 103
  • 1
  • 4

1 Answers1

3
http {
  geo $allowed_post_put {
    default 0;
    127.0.0.1 1;
    ::1 1;
  }
  map $request_method:$allowed_post_put $return_405 {
    "POST:0" 1; 
    "PUT:0" 1;
  }
}

location / {
  if ( $return_405 = 1 ) {
    return 405;
  }
}

http://nginx.org/en/docs/http/ngx_http_geo_module.html - geo module allows to create variables depending on client IP address.

http://nginx.org/en/docs/http/ngx_http_map_module.html - map module creates variables whose values depend on values of other variables.

if directive from rewrite module does not allow complex logical expressions, just single variable comparsion. So we use map module to create variable that depends both on client IP and request method.

UPD: An alike configuration with geo/map/if hack works fine for me in production.

Selivanov Pavel
  • 2,206
  • 3
  • 26
  • 48
  • I don't seem to understand the code, can you please explain how it works? – Keshav Agarwal Mar 01 '17 at 11:23
  • Updated question – Selivanov Pavel Mar 01 '17 at 11:29
  • @KeshavAgarwal Does it work for you? – Selivanov Pavel Mar 01 '17 at 12:09
  • Hi, I added the geo and map blocks in the nginx.conf file. No matter what variable name I choose for `return_405`, it always raises `invalid variable name "return_405"`. I've tried to change the variable's name to all alphabets like `foo` and ones without special characters like `return405`, but none of them seems to work. Can you tell me where the problem lies? – Keshav Agarwal Mar 02 '17 at 05:13
  • My mistake, all variables in `map` directive should have `$` prefix. I fixed the answer – Selivanov Pavel Mar 02 '17 at 09:35
  • It's giving 405 error on post/put when trying to access `localhost:8000` and `127.0.0.1:8000` using postman. – Keshav Agarwal Mar 02 '17 at 12:42
  • @KeshavAgarwal I just checked out this configuration, works for me. Also I have a lot alike configuration with geo/map hack working in production. Change `return 405` to something else, like `return 418`, and you will see if 405 is returned by nginx or by underlying code. – Selivanov Pavel Mar 02 '17 at 13:24
  • Another if block after this one was the culprit for this, I changed that and now it's working fine. Thanks a lot! :D – Keshav Agarwal Mar 03 '17 at 06:17