8

Example case. I have a web-server with one IPV4 address. I am hosting ~50 web sites and just two of them have ssl certificates. I have configured vhosts for 2 ssl websites and everything is OK, except one big problem - if i visit other 48 sites using https:// prefix (they don't have ssl certificates), i am seeing my latest website with a SSL certificate.

It is possible to prevent https prefix on non SSL websites? Or there's just one solution - to use dedicated ip addresses. If i have dedicated ip address i am using the following config:

server {
        listen 111.222.333.444:443 ssl;
}

This way, if i force https on non SSL website - nothing happens.

Alexander Kim
  • 597
  • 3
  • 8
  • 21
  • Possibly linked to this question: https://serverfault.com/questions/578648/properly-setting-up-a-default-nginx-server-for-https which has more upvoted answers. – kevin Dec 19 '18 at 15:59

2 Answers2

7

Nginx uses a "default server" that it picks to serve pages when there isn't another closest match. If you haven't specified the default server to use, I believe it uses the first that it finds (which in your case is one of your HTTPS sites). So you should be able to create a default server vhost with your IPv4 address for TLS (you are using only TLS and not SSL anymore right?) and have it listen on the IP that will catch the random HTTPS requests. You might also need to add the server_name item in there too for your 48 non-secure sites, but it may work without that.

#Your catch-all HTTPS server:
server {
  listen 104.218.234.204:443 default_server;
  server_name ruel.in ruel.pro;
  root /var/www/ruel.pro;
  index index.html;
  ssl on;
  ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
  ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
  ssl_dhparam /etc/ssl/private/dhparams_4096.pem;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
}

#Regular HTTP server
server {
  listen ruel.in:80;
  server_name ruel.in;
  root /var/www/ruel.in;
  index index.php index.html index.htm;
  ...
}

#Regular HTTP server
server {
  listen ruel.pro:80;
  server_name ruel.pro;
  root /var/www/ruel.pro;
  index index.php index.html index.htm;
  ...
}

#Your HTTPS server
server {
  listen dallas.ruhnet.net:443 ssl;
  server_name dallas.ruhnet.net;
  ...
}

Another alternative is to go ahead and set them all up with HTTPS! With the Let's Encrypt project (https://www.letsencrypt.org) you can get trusted certificates for all those other sites for free. And it's quite easy.

ruhnet
  • 256
  • 1
  • 3
  • Thank you, but none of your methods are suitable for me. 1) With your config, if i want to access website with SSL certificate - it won't allow me to do that. Server_name is not working when it comes to SSL 2) I don't feel to make 48 certificates for the websites, that even doesn't need it at all. 3) Seems like the only working method is to buy additional ipv4 address. – Alexander Kim Mar 30 '16 at 05:58
  • You shouldn't need to buy more IPv4---it's just a matter of config. The server_name directive will work with SSL with all modern browsers. I tested a similar situation as yours on my server and have adjusted my answer. Use a self signed (or not) certificate on your catch-all server block and set it up to correctly serve over HTTPS. Then just create an index.html that says "Not found" or similar. See above. (You can try it too to verify.) – ruhnet Mar 30 '16 at 18:36
  • Did you try my updated solution? It should do exactly what you want. – ruhnet Apr 05 '16 at 18:20
1

I'm familiar with the issue.

We basically want to avoid at all cost that the first server definition in our config file is served as a catch-all-server for SSL connections. We all know that it does that (opposed to http and using default_server config which works nicely).

This cannot be achieved declaratively in Nginx for SSL (yet) so we have to code it with an IF...

The variable $host is the host name from the request line or the http header. The variable $server_name is the name of the server block we are in right now.

So if these two are not equal then you served this SSL server block for another host so that should be blocked.

We can implement this in each server block (with a generic include of course).

The code does not contain specific references to your server IP addresses or server names so it can easily be reused for other server configs without modification.

Example:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ###
    ### Section: SSL

    #
    ## Check if this certificate is really served for this server_name
    ##   http://serverfault.com/questions/578648/properly-setting-up-a-default-nginx-server-for-https
    if ($host != $server_name) {
        #return 404 "this is an invalid request";
        return       444;
    }

    # ...
Rolf
  • 301
  • 2
  • 5