Nginx location - Distinguish requests by arguments or queries

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Nginx location - Distinguish requests by arguments or queries

Olaf van der Spek
Hello,

We have a case when we should permit different methods on the same location
but different requests.

For location /test we should permit only POST, for /test?doc we should
permit only GET methods.

Our config example:
----
  location /test {
    error_page  403 =405 /custom-error;

    limit_except POST {
      deny all;
    }

    proxy_pass http://test/in;
  }
----

Map doesn't help us, it can't be used inside 'limit_except'. We also tried
to use 'if' to pass a request to named location using 'try_files' but it
also is not allowed inside 'if'.

Can't be used:
----
limit_except $method {
  deny all;
}

if ($request_uri ~ \?doc) {
  try_files @test-doc;
}
----

The question is, if there a way to implement this and how?

Thank you!

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,287101,287101#msg-287101

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Nginx location - Distinguish requests by arguments or queries

Francis Daly
On Tue, Feb 25, 2020 at 07:05:05AM -0500, stmx38 wrote:

Hi there,

> We have a case when we should permit different methods on the same location
> but different requests.

The nginx model does "location" matching without reference to the
query string.

But you may be able to build your own "good-enough" config, for your
use case.

> For location /test we should permit only POST, for /test?doc we should
> permit only GET methods.

As you have seen - limit_except does not do what you want.

But if you can enumerate the things that you want to allow (or: want
to block), then you can make your own variable from "method" and "uri"
that you can test. Only checking it within the one location{} should
reduce the impact on any other requests.

If you change the list of things that should be allowed, then you will
need ongoing maintenance of the hardcoded list.

Something like:

==
    map $request_method$request_uri $block_this {
        default 1;
        ~*GET/test\?doc 0;
        ~*POST/test\?. 1;
        ~*POST/test 0;
    }
==

along with

==
    location /test {
        error_page  403 =405 /custom-error;
        if ($block_this = 1) { return 403; }
        proxy_pass http://test/in;
    }
==

(Set the return code to what you want.)

may be adaptable to do what you want.

The case-insensitive is because I don't know if your clients do GET or get
or gEt; and I don't know how important it is that *no* "should-be-blocked"
requests get to your back-end. Adjust to taste based on testing.

Good luck with it,

        f
--
Francis Daly        [hidden email]
_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Nginx location - Distinguish requests by arguments or queries

Francis Daly
On Tue, Feb 25, 2020 at 05:06:15PM +0000, Francis Daly wrote:
> On Tue, Feb 25, 2020 at 07:05:05AM -0500, stmx38 wrote:

Hi there,

two corrections to my suggestion...

> > For location /test we should permit only POST, for /test?doc we should
> > permit only GET methods.

> Something like:
>
> ==
>     map $request_method$request_uri $block_this {
>         default 1;
>         ~*GET/test\?doc 0;
>         ~*POST/test\?. 1;
>         ~*POST/test 0;
>     }
> ==

> The case-insensitive is because I don't know if your clients do GET or get
> or gEt; and I don't know how important it is that *no* "should-be-blocked"
> requests get to your back-end. Adjust to taste based on testing.

"method" (GET, POST, etc) is case-sensitive -- so will always be
uppercase, so you don't need the case-insensitive matching.

And the patterns should be anchored to the start, to avoid some false
matches.

So - change each instance of "~*" to be "~^", and it should be righter.

Cheers,

        f
--
Francis Daly        [hidden email]
_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Nginx location - Distinguish requests by arguments or queries

Olaf van der Spek
Hello Francis,

It seems that your solution working as expected and I have started to test
it. Also, have some questions here:

1. "~*" to be "~^"
The first one looks like Nginx regexp we can use for locations, but the
second one not (^~):
https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms

2. It seems that the order of records in map is important. We pass required
queries with args, we block then all queries with args and allow without
args a then default value is applied. Maybe you can provide more details
here.

Thank you for help and for the ready solution!

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,287101,287136#msg-287136

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Nginx location - Distinguish requests by arguments or queries

Francis Daly
On Thu, Feb 27, 2020 at 02:20:45PM -0500, stmx38 wrote:

Hi there,

> 1. "~*" to be "~^"
> The first one looks like Nginx regexp we can use for locations, but the
> second one not (^~):

"map" is documented at http://nginx.org/r/map

"~" means "this arg is a regex, not a string". "~*" means "and that regex
is case-insensitive". The rest of the argument is the regex; in that,
"^" means "match the start of the string".

So "~^GET/test" in this case will match all GET requests that start with
"/test". "~GET/test" would also match any requests that include the
8-character sting GET/test, which is probably not what you want.

> 2. It seems that the order of records in map is important. We pass required
> queries with args, we block then all queries with args and allow without
> args a then default value is applied. Maybe you can provide more details
> here.

Yes, the order is important. Per the docs: the first matching regex is
the regex that counts.

So - if you have some things which are subsets of some other things,
you must put the more specific ones earlier in the config list.

Good luck with it,

        f
--
Francis Daly        [hidden email]
_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx