Blocking unknown hostnames for SSL/TLS connections

Patrick O'Brien patrickobrien at tetrisblocks.net
Fri Dec 4 01:34:50 UTC 2015


On Thu, Dec 3, 2015 at 1:44 PM, Valentin V. Bartenev <vbart at nginx.com> wrote:
> On Thursday 03 December 2015 11:41:51 Patrick O'Brien wrote:
>> Hello,
>>
>> We're currently using nginx for SSL/TLS termination, which then
>> proxies the request to a pair of internal pair of load balancers.
>> Since the TLS handshake is performed before nginx is able to figure
>> out what hostname is being requested, except in cases where SNI is
>> used, it will accept any request for any hostname and pass it along
>> to our internal load balancers. This puts us in a situation where
>> internal resources are allowed to be exposed externally, although in
>> a roundabout way.
>>
>> For example, our internal load balancers have a pool called "news",
>> which is accessible via news, or news.dc1.example.com and is intended
>> to be internally accessible only. If you were to add our external IP
>> address mapped to news.dc1.example.com and told curl to ignore the
>> invalid cert, nginx will proxy this request along to our internal
>> load balancers and the internal service will happily respond. Here's
>> a curl example of this hitting the internal healthcheck endpoint:
>>
>> ```
>> curl -k https://news.dc1.example.com
>> alive
>> ```
>>
>> Ideally this would be blocked at our ingress point, which is nginx.
>>
>> The only way around this that I've found so far is to inspect the
>> $host variable in the server definition for the 443 blocks. The
>> example below shows the check for the server block which is intended
>> to respond to www.example.com and stg1.example.com only:
>>
>> ```
>>   # if the request coming in doesn't match any of the hosts we know
>>   # about, throw a 301 and rewrite to the default server.
>>    if ($host !~ (^www.example.com$|^stg1.example.com$)) {
>>      return 301 https://stg1.example.com;
>>    }
>> ```
>>
>> In our production environment we have a wildcard cert that covers as
>> many as 6 externally available resources, so I am concerned with the
>> performance hit of doing a check on every host.
>>
>> Is there a preferred method of dealing with an issue like this? I've
>> read through the config pitfalls page on the readthedocs.org[0] page,
>> and the If Is Evil page[1], so I am pretty positive the solution
>> above is very inefficient. The pitfalls page even talks about the
>> preferred alternative to an if statement for hostname matching[2],
>> but this does not appear to cover TLS connections. Is there any other
>> documentation that talks about this or could be useful?
>>
> [..]

Hi Valentin,

>
> http://nginx.org/en/docs/http/server_names.html
> http://nginx.org/en/docs/http/request_processing.html
>
> It's as simple as:
>
>   ssl_certificate      example.com.crt;
>   ssl_certificate_key  example.com.key
>
>   server {
>       listen 443 ssl default_server;
>       return 301 https://stg1.example.com;
>   }
>
>   server {
>       listen 443 ssl;
>       server_name stg1.example.com www.example.com ...;
>
>       location / {
>           proxy_pass ...;
>       }
>   }
>

It looks like this works unless if you have multiple ssl server
definitions which require different certs. Here is what we ended up
with (more or less):

# rewrite everything on 80 to https
server {
  listen 80;
  server_name _;
  return 301 https://$host$request_uri;
  proxy_set_header Host $host;
}

# server definition for www
server {
  listen 443 ssl;
  server_name www.example.com example.com;
  access_log /var/log/nginx/ssl.access.log;
  error_log /var/log/nginx/ssl.error.log;

  ssl_certificate /etc/nginx/ssl/www.combined;
  ssl_certificate_key /etc/nginx/ssl/www.key;
...
}

# server definition for wildcard
server {
  listen 443 ssl;
  server_name foo.example.com bar.example.com;
  access_log /var/log/nginx/ssl.access.log;
  error_log /var/log/nginx/ssl.error.log;

  ssl_certificate /etc/nginx/ssl/wildcard.combined;
  ssl_certificate_key /etc/nginx/ssl/wildcard.key;
...
}

# catch all to force unknown hostnames to www
server {
  listen 443 ssl default_server;
  server_name _;
  ssl_certificate /etc/nginx/ssl/www.combined;
  ssl_certificate_key /etc/nginx/ssl/www.key;
  return 301 https://www.example.com;
}

I did some spot checking via curl and chrome and everything appears to
be working how I expect it to.

-pat

> wbr, Valentin V. Bartenev
>
> _______________________________________________
> nginx mailing list
> nginx at nginx.org
> http://mailman.nginx.org/mailman/listinfo/nginx



More information about the nginx mailing list