Nginx config to prevent invalid sock file access attacks

Once your site goes live there are some elements out there who keep trying to access your filesystem using an old UNIX hack of trying to hit your site with a path to sock file. Using frameworks like Django can prevent this already if you have your DISALLOWED_HOST settings set properly to only allow valid hostnames in it. But the first line of defense for this must be the webserver i.e. Nginx. The assumption being here that you are using Nginx as your reverse proxy server.

Depending on your configuration your conf files for Nginx will be in either /etc/nginx/conf.d/ or as a link in /etc/nginx/sites-enabled/. Modify your instructions accordingly i.e. updating files directly in /etc/nginx/conf.d/ or if its configured to be so, update /etc/nginx/sites-available/ and link to /etc/nginx/sites-enabled/ directory. Me personally recommending to have configured the latter flow for production setups.

Your typical Nginx config for a production reverse-proxy (with letsencrypt) will look something like this:

server {
server_name mysite.com;
client_max_body_size 100M;
access_log /var/log/mysite/nginx.access.log;
error_log /var/log/mysite/nginx.error.log;
location / {
include proxy_params;
proxy_pass http://unix:/tmp/mysite.sock;
}
...
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}server {
if ($host = mysite.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80; server_name mysite.com;
return 404; # managed by Certbot
}

After configuring this you might be taking a breath of relief that your configuration is done only to find the following error keeps popping up in your application server log history:

[15/Jan/2021:21:49:49 +0530] “GET /account/login/ HTTP/1.0” 200 2348 “-” “Mozilla/5.0 (iPad; CPU OS 11_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1” in 109739µs
ERROR 2021–01–16 06:15:07,816 [none] 127579 — — 88 [django.security.DisallowedHost] response_for_exception : Invalid HTTP_HOST header: ‘/tmp/mysite.sock:’. The domain name provided is not valid according to RFC 1034/1035.
WARNING 2021–01–16 06:15:07,945 [none] 127579 — — 224 [django.request] log_response : Bad Request: /
[16/Jan/2021:06:15:07 +0530] “GET / HTTP/1.0” 400 143 “-” “-” in 129680µs

In short someone tried accessing your site using your sites URL but with a server-name of /tmp/mysite.sock?

If you recheck your Nginx config you will be scratching your head thinking you have specifically told it in both the above server blocks to only entertain server_name mysite.com then how in the world is this request reaching your application server.

The magic happening here is because there is no default server defined in your Nginx config and hence if it fails to find any server block in all config files then it by default chooses to use your only configured block even when you have written it for mysite.com only. In short the server_name directive in the block acts only as a tag and not as a gate keeper.

The way to go about fixing this is to define default server blocks so that Nginx will choose them over your mysite.com blocks. Within those server blocks you can then reject the requests and close connection from Nginx itself before it even reaches your application server.

Usually I use the file default.conf for this which Nginx creates for you as a sample on a fresh install. It really doesn’t matter in which file you write, this is just a convention I like to follow. Backup the default conf file just in case and then replace its contents with following:

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
server {
listen 443 ssl default_server;
listen [::]:443 default_server;
server_name _;
ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
return 444;
}

Note that the server_name directive is just a placeholder here and it’s value can be anything, but just for a convention I am using _ as its value which will definitely not clash with any of my other server blocks.

Just as a precautionary measure I also edit my mysite server block just for peace of my mind:

server {    server_name mysite.com;    if ($host != "mysite.com") {
return 444;
}
... # same other configs as before
}

Save all these configs and reload nginx server as follows:

sudo nginx -t
sudo nginx -s reload

And now finally Nginx configs will behave according to your expectation with no further magic.

Always learning.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store