1

I have a nginx reverse proxy that acts as a one to many (single public IP) proxy for three other web servers.

I have all the blocks set up to redirect to each server depending on what URL is provided by the client.

What happens if the client simply puts the reverse proxy's IP address in their browser instead of an URL? How does nginx determine where to send the traffic to?

I just tried it and it seems to send the traffic to the last server that it forwarded traffic to?

How do I drop/deny traffic that does not match one of the three server blocks in my configuration (i.e. traffic that uses an IP instead of URL)?

Update: For my configuration, here is the only conf file in sites-enabled:

######## Server1 ########
server {
        if ($host = server1.domain.com) {
                return 301 https://$host$request_uri;
        }

        listen 80;
        server_name server1.domain.com;
        return 404;
}
server {
        listen 443 ssl; # managed by Certbot
                ssl_certificate /etc/letsencrypt/live/server1.domain.com/fullchain.pem;       # managed by Certbot
                ssl_certificate_key /etc/letsencrypt/live/server1.domain.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_name server1.domain.com;

        location / {
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_redirect off;
                proxy_pass_request_headers on;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_pass https://192.168.20.2:443;
        }

        location ^~ /wp-login.php {
                satisfy any;
                allow 172.20.5.2;
                deny all;

                proxy_pass https://192.168.20.2:443;
        }
}
######## Server2 ########
server {
        if ($host = server2.domain.com) {
                return 301 https://$host$request_uri;
        }

        listen 80;
        server_name server2.domain.com;
        return 404;
}

server {
        listen 443 ssl http2;
                ssl_certificate /etc/letsencrypt/live/server2.domain.com/fullchain.pem; # managed by Certbot
                ssl_certificate_key /etc/letsencrypt/live/server2.domain.com/privkey.pem; # managed by Certbot
                include /etc/letsencrypt/options-ssl-nginx.conf;
                ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

        server_name server2.domain.com;

        location / {
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_redirect off;
                proxy_pass_request_headers on;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_pass https://192.168.20.3:443;
        }

}

######## Server3 ########
server {
        if ($host = server3.domain.com) {
                return 301 https://$host$request_uri;
        }

        listen 80;
        server_name server3.domain.com;
        return 404;
}
server {
        listen 443 ssl http2;
                ssl_certificate /etc/letsencrypt/live/server3.domain.com/fullchain.pem; # managed by Certbot
                ssl_certificate_key /etc/letsencrypt/live/server3.domain.com/privkey.pem; # managed by Certbot
                include /etc/letsencrypt/options-ssl-nginx.conf; 
                ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; 

        server_name server3.domain.com;

        location / {
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_redirect off;
                proxy_pass_request_headers on;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_pass https://192.168.20.4:443;
        }
}

Nginx reverse proxy IP is 192.168.20.6

So what I am seeing is if I put in just the IP into my browser, NGINX appears to go to the first server block in my conf file, which tracks with this link: https://nginx.org/en/docs/http/request_processing.html

And it does try and load server1 in my case, but since the serving of website content is based upon the URL, it sorta breaks some features of my three web servers.

Looking at that link above, I see that I can employ a block like this at the beginning to block IP only requests?

server {
    listen      80;
    listen      443;
    server_name "";
    return      444;
}
ehammer
  • 75
  • 1
  • 10
  • Question is if you use *server_name* or not. If you use it then IP won't match and default server block would match. More info please. – Jiri B Mar 28 '21 at 08:06
  • An interesting way to do proxy, I haven't tested it. https://serverfault.com/a/881881/451558 – Jiri B Mar 28 '21 at 08:11
  • 1
    The question cannot be answered without seeing your nginx configuration. Please add output of `nginx -T` to your question. – Tero Kilkanen Mar 28 '21 at 08:12
  • iv also raised a flag that beeded information are missing, but in fact if someone usetze server ip it will serve the default pages if configured – djdomi Mar 28 '21 at 09:10
  • See [how Nginx processes a request](http://nginx.org/en/docs/http/request_processing.html) and note that there is **always** a default server. – Richard Smith Mar 28 '21 at 09:33
  • Added more information to the post. I think that block above should work to stop these requests? – ehammer Mar 28 '21 at 12:22

1 Answers1

0

"default_server"

There are times you do not want visitors accessing services via your IP Address and the most effective method, at lease for me, is disabling direct IP access though using Nginx's default_server.

The contents of file /etc/nginx/sites-enabled/default_server.conf are;

listen 192.168.0.100:80 default_server;
listen 192.168.0.100:443 ssl default_server;
server_name ""; ## see update edit below
access_log off;

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

return 444; 

## If you'd prefer to redirect them to your actual site un-comment below. 
# return 301 https://example.com$request_uri;
 
}

