TLS with Certbot
To set up SSL/TLS access in Unit, you need certificate bundles. Although you can use self-signed certificates, it’s advisable to obtain certificates for your website from a certificate authority (CA). For this purpose, you may employ EFF’s Certbot that issues free certificates signed by Let’s Encrypt, a non-profit CA.
Note:
The commands in this document starting with a hash (#) must be run as root or with superuser privileges.
Generating certificates
-
Install Unit on your website’s server.
-
Install Certbot on the same server, choosing
None of the above
in theSoftware
dropdown list and the server’s OS in theSystem
dropdown list at EFF’s website. -
Run the
certbot
utility and follow its instructions to create the certificate bundle. You’ll be prompted to enter the domain name of the website and validate domain ownership; the latter can be done differently. Perhaps, the easiest approach is to use the webroot method by having Certbot store a certain file locally and then access it by your domain name. First, configure Unit with a temporary route at port 80:{ "listeners": { "*:80": { "pass": "routes/acme", "comment_*:80": "Certbot attempts to reach the domain name at port 80" } }, "routes": { "acme": [ { "match": { "uri": "/.well-known/acme-challenge/*", "comment_uri": "The URI that Certbot probes to download the file" }, "action": { "share": "/var/www/www.example.com$uri/", "comment_share": "Arbitrary directory, preferably the one used for storing static files" } } ] } }
Make sure the share directory is accessible for Unit’s router process user account, usually unit:unit.
Next, run
certbot
, supplying the share directory as the webroot path:# certbot certonly --webroot -w /var/www/www.example.com/ -d www.example.com # path where the file is stored and your domain name
If you can’t employ the previous method for some reason, try using DNS records to validate your domain:
# certbot certonly --manual --preferred-challenges dns -d www.example.com # your domain name
Certbot will provide instructions on updating the DNS entries to prove domain ownership.
Any such
certbot
command stores the resulting .pem files as follows:/etc/letsencrypt/ # Location can be configured, see Certbot help └── live/ └── www.example.com # Your website name ├── cert.pem # Leaf website certificate ├── chain.pem # Root CA certificate chain ├── fullchain.pem # Concatenation of the two PEMs above └── privkey.pem # Your private key, must be kept secret
Note:
Certbot offers other validation methods (authenticators) as well, but they’re omitted here for brevity. -
Create a certificate bundle fit for Unit and upload it to the certificates section of Unit’s control API:
# cat /etc/letsencrypt/live/www.example.com/fullchain.pem \ /etc/letsencrypt/live/www.example.com/privkey.pem > bundle1.pem # Arbitrary certificate bundle's filename
# curl -X PUT --data-binary @bundle1.pem # Certificate bundle's filename \ --unix-socket /path/to/control.unit.sock # Path to Unit's control socket in your installation \ http://localhost/certificates/certbot1 # Certificate bundle name in Unit's configuration { "success": "Certificate chain uploaded." }
-
Create or update a listener to use the uploaded bundle in Unit:
# curl -X PUT --data-binary \ '{"pass": "applications/ssl_app", "tls": {"certificate": "certbot1"}}' # Certificate bundle name in Unit's configuration \ --unix-socket /path/to/control.unit.sock # Path to Unit's control socket in your installation \ 'http://localhost/config/listeners/*:443' # Listener's name in Unit's configuration
-
Try accessing your website via HTTPS:
$ curl https://www.example.com -v ... * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.3 (IN), TLS Unknown, Certificate Status (22): * TLSv1.3 (IN), TLS handshake, Unknown (8): * TLSv1.3 (IN), TLS Unknown, Certificate Status (22): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS Unknown, Certificate Status (22): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.3 (IN), TLS Unknown, Certificate Status (22): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Client hello (1): * TLSv1.3 (OUT), TLS Unknown, Certificate Status (22): * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server did not agree to a protocol * Server certificate: * subject: CN=www.example.com * start date: Sep 21 22:10:42 2020 GMT * expire date: Dec 20 22:10:42 2020 GMT ...
Renewing certificates
Certbot enables renewing the certificates manually or automatically. For manual renewal and rollover:
-
Repeat the preceding steps to renew the certificates and upload the new bundle under a different name:
# certbot certonly --standalone What would you like to do? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: Keep the existing certificate for now 2: Renew & replace the cert (may be subject to CA rate limits)
# cat /etc/letsencrypt/live/www.example.com/fullchain.pem \ /etc/letsencrypt/live/www.example.com/privkey.pem > bundle2.pem # Arbitrary certificate bundle's filename
# curl -X PUT --data-binary @bundle2.pem # Certificate bundle's filename \ --unix-socket /path/to/control.unit.sock # Path to Unit's control socket in your installation \ http://localhost/certificates/certbot2 # Certificate bundle name in Unit's configuration { "success": "Certificate chain uploaded." }
Now you have two certificate bundles uploaded; Unit knows them as certbot1 and certbot2. Optionally, query the certificates section to review common details such as expiry dates, subjects, or issuers:
# curl --unix-socket /path/to/control.unit.sock \ # Path to Unit's control socket in your installation http://localhost/certificates
-
Update the listener, switching it to the renewed certificate bundle:
# curl -X PUT --data-binary 'certbot2' # New certificate bundle name in Unit's configuration \ --unix-socket /path/to/control.unit.sock # Path to Unit's control socket in your installation \ 'http://localhost/config/listeners/*:443/tls/certificate' # Listener's name in Unit's configuration
Note:
There’s no need to shut Unit down; your server can stay online during the rollover. -
Delete the expired bundle:
# curl -X DELETE --unix-socket /path/to/control.unit.sock # Path to Unit's control socket in your installation \ 'http://localhost/certificates/certbot1' # Old certificate bundle name in Unit's configuration { "success": "Certificate deleted." }
-
You can also make use of Unit’s SNI support by configuring several certificate bundles for a listener.
Suppose you’ve successfully used Certbot to obtain Let’s Encrypt certificates for two domains, www.example.com and cdn.example.com. First, upload them to Unit using the same steps as earlier:
# cat /etc/letsencrypt/live/cdn.example.com/fullchain.pem \ /etc/letsencrypt/live/cdn.example.com/privkey.pem > cdn.example.com.pem # Arbitrary certificate bundle's filename
# cat /etc/letsencrypt/live/www.example.com/fullchain.pem \ /etc/letsencrypt/live/www.example.com/privkey.pem > www.example.com.pem # Arbitrary certificate bundle's filename
# curl -X PUT --data-binary @cdn.example.com.pem # Certificate bundle's filename \ --unix-socket /path/to/control.unit.sock # Path to Unit's control socket in your installation \ http://localhost/certificates/cdn.example.com # Certificate bundle name in Unit's configuration { "success": "Certificate chain uploaded." }
# curl -X PUT --data-binary @www.example.com.pem # Certificate bundle's filename \ --unix-socket /path/to/control.unit.sock # Path to Unit's control socket in your installation \ http://localhost/certificates/www.example.com # Certificate bundle name in Unit's configuration { "success": "Certificate chain uploaded." }
Next, configure the listener, supplying both bundles as an array value for the tls/certificate option:
# curl -X PUT --data-binary '{"certificate": ["cdn.example.com", "www.example.com"]}' # Certificate bundle names in Unit's configuration \ --unix-socket /path/to/control.unit.sock # Path to Unit's control socket in your installation \ 'http://localhost/config/listeners/*:443/tls' # Listener's name in Unit's configuration
Unit does the rest of the job, automatically figuring out which bundle to produce for each incoming connection to both domain names.
Note:
Currently, Certbot doesn’t have installer plugins that enable automatic certificate rollover in Unit. However, you can set up Certbot’s hooks using the commands listed here to the same effect.