1

Looking for some advice and peer input - I'm currently trying to configure NGINX in a way so that I can configure multiple websites at the same time without having to set up individual VHost configs or declaring multiple 'server {}' entries with duplicate config parameters in my NGINX configuration. I have partial success thus far but I'm worried that in doing this it might create security concerns or inadvertently have too broadly defined range. I'm also unsure how to properly/correctly use the "default" value in 'map' directive.

All in all, currently I have a typical NGINX configuration with 2 VHost configs for one domain name (and a 'www.*' subdomain for it) in 'nginx_root/sites-available' - one for HTTP (port 80) and one for HTTPS (port 443) which are soft-symlinked to 'nginx_root/sites-enabled'. But as previously mentioned - I'd like to add additional domain names but keep the amount and size of these configs as they currently are by using the NGINX 'map' features to set-up new domain names dynamically.

The way I've mapped it at the moment is by creating a config file: 'nginx_root/mapped_fqdn.conf' which I have then included early in my NGINX config (so that it gets mapped before it's called/referenced further in the config) and then using the map as 'server_name' parameter. If it's possible to expand this to other config parameters and safe to do so, I'd like to add the new domain names in this same way.

mapped_fqdn.conf:

### Domain name mapping
    map $host $fqdn_map {
        hostnames;
        default 0;

        domain-1.com 0;
        www.domain-1.com 0;
    }

00-http.conf:

server {
    listen 80;
    listen [::]:80;
    server_name $fqdn_map;
    root /var/www/public_html;
    index index.html;
    access_log /var/log/nginx/nginx_access.log nginx_access;;

    ### HTTP to HTTPS Redirect
    return 301 https://$server_name$request_uri;
}

For the sake of not repeating I'll skip adding the HTTPS config, which looks about the same, except it has some few minor additional parameters, along with the SSL-specific config parameters added and doesn't have the 301 redirect, which is present in the HTTP config.

The way I see it, I can add the additional domain names in the same way...

example of updated mapped_fqdn.conf: (the way I see is that perhaps adding specific subdomains would be safer than adding them like wildcards, i.e.: "*.domain.com")

### Domain name mapping
    map $host $fqdn_map {
        hostnames;
        default 0;

        domain-1.com 10;
        www.domain-1.com 11;

        domain-2.co.uk 20;
        www.domain-2.co.uk 21;
        subdomain1.domain-2.co.uk 22;
        subdomain2.domain-2.co.uk 23;

        domain-3.com 30;
        www.domain-3.com 31;
        subdomain1.domain-3.com 32;
    }

example of updated 00-http.conf:

server {
    listen 80;
    listen [::]:80;
    server_name $fqdn_map;
    root /var/www/$fqdn_map/public_html;
    index index.html;
    access_log /var/log/nginx/$fqdn_map-nginx_access.log nginx_access;;

    ### HTTP to HTTPS Redirect
    return 301 https://$server_name$request_uri;
}

However, before I proceed, I'm concerned about how would this work with directories, i.e.: Let's Encrypt certificates for HTTPS config - is it possible to specifically select which mapped items to use? For example: $fqdn_map{10|20|30} (this way selecting only the domain names without subdomain names).

    ssl_client_certificate /etc/ssl/certs/$fqdn_map{10|20|30}/cloudflare_origin.pem;
    ssl_certificate /etc/letsencrypt/live/$fqdn_map{10|20|30}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/$fqdn_map{10|20|30}/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/$fqdn_map{10|20|30}/chain.pem;

Or perhaps I'm going about this the wrong way and I should map FQDN and subdomains separately ... or maybe just create symlinks and junctions from mapped subdomain directories to fqdn directories (which, while resulting in less/cleaner configs, leads to more symlinking kinda defeating the purpose of "doing less but achieving more")?

Kārlis K.
  • 23
  • 1
  • 1
  • 8
  • This look really complicated to me. I would recommend using a configuration management system to generate the configuration files. I also doubt that this approach doesn't even work at all: `server_name` would receive value `0` when the request has `Host:` header `domain-1.com`. `server_name` must always have a FQDN or regular expression in it so that it would work correctly. – Tero Kilkanen Jun 23 '17 at 15:57
  • Thanks for the input - this is why I'm looking for peer input on this topic. I'm not entirely familiar with the 'map' directive and how to construct a valid formula. Some sort of config manager could certainly work from the viewpoint of managing configs, but I'm afraid that this will lead to more configuration files involved and potentially pose as a threat or a complication in the future (instead of just configuring the web server, now I have to worry about configuring the software that configures the server). – Kārlis K. Jun 23 '17 at 20:48
  • From the code samples I've seen, 'map' is usually used for redirections. Also, would it work if instead of numbers I assigned FQDN's? For example, one site with different subdomains would have the assigned value of a single domain? That would be a more welcomed result - so that a mapped domain name as a server_name, with multiple subdomains would point towards the same Certbot certificate directory (certbot creates unified certificate bundles for each domain with all the subdomains included in the same directory) and the same logs (condensed logs instead of creating different standalone ones). – Kārlis K. Jun 23 '17 at 21:10
  • You should have presented your exact problem directly in your question, that would have avoided extra work here. What you need is a `map` where you map host names to certificate directories, and then use the mapped variable in the certificate directory statement. – Tero Kilkanen Jun 23 '17 at 21:58
  • I like what you're suggesting - it sounds exactly like what I'm trying to achieve... if you could write it up as an answer below I'd accept and mark it as the answer to my situation. – Kārlis K. Jun 24 '17 at 19:48
  • I updated my answer. – Tero Kilkanen Jun 25 '17 at 11:17

1 Answers1

0

If you simply want to map hostnames to certificate directories, then you can use this approach:

map $http_host $certdir {
    hostnames;

    dom1.example.com dom1;
    dom2.example.com dom2;
    .example.com dom;
}

server {
    listen 443 ssl;

    server_name something.example.com;

    ssl_certificate /path/to/certificates/$certdir/dom.crt;
    ssl_certificate_key /path/to/certificates/$certdir/dom.key;
    ...
}

Here $certdir value will be dom, since something.example.com matches .example.com in the map definition.

--- First answer which answered a question which was not originally asked ---

Maybe you are looking for this:

server {
    listen 80 default_server;

    server_name _;

    root /var/www/$host/public_html;

    ....
}

We specify a default_server block here, which processes all requests that do not have a matching virtual host elsewhere.

Then we use the $host variable, which contains the domain name.

If you want to use more specific matching for only some domains, use regular expression:

server {
     listen 80;

     server_name ~ ^(domain-1.com|domain-2.com)$;

     root /var/www/$host/public_html;

     ....
}

You can refine the regular expression.

However, as I stated in my comment, this will place restrictions how you can customise each server block. You could use include /etc/nginx/conf.d/$host.conf to add domain specific configuration in another file.

Still, in the long run a configuration management system is the best option.

Tero Kilkanen
  • 34,499
  • 3
  • 38
  • 58
  • I was afraid it might not be possible, but the progress so far lead me to believe it might. I do like the second code snippet - it's simpler and I hadn't thought about doing it this way. However, reason why I was looking for a way to map it is in the interest to have the corresponding sites use the correct Certbot SSL certificates. For example - domain1.com, subdomain6.domain.com and subdomain1.domain1.com look for the certificates in '//live/domain1.com/*' instead of '//live/subdomain1.domain1.com/*' or '//live/subdomain6.domain1.com/*'. – Kārlis K. Jun 23 '17 at 20:55