Reload Nginx and visitors attempting access via IP directly will be refused. ​

Visitors should not be able to go directly to your back end server directly. Nginx should be the only public facing http server.

Hackers will often access your IP directly - they are the bad-guys!

Update Edit

I've just noticed the last line of the conf example above was incorrect. To redirect to a URL you need to include "return 301 https://example.com$request_uri;"

In my answer I failed to include "return 301". Typing error on my part.

In addition, I was wrong in setting "server_name _;". According to Nginx docs the correct method is 'server_name "";'

http://nginx.org/en/docs/http/request_processing.html

  • Can that block be implemented in the same "sites-enabled/conf" file as the rest of my reverse proxy server blocks? – ehammer Mar 31 '21 at 14:51
  • How would the lets encrypt ssl certificates work with a non url request? does it matter or should I generate separate certs for domain.com instead of site.domain.com? – ehammer Mar 31 '21 at 20:32
  • @ehammer, the ssl works but is invalid. I only include the ssl in my config because it listens on 443 too. I could be wrong but doesn't nginx kick a stink if ssl is listening with no ssl options. I could be wrong. I guess it will work in a single config with multiple domains, its the addition of "default_server" which sets it as default. I haven't tried as all my domains are in individual .conf files. – Admiral Noisy Bottom Apr 01 '21 at 04:43
  • I created a new conf file (default.conf) in addition the conf file with all my server blocks. I tried it with ssl cert of one of my servers, and it bricked connections to any domain. I will try again later today with no ssl options. Should the listen IP be the private IP? – ehammer Apr 01 '21 at 11:38
  • Got rid of the SSL certs, same error as before: ```PR_END_OF_FILE_ERROR``` When browsing to any of the URLs for my backend servers – ehammer Apr 01 '21 at 19:15
  • @ehammer, hmm, didn't brick mine. The IP address must be the actual IP of the machine it is on, not necessarily your public IP. You could remove the IP and just have "listen 80;" and Nginx will listen on all IP's associated with that device. I use my local network IP because it is part of my network and port 80 is forwarded to it. I don't need Nginx on 127.0.0.1, so I specify it. – Admiral Noisy Bottom Apr 02 '21 at 01:16
  • So some more digging, the block does work for IP only urls from the internet, but it also breaks real requests with URLs with the error: ```Secure Connection Failed; authenticity of the received data could not be verified``` from the browser. Not sure why its affecting my other conf file, since it should be ignoring URL requests? – ehammer Apr 02 '21 at 19:52
  • Requested a lets encrypt for domain.com and added that to the block listening for _ url. Seems to work, only problem is clients don't get the HTTP 444 response, instead their browser would have the cert error since their url wouldnt match the cert sni. It might be kinda silly wanting a proper HTTPS connection only to just close it once the client sends a http request for an IP. I suppose its impossible to close an HTTPS connection based on requested URL before the TLS handshake? – ehammer Apr 02 '21 at 20:14
  • @ehammer I believe https stuff occurs first. I'll do some tests and get back to you. – Admiral Noisy Bottom Apr 02 '21 at 22:06
  • @ehammer After reading http://nginx.org/en/docs/http/request_processing.html I found setting server_name _; is incorrect and the correct method is server_name ""; – Admiral Noisy Bottom Apr 02 '21 at 23:01
  • Ah, yeah now it works. Proper traffic gets through. I don't quite know if http 444 is actually happening since any client I try to test from the internet breaks the connection (over HTTPS) because the certificate SNI obviously doesn't match the url of ```https://x.x.x.x/``` Occasionally I see in packet captures the client providing the SNI its expecting in its client hello, but I didn't see it in an IP only URL. Not sure if its possible to filter on the SNI the clients sends if its not that common – ehammer Apr 04 '21 at 00:33
  • @ehammer, I'm glad you got it "mostly" working. A shame we can't get an SSL certificate for an IP address rather than a URL. When you say https breaks the connection, does it still brick your nginx box or just pass the connection to the URL referred to in the certificate? – Admiral Noisy Bottom Apr 05 '21 at 02:54
  • The browser im using when testing it breaks the connection. When it sees the certificate SNI of a domain name and not the IP, it stops the connection. But I suppose if someone malicious is browsing to the IP they should get the correct 444 response (or lack of) – ehammer Apr 05 '21 at 17:57