Home > Software design >  Nginx block all traffic with specific custom header except to some urls
Nginx block all traffic with specific custom header except to some urls

Time:11-09

I have a service that is hosted in an internal network that is receiving traffic in port 443 (via https) behind a custom loadbalancer both from the internet but also from the internal network.

Internal network requests are coming with an extra custom header, let's call it X-my-lb-header.

I want to block all external incoming traffic to all uris (return an http response code), except to some specific ones.

Eg, let's say that i want to allow traffic that is coming to two endpoints /endpoind1/ (preffix match) and /endpoint2 actual match.

What is the best way to achieve a behaviour like this?

If my understanding is correct I need something like (not correct syntax bellow)

   location = /endpoind2 {
        if ($http_x_my_lb_header not exists) {
            pass
        } else {
            return 404
        }
        ... the rest of the directives
    }


   location ~ / {
        if ($http_x_my_lb_header) {
            return 404;
        }
        ... the rest of the directives
    }

But since else is not supported in nginx, i cannot figure out to do it.

Any ideas?

CodePudding user response:

So you need some logic like

if (header exists) {
    if (request URI isn't whitelisted) {
        block the request
    }
}

or in another words

if ((header exists) AND (request URI isn't whitelisted)) {
    block the request
}

Well, nginx don't allow nested if blocks (nor logical conditions). While some people inventing a really weird but creative solutions like this one (emulating AND) or even this one (emulating OR), a huge part of such a problems can be solved using map blocks (an extremely powerfull nginx feature).

Here is an example:

# get the $block variable using 'X-my-lb-header' value
map $http_x_my_lb_header $block {
    # if 'X-my-lb-header doesn't exists, get the value from another map block
    ''    $endpoint;
    # default value (if the 'X-my-lb-header' exists) will be an empty string
    # (unless not explicitly defined using 'default' keyword)
}

# get the $endpoint variable using request URI
map $uri $endpoint {
    # endpoint1 prefix matching (using regex)
    ~^/endpoint1    ''; don't block
    # endpoint2 exact matching
    /endpoint2      ''; don't block
    default         1; # block everything other
}

Now you can use this check in your server block (don't put it to some location, use at the server context):

if ($block) { return 404; }
  • Related