Mind Dump, Tech And Life Blog
written by Ivan Alenko
published under license Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)copy! share!
posted at 08. Feb '18
last updated at 11. May '21

Let’s Encrypt Subdomains HOWTO

OK. Here is my situation. I want to use HTTPS everywhere, for the main site and also all subdomains. Because it’s secure. And allegedly you get higher ranks in Google.

See also HOWTO for Let’s Encrypt wildcard certificate for subdomains.

TL;DR

I’m an expert, spare me the details.

Here you are:

  • Copy over and adapt configuration files for Bind and Nginx. Restart Bind right away, restart Nginx after Certbot.
  • Command to run: certbot --installer=nginx --authenticator webroot -w /home/myrtana_sk/current/public -d myrtana.sk,projects.myrtana.sk,kittydemo.myrtana.sk,www.myrtana.sk,ww.myrtana.sk,starz.myrtana.sk,light.myrtana.sk,legends.myrtana.sk,library.myrtana.sk
  • Adding more subdomains later: certbot --expand --installer=nginx --authenticator webroot -w /home/myrtana_sk/current/public -d myrtana.sk,projects.myrtana.sk,kittydemo.myrtana.sk,www.myrtana.sk,ww.myrtana.sk,starz.myrtana.sk,light.myrtana.sk,legends.myrtana.sk,library.myrtana.sk,new.myrtana.sk

Pick the correct web root and list all domains and subdomains that should go into the certificate. List existing + new domains when adding later.

And for others, here is more detailed guide…

Give Me More Details

The existing setup consists of Bind as DNS and Nginx as Web Server. Nothing else really matters.

Let’s get to work then. I use real config files crafted for my headquarters, myrtana.sk.

The strategy is following:

  1. DNS: set up wildcard A, AAAA and CAA records for root domain and subdomains. Everything (every subdomain) needs to hit our web server.
  2. Nginx: Set up HTTPS virtual host, all needs to be accepted and processed by the web server.
  3. Let’s Encrypt: use certbot to generate all certificates for main site and subdomains. Let’s Encrypt doesn’t support wildcard certificates, therefore subdomains needs to be known beforehand. Why not.

DNS

There are A (IPv4) and AAAA (IPv6) records for root domain (@) and all subdomains (*.). Also there are CAA records which say only letsencrypt.org can issue certificates for our domain.

; /etc/bind/db.myrtana
;
; BIND reverse data file for local loopback interface
;
$TTL  3600
@ IN  SOA ns1.myrtana.sk. root.myrtana.sk. (
  2017020701  ; Serial
       1h   ; Refresh
       10m  ; Retry
       4w   ; Expire
       1h ) ; Negative Cache TTL
;

@   IN  NS  ns1.myrtana.sk.
@   IN  NS  ns2.myrtana.sk.
@   IN  MX  10  starz.myrtana.sk.

@   IN  A 37.205.11.69
*.myrtana.sk. IN  A 37.205.11.69

@   IN  AAAA  2a03:3b40:100::1:80
*.myrtana.sk. IN  AAAA  2a03:3b40:100::1:80

@   IN  TXT "v=spf1 mx -all"

@   IN  CAA 0 issue "letsencrypt.org"
*.myrtana.sk. IN  CAA 0 issue "letsencrypt.org"

Nginx

I enabled HTTP/2, because Passenger supports it and TLS connection is reused, so the site response should be faster. It is not mandatory.

