How to setup reverse NGINX and LetsEncrypt certificates

Hi,
I am struggling for some time to make following ‘architecture’ to work: There is a publicly available server on which I run NGINX. It actually only redirect HTTP(S) requests to other NGINX instances running on VMs (so I don’t have to use different ports, set port forwarding, etc).HTTP works, but I wanted to add certificates and improve security.

Seems that I am missing something and not sure what. Main puzzle for me is: shall I create LE SSL certificates on server, on VM where ‘target’ NGINX is running or on both? Currently I use both: on main server there are ‘root’ domain certificates and on VM are '‘subudomain’ certs.

Any help/advice is more than welcome.

Here is the ‘main’ NGINX config file (/etc/nginx/sites-enabled/default) located on server:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name main.org www.main.org;
    return 301 https://$server_name$request_uri;
}

server {
        # SSL configuration
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2 default_server;

        ssl on;
        ssl_certificate /etc/letsencrypt/live/www.main.org/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/www.main.org/privkey.pem;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
        ssl_ecdh_curve secp384r1;
        ssl_session_cache shared:SSL:10m;
        ssl_session_tickets off;
        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 8.8.8.8 8.8.4.4 valid=300s;
        resolver_timeout 5s;

        add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;

        ssl_dhparam /etc/ssl/certs/dhparam.pem;

        # Session timeout value must be properly set in /etc/wok/wok.conf as well
        ssl_session_timeout 10m;

        root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }

        location ~ /.well-known {
                allow all;
        }
}

Following is the content of subsite NGINX config (/etc/nginx/sites-enabled/subsite) - also on main server.
192.168.0.24:80 is the HTTP endpoint of subdomain NGINX. This address is not visible from outer world.

Notice that I here I am also using www.main.org certificate - which is probably wrong, but could not create LE certificate for this site; got 404 Not Found and command was: sudo letsencrypt certonly --renew-by-defaul --agree-tos --staging --webroot -m nikoladsp@gmail.com -w /var/www/html -d www.subsite.org

server {
    listen 80;
    server_name subsite.org www.subsite.org;

    access_log  /var/log/nginx/subsite.access.log;
    error_log  /var/log/nginx/subsite.log;

    include /etc/nginx/snippets/letsencrypt-acme-challenge.conf;

    location / {
        return 301 https://$server_name$request_uri;
    }
}


server {
    listen 443;
    server_name subsite.org www.subsite.org;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/www.main.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.main.org/privkey.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_ecdh_curve secp384r1;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;


    add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;

    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    # Session timeout value must be properly set in /etc/wok/wok.conf as well
    ssl_session_timeout 10m;


    access_log  /var/log/nginx/subsite.access.log;
    error_log  /var/log/nginx/subsite.log;

    location / {
        proxy_pass  http://192.168.0.24:80;

        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;
        proxy_buffering off;
        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_set_header        X-Forwarded-Proto $scheme;
    }
}

Content of /etc/nginx/snippets/letsencrypt-acme-challenge.conf is as follows:

# Rule for legitimate ACME Challenge requests (like /.well-known/acme-challenge/xxxxxxxxx)
# We use ^~ here, so that we don't check other regexes (for speed-up). We actually MUST cancel
# other regex checks, because in our other config files have regex rule that denies access to files with dotted names.
location ^~ /.well-known/acme-challenge/ {

    # Set correct content type. According to this:
    # https://community.letsencrypt.org/t/using-the-webroot-domain-verification-method/1445/29
    # Current specification requires "text/plain" or no content header at all.
    # It seems that "text/plain" is a safe option.
    default_type "text/plain";

    # This directory must be the same as in /etc/letsencrypt/cli.ini
    # as "webroot-path" parameter. Also don't forget to set "authenticator" parameter
    # there to "webroot".
    # Do NOT use alias, use root! Target directory is located here:
    # /var/www/common/letsencrypt/.well-known/acme-challenge/
    root         /usr/share/nginx/html;
}

# Hide /acme-challenge subdirectory and return 404 on all requests.
# It is somewhat more secure than letting Nginx return 403.
# Ending slash is important!
location = /.well-known/acme-challenge/ {
    return 404;
}

Finally, on subdomain NGINX running in VM; config file (/etc/nginx/sites-enabled/default) looks like:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name subsite.org www.subsite.org;

    location ~ /.well-known {
        allow all;
    }

    location / {
        return 301 https://$server_name$request_uri;
    }

}


server {
        # SSL configuration
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2 default_server;

        ssl on;
        ssl_certificate /etc/letsencrypt/live/subsite.org/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/subsite.org/privkey.pem;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
        ssl_ecdh_curve secp384r1;
        ssl_session_cache shared:SSL:10m;
        ssl_session_tickets off;
        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 8.8.8.8 8.8.4.4 valid=300s;
        resolver_timeout 5s;
        
        add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;

        ssl_dhparam /etc/ssl/certs/dhparam.pem;

        # Session timeout value must be properly set in /etc/wok/wok.conf as well
        ssl_session_timeout 10m;

        root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }
}

try adding this in the proxy section:
ProxyPass /.well-known/acme-challenge !

And this in the main :443 section:
Alias /.well-known/acme-challenge /path-to-challenge

Is it possible that these are Apache-specific syntax that doesn’t work under nginx? I think the ! is and I notice that nginx configuration directives are usually all lowercase.

I just figure out that if I add www.subsite.org to main site config listen addresses; I am able to generate certificate but is that OK? - since I will remove it after certs are created. But even with this change applied, I still cant reach subdomain over HTTPS.

Is this what am I trying possible?

Do I need LE certificates on each SSL section (both configuration on main site and target VM NGINX)?

I was lucky enough to solve this issue.

First, I renewed certificates:

sudo service nginx stop
sudo letsencrypt certonly -a standalone -d main.org -d www.main.org -d www.subsite.org
sudo service nginx start

Then changed configuration for subsite on main server so that server_name line is as follows

server_name www.subsite.org ;

And HTTP server block (commented out include /etc/nginx/snippets/letsencrypt-acme-challenge.conf; line):

server {
    listen 80;
    server_name www.subsite.org;

    access_log  /var/log/nginx/subsite.access.log;
    error_log  /var/log/nginx/subsite.log;

#    include /etc/nginx/snippets/letsencrypt-acme-challenge.conf;

    location / {
        return 301 https://$server_name$request_uri;
    }
}

Also, in ‘subdomain’ NGINX configuration, I removed HTTPS block completely

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.