NGINX: Restrict access by IP address

Crude way to stop an attack in case of an emergency.

Keep in mind that this is a static configuration, which is not very effective against adversarial attacks when an attacker constantly changes their tactics.

Denylisting (that's what NGINX calls it) is done through ngx_http_access_module, which provides static denylisting. For dynamic denylisting we would have to use NGINX Plus.

There are 2 control directives in play here: allow and deny. The configuration can be added either to the main file /etc/nginx/nginx.conf or to any of the virtual hosts /etc/nginx/sites-enabled/website.conf.

NGINX supports adding the directive in the following blocks

  • http - to protect all hosts that are served from a given NGINX instance
  • server - to assert control on the virtual host level
  • location - to assert control on the specific path
  • limit_except - exclude provided HTTP verbs
http {
    deny 8.8.8.8;
}

server {
    deny 8.8.8.8;
}

location /login {
    deny 8.8.8.8;
}

location / {
    limit_except GET {
        deny 8.8.8.8;
    }
}

Restricting access to multiple IPs

We can also use a CIDR notation to protect from botnets deployed in a single subnet. Adding multiple directives means that all of them are going to be applied.

location /login {
    deny 8.8.8.0/24;
    deny 2001:0db8::/32;
}

Additional blanket value all can be used to allow the access to a specific subnet while disabling the access to the rest.

location /login {
    allow 8.8.8.0/24;
    deny all;
}

Managing denylist

We can also manage it through a separate file on a filesystem.

# cat denylist.conf
deny 8.8.8.8
deny 32.32.32.32

For that we need to use include in the NGINX configuration file.

location /login {
    include denylist.conf;
}

Customizing error message

By default NGINX will return 403 Forbidden, but we can customize it.

location /login {
    deny 8.8.8.8;
    error_page 403 /deny_403.html;
    location = /deny_403.html {
        root /usr/share/nginx/html;
        allow all;  # this is needed to render the page itself
    }
}

Applying configuration

/usr/local/nginx/sbin/nginx -t

to test the config.

/usr/local/nginx/sbin/nginx -s reload
# or
service nginx reload

to reload.