# /etc/nginx/local/ssl_settings
# last check 2018/02/09
# setup from mozilla tls generator + http headers from cipherli.st
ssl on;
# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# modern configuration. tweak to your needs.
ssl_protocols TLSv1.2;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256;
ssl_prefer_server_ciphers on;
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;
resolver 127.0.0.1 [::1] valid=300s;
# /etc/nginx/sites-enabled/myrtana.sk
server {
  listen 80 default_server;
  listen [::]:80 default_server;

  client_max_body_size 64M;
  server_name .myrtana.sk;

  access_log /var/log/nginx/myrtana.sk.access.log combined;
  error_log /var/log/nginx/myrtana.sk.error.log;

  # Redirect non-https traffic to https
  if ($scheme != "https") {
    return 301 https://$host$request_uri;
  } # managed by Certbot
}

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

  include local/ssl_settings;
  ssl_certificate /etc/letsencrypt/live/projects.myrtana.sk/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/projects.myrtana.sk/privkey.pem; # managed by Certbot

  ## verify chain of trust of OCSP response using Root CA and Intermediate certs
  ssl_trusted_certificate /etc/letsencrypt/live/projects.myrtana.sk/fullchain.pem;

  access_log /var/log/nginx/myrtana.sk.access.log combined;
  error_log /var/log/nginx/myrtana.sk.error.log;

  client_max_body_size 64M;
  server_name .myrtana.sk;

  access_log /var/log/nginx/myrtana.sk.access.log combined;
  error_log /var/log/nginx/myrtana.sk.sk.error.log;

  # ~2 seconds is often enough for most folks to parse HTML/CSS and
  # retrieve needed images/icons/frames, connections are cheap in
  # nginx so increasing this is generally safe...
  keepalive_timeout 7;

  # path for static files
  root /home/myrtana_sk/current/public;
  passenger_ruby /home/myrtana_sk/.rbenv/shims/ruby;
  passenger_enabled on;
  passenger_app_env production;
}

Just put a new configuration here, but don’t restart Nginx as paths to the certificate don’t exist yet and the restart will fail. Certbot will update the paths automatically.

Certbot (letsencrypt)

First install python-certbot-nginx package.

apt-get install python-certbot-nginx

For Apache use python-certbot-apache and the setup is similar and the “installer” is apache.

Steps:

  1. Run certbot --installer nginx.
  2. Select webroot authentication.
  3. Select your domain (extractedby certbot from Nginx configs).
  4. Enter root of the web application - it will put a file here and access from the outside, look into Nginx config for the path, i.e. /home/myrtana_sk/current/public.
  5. certbot will add paths to new certificates and also add redirect from HTTP to HTTPS. And will refresh certificates automatically (and reload nginx, because certs are loaded only once).
  6. Add additional domains, i.e. subdomains:

certbot --expand -d myrtana.sk,projects.myrtana.sk,kittydemo.myrtana.sk,www.myrtana.sk,ww.myrtana.sk,starz.myrtana.sk,light.myrtana.sk,legends.myrtana.sk,library.myrtana.sk --installer=nginx

BONUS

All this can be done with one command at once. Use with ‘–expand’ to update the certificate, but all domains must still be here along with new ones.

certbot --installer=nginx --authenticator webroot -w /home/myrtana_sk/current/public -d myrtana.sk,projects.myrtana.sk,kittydemo.myrtana.sk,www.myrtana.sk,ww.myrtana.sk,starz.myrtana.sk,light.myrtana.sk,legends.myrtana.sk,library.myrtana.sk

Conclusion

Now you have your web server using only HTTPS protocol. HTTP is redirected to HTTPS. HTTP/2 is enabled.

SSL/TLS settings adapted from Mozilla TLS configuration generator and cipherli.st* ensures the best practices in 2018. To support older clients, you might want to allow TLSv1 and TLSv1.1, but TLSv1.2 is supported in a really large portion of clients.

Also HSTS HTTP header is sent, so a typical web browser will refuse to load the site without HTTPS and without a valid certificate.

CAA record in DNS ensures that only chosen certificate authorities can issue certificates for our domain.

Postfix and Dovecot can also use the certificate for SMTP and POP3/IMAP connections. But don’t forget to add mailserver subdomain to the certificate.

Things that can be improved: HTTP configuration needs another improvements and redirect www and ww subdomains to root domain for SEO reasons (deduplicate content). Also that redirect from Certbot is good for public websites, but is awkward for private and secure APIs. In these cases it should redirect only to root domain and discard the rest of URI. myrtana.sk is all public so far.

The process wasn’t entirely straightforward in my case, because I didn’t set up HTTPS virtual host in Nginx at first, so I had to run command again to reinstall certificates. Actually I messed up like three times.

