Adventures of the Retired Guy

Adventures of the Retired Guy

Stuff and nonsense from a retired guy

08 Apr 2019

Wildcard cert via acme.sh

New standard for single-tenant, multi-vdir sites: configure nginx as outer server with paths for each service; run the services as separate vdirs with whatever server they ship with; let nginx terminate the SSL sessions for all of the vdirs and run the service in HTTP. This is easier if you use a single wildcard cert for the domain.

Let's Encrypt has made it simple and very cheap to get a free, generally useful SSL cert. But when you are running multiple vdirs as separate subdomains (which is everybody) you can get a single wildcard domain cert (such as *.bobhy.com) to support SSL at all of them in one fell swoop. However, Let's Encrypt is, let us say, Windows-agnostic; the best understood client tools are for Linux. And wildcard certs require a DNS host that supports automation if you really want to automate the whole refresh cycle. We use domains.google.com, for which I cannot find an API or an Acme tool, so I must abandon the dream of full automation and do it by hand. That's OK because it's precisely in the area automation that the various tools get stuck in the weeds. And the benefit is limited if you can simplify the configuration so only one certificate needs to be renewed and reinstalled.

Configure Nginx to share the wildcard cert for all subdomains

Once you set this up, Nginx will look for the cert in the location acme.shinstalls it.

In /etc/nginx/sites-available/default:

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        # SSL configuration
        #
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;

        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        ssl_certificate /home/bob/.acme.sh/*.bobhy.com/*.bobhy.com.cer;
        ssl_certificate_key /home/bob/.acme.sh/*.bobhy.com/*.bobhy.com.key;
        include /etc/nginx/snippets/ssl-params.conf;

        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;
        }

}

Then, in each site vdir entry (/etc/nginx/sites-available/<site>.conf):

server {
    listen 80;
    listen [::]:80;

    server_name blog.bobhy.com;
    root /var/www/ghost/system/nginx-root;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://127.0.0.1:2368;

    }

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

    client_max_body_size 50m;
}

Note there is no SSL gobbledegook in these files, it's all contained in default.

Now, you're set to get a new or renew an existing wildcard SSL cert.

Renew the wildcard cert

$ acme.sh --issue --dns -d *.bobhy.com --yes-I-know-dns-manual-mode-enough-go-ahead-please

[Sun Apr  7 19:23:06 EDT 2019] Registering account
[Sun Apr  7 19:23:06 EDT 2019] Registered
[Sun Apr  7 19:23:07 EDT 2019] ACCOUNT_THUMBPRINT='. . .'
[Sun Apr  7 19:23:07 EDT 2019] Creating domain key
[Sun Apr  7 19:23:08 EDT 2019] The domain key is here: /home/bob/.acme.sh/*.bobhy.com/*.bobhy.com.key
[Sun Apr  7 19:23:08 EDT 2019] Single domain='*.bobhy.com'
[Sun Apr  7 19:23:08 EDT 2019] Getting domain auth token for each domain
[Sun Apr  7 19:23:09 EDT 2019] Getting webroot for domain='*.bobhy.com'
[Sun Apr  7 19:23:09 EDT 2019] Add the following TXT record:
[Sun Apr  7 19:23:09 EDT 2019] Domain: '_acme-challenge.bobhy.com'
[Sun Apr  7 19:23:09 EDT 2019] TXT value: '. . .'
[Sun Apr  7 19:23:09 EDT 2019] Please be aware that you prepend _acme-challenge. before your domain
[Sun Apr  7 19:23:09 EDT 2019] so the resulting subdomain will be: _acme-challenge.bobhy.com
[Sun Apr  7 19:23:09 EDT 2019] Please add the TXT records to the domains, and re-run with --renew.
[Sun Apr  7 19:23:09 EDT 2019] Please add '--debug' or '--log' to check more details.
[Sun Apr  7 19:23:09 EDT 2019] See: https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh

---------- bob@server3:~ ----------

## Update DNS

1. Visit https://domains.google.com, 
2. find the DNS records for the domain, 
3. remove older `_acme.challenge` record and 
4. replace it with the new one.

## Install new cert

$ acme.sh --renew -d '.bobhy.com' --yes-I-know-dns-manual-mode-enough-go-ahead-please [Sun Apr 7 19:29:10 EDT 2019] Renew: '.bobhy.com' [Sun Apr 7 19:29:11 EDT 2019] Single domain='.bobhy.com' [Sun Apr 7 19:29:11 EDT 2019] Getting domain auth token for each domain [Sun Apr 7 19:29:11 EDT 2019] Verifying: .bobhy.com [Sun Apr 7 19:29:14 EDT 2019] Success [Sun Apr 7 19:29:14 EDT 2019] Verify finished, start to sign. [Sun Apr 7 19:29:14 EDT 2019] Lets finalize the order, Le_OrderFinalize: https://acme-v02.api.letsencrypt.org/acme/finalize/54781655/389627092 [Sun Apr 7 19:29:16 EDT 2019] Download cert, Le_LinkCert: https://acme-v02.api.letsencrypt.org/acme/cert/039e96c0d9a32bebac5a6fab6eabbd087a67 [Sun Apr 7 19:29:17 EDT 2019] Cert success. -----BEGIN CERTIFICATE----- MIIFUDCCBD . . . . . . 23h3N02QVBa2481Y1ydNtK9FFpQ= -----END CERTIFICATE----- [Sun Apr 7 19:29:17 EDT 2019] Your cert is in /home/bob/.acme.sh/.bobhy.com/.bobhy.com.cer [Sun Apr 7 19:29:17 EDT 2019] Your cert key is in /home/bob/.acme.sh/.bobhy.com/.bobhy.com.key [Sun Apr 7 19:29:17 EDT 2019] The intermediate CA cert is in /home/bob/.acme.sh/.bobhy.com/ca.cer [Sun Apr 7 19:29:17 EDT 2019] And the full chain certs is there: /home/bob/.acme.sh/.bobhy.com/fullchain.cer [Sun Apr 7 19:29:17 EDT 2019] It seems that you are using dns manual mode. please take care: The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead. [Sun Apr 7 19:29:17 EDT 2019] Call hook error.

This installs the cert in just the location that Nginx config is looking for it.  I didn't change security on the certificate archive and Nginx was apparently able to access it, must be running as root.  Your mileage may vary.

There are rumors that Nginx does need to be restarted (not just reloaded) to fully use the new cert.