Don’t forget to test your configuration.

https://www.ssllabs.com/ssltest/analyze.html?d=myrtana.sk
https://www.ssllabs.com/ssltest/analyze.html?d=kittydemo.myrtana.sk

If you have any questions or any feedback, contact me.

Feel free to link, share, copy and remix.

TODO: fix stapling issue: 2018/02/10 01:01:55 [warn] 31122#31122: "ssl_stapling" ignored, issuer certificate not found

Detailed Process

Details are very helpful when you hit a bug, want to see the process beforehand or don’t get something at first and need to compare the outputs.

There are some long dead subdomains and weird administration interfaces. Also a few old PHP services should be disabled now, just to be sure xD

root@starz:/etc/bind# certbot --installer nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log

How would you like to authenticate with the ACME CA?
-------------------------------------------------------------------------------
1: Place files in webroot directory (webroot)
2: Spin up a temporary webserver (standalone)
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 1

Which names would you like to activate HTTPS for?
-------------------------------------------------------------------------------
1: devart.sk
2: ww.devart.sk
3: www.devart.sk
4: myrtana.sk
5: amigood.myrtana.sk
6: blackbody.myrtana.sk
7: piwik.myrtana.sk
8: posta.myrtana.sk
9: webmail.myrtana.sk
10: workingcat.myrtana.sk
11: ww.myrtana.sk
12: www.myrtana.sk
-------------------------------------------------------------------------------
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel):4
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for myrtana.sk

Select the webroot for myrtana.sk:
-------------------------------------------------------------------------------
1: Enter a new webroot
-------------------------------------------------------------------------------
Press 1 [enter] to confirm the selection (press 'c' to cancel): 1
Input the webroot for myrtana.sk: (Enter 'c' to cancel):/home/myrtana_sk/current/public
Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem
Cannot find a cert or key directive in /etc/nginx/sites-enabled/myrtana.sk for set(['myrtana.sk', 'www.myrtana.sk', 'ww.myrtana.sk']). VirtualHost was not modified.

IMPORTANT NOTES:
 - Unable to install the certificate
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/myrtana.sk/fullchain.pem. Your cert will
   expire on 2018-05-08. To obtain a new or tweaked version of this
   certificate in the future, simply run certbot again with the
   "certonly" option. To non-interactively renew *all* of your
   certificates, run "certbot renew"

Done! You can see Unable to install the certificate. We will handle that with this command:

root@starz:/etc/bind# certbot --installer nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log

How would you like to authenticate with the ACME CA?
-------------------------------------------------------------------------------
1: Place files in webroot directory (webroot)
2: Spin up a temporary webserver (standalone)
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 1

Which names would you like to activate HTTPS for?
-------------------------------------------------------------------------------
1: devart.sk
2: ww.devart.sk
3: www.devart.sk
4: myrtana.sk
5: amigood.myrtana.sk
6: blackbody.myrtana.sk
7: posta.myrtana.sk
8: webmail.myrtana.sk
9: workingcat.myrtana.sk
10: ww.myrtana.sk
11: www.myrtana.sk
-------------------------------------------------------------------------------
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel):4
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org
Cert not yet due for renewal

You have an existing certificate that has exactly the same domains or certificate name you requested and isn't close to expiry.
(ref: /etc/letsencrypt/renewal/myrtana.sk.conf)

What would you like to do?
-------------------------------------------------------------------------------
1: Attempt to reinstall this existing certificate
2: Renew & replace the cert (limit ~5 per 7 days)
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 1
Keeping the existing certificate
Deployed Certificate to VirtualHost /etc/nginx/sites-enabled/myrtana.sk for set(['myrtana.sk', 'www.myrtana.sk', 'ww.myrtana.sk'])

Please choose whether HTTPS access is required or optional.
-------------------------------------------------------------------------------
1: Easy - Allow both HTTP and HTTPS access to these sites
2: Secure - Make all requests redirect to secure HTTPS access
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/myrtana.sk

-------------------------------------------------------------------------------
Congratulations! You have successfully enabled https://myrtana.sk

You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=myrtana.sk
-------------------------------------------------------------------------------
root@starz:/etc/bind#

Select Attempt to reinstall this existing certificate and it’s done.

Expand certificate with more subdomains:

root@starz:/etc/nginx# certbot --expand --installer=nginx --authenticator webroot -d myrtana.sk,projects.myrtana.sk,kittydemo.myrtana.sk,www.myrtana.sk,ww.myrtana.sk,starz.myrtana.sk,light.myrtana.sk,legends.myrtana.sk,library.myrtana.sk
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for myrtana.sk
http-01 challenge for projects.myrtana.sk
http-01 challenge for kittydemo.myrtana.sk
http-01 challenge for www.myrtana.sk
http-01 challenge for ww.myrtana.sk
http-01 challenge for starz.myrtana.sk
http-01 challenge for light.myrtana.sk
http-01 challenge for legends.myrtana.sk
http-01 challenge for library.myrtana.sk

Select the webroot for myrtana.sk:
-------------------------------------------------------------------------------
1: Enter a new webroot
-------------------------------------------------------------------------------
Press 1 [enter] to confirm the selection (press 'c' to cancel): 1
Input the webroot for myrtana.sk: (Enter 'c' to cancel):/home/myrtana_sk/current/public

Select the webroot for projects.myrtana.sk:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /home/myrtana_sk/current/public
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

Select the webroot for kittydemo.myrtana.sk:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /home/myrtana_sk/current/public
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

Select the webroot for www.myrtana.sk:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /home/myrtana_sk/current/public
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

Select the webroot for ww.myrtana.sk:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /home/myrtana_sk/current/public
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

Select the webroot for starz.myrtana.sk:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /home/myrtana_sk/current/public
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

Select the webroot for light.myrtana.sk:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /home/myrtana_sk/current/public
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

Select the webroot for legends.myrtana.sk:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /home/myrtana_sk/current/public
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

Select the webroot for library.myrtana.sk:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /home/myrtana_sk/current/public
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0003_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0003_csr-certbot.pem
Deployed Certificate to VirtualHost /etc/nginx/sites-enabled/myrtana.sk for set(['myrtana.sk', 'www.myrtana.sk', 'kittydemo.myrtana.sk', 'projects.myrtana.sk', 'ww.myrtana.sk'])
Deployed Certificate to VirtualHost /etc/nginx/sites-enabled/myrtana.sk for set(['myrtana.sk', 'www.myrtana.sk', 'kittydemo.myrtana.sk', 'projects.myrtana.sk', 'ww.myrtana.sk'])
Deployed Certificate to VirtualHost /etc/nginx/sites-enabled/myrtana.sk for set(['myrtana.sk', 'www.myrtana.sk', 'kittydemo.myrtana.sk', 'projects.myrtana.sk', 'ww.myrtana.sk'])
Deployed Certificate to VirtualHost /etc/nginx/sites-enabled/myrtana.sk for set(['myrtana.sk', 'www.myrtana.sk', 'kittydemo.myrtana.sk', 'projects.myrtana.sk', 'ww.myrtana.sk'])
Deployed Certificate to VirtualHost /etc/nginx/sites-enabled/myrtana.sk for set(['myrtana.sk', 'www.myrtana.sk', 'kittydemo.myrtana.sk', 'projects.myrtana.sk', 'ww.myrtana.sk'])
Cannot find a VirtualHost matching domain starz.myrtana.sk.

IMPORTANT NOTES:
 - Unable to install the certificate
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/projects.myrtana.sk/fullchain.pem. Your cert
   will expire on 2018-05-10. To obtain a new or tweaked version of
   this certificate in the future, simply run certbot again with the
   "certonly" option. To non-interactively renew *all* of your
   certificates, run "certbot renew"

I know, I messed up again, because forgot to update Nginx configuration. I fixed it with the previous command. And didn’t set webroot, so had to fill it manually like 10 times.

THAT’S ALL GUYS.

Add Comment