Self-generated certificate is good for bootstrapping purpose but sooner than later we need a valid SSL certificate for all services (IMAPS, HTTPS, DoT etc).
1. Install
pacman -S certbot certbot-nginx
2.1 Manual generation
Init the certificate generation
certbot certonly --manual -d "*.costan.ro" -d "costan.ro"
The above tool spits out a DNS challenge that we need to set as a DNS TXT record.
Set the DNS TXT record
It depends on your DNS provider but usually is as simple as adding a new record.
grep acme /etc/nsd/zones/costan.ro
_acme-challenge IN TXT "PDFYVU1v3Jlo6Yeo50-LHiokC8PwNeZl_-FCz13CKPs"
Check using local DNS server first (mind @ns.costan.ro local DNS server at the end)
dig +short TXT _acme-challenge.costan.ro @ns.costan.ro
PDFYVU1v3Jlo6Yeo50-LHiokC8PwNeZl_-FCz13CKPs
Wait for DNS propagation and check using public (or default) DNS server. This "wait" part is important otherwise LetsEncrypt won't be able to resolve the DNS challenge and SSL generation process will fail.
dig +short TXT _acme-challenge.costan.ro @8.8.8.8
PDFYVU1v3Jlo6Yeo50-LHiokC8PwNeZl_-FCz13CKPs
Finish the certificate generation
Once the DNS TXT change is propagated press "ENTER" and certbot tool will generate all certificate files.
List the certificate
certbot certificates
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Found the following certs: Certificate Name: costan.ro Serial Number: 4f6208230dcfb39f339295c19a5eb7781fa Key Type: ECDSA Domains: *.costan.ro Expiry Date: 2023-03-19 12:40:17+00:00 (VALID: 89 days) Certificate Path: /etc/letsencrypt/live/costan.ro/fullchain.pem Private Key Path: /etc/letsencrypt/live/costan.ro/privkey.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Usage
Change configuration files for all public services (Postfix, Dovecot, Nsd, etc) and point to newly generated fullchain.pem and privkey.pem files.
postconf -n | grep -E "smtpd_tls_.*_file"
smtpd_tls_cert_file = /etc/letsencrypt/live/costan.ro/fullchain.pem smtpd_tls_key_file = /etc/letsencrypt/live/costan.ro/privkey.pem
doveconf ssl_cert ssl_key
ssl_cert = </etc/letsencrypt/live/costan.ro/fullchain.pem ssl_key = </etc/letsencrypt/live/costan.ro/privkey.pem
Checker
And last, check installed SSL cert using an SSLlabs online checker or openssl tool.
openssl s_client -connect www.costan.ro:443
2.2 Semi-automated renewal
Config file
Basic config files with a few cmd line settings.
cat /etc/letsencrypt/cli.ini
key-type = ecdsa email = letsencrypt@costan.ro authenticator = manual agree-tos = true
Auth script
This script will update _acme-challenge in DNS config, sign and reload the zone.
cat /etc/letsencrypt/renewal-hooks/auth.sh
#!/usr/bin/env sh echo CERTBOT_DOMAIN=$CERTBOT_DOMAIN echo CERTBOT_VALIDATION=$CERTBOT_VALIDATION echo CERTBOT_REMAINING_CHALLENGES=$CERTBOT_REMAINING_CHALLENGES echo CERTBOT_ALL_DOMAINS=$CERTBOT_ALL_DOMAINS echo Sed: replace _acme_challenge in /etc/nsd/zones/$CERTBOT_DOMAIN ...: sed -Ei "s/_acme-challenge(.+)\"(.+)\"/_acme-challenge\1\"$CERTBOT_VALIDATION\"/" /etc/nsd/zones/$CERTBOT_DOMAIN echo NSD: sign/reload $CERTBOT_DOMAIN zone... cd /etc/nsd/zones/ ldns-signzone -n -p -e $(date -d "$(date) +3 month" +%Y%m%d%H%M%S) costan.ro Kcostan.ro.+008+03304 Kcostan.ro.+008+19957 nsd-control reconfig nsd-control reload $CERTBOT_DOMAIN echo Wait for DNS propagation... sleep 15s echo "DONE."
Deploy script
This script restarts all services that uses widlcard certificate.
cat /etc/letsencrypt/renewal-hooks/deploy/deploy.sh
#!/usr/bin/env sh echo Restarting Nginx... systemctl restart nginx echo Restarting Postfix... systemctl restart postfix echo Restarting Dovecot... systemctl restart dovecot echo DEPLOY-HOOK: DONE.
Renew
And finally the magic command.
certbot renew --manual-auth=/etc/letsencrypt/renewal-hooks/auth.sh -v
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/costan.ro.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Certificate is due for renewal, auto-renewing... Plugins selected: Authenticator manual, Installer None Renewing an existing certificate for *.costan.ro Performing the following challenges: dns-01 challenge for costan.ro Running manual-auth-hook command: /etc/letsencrypt/renewal-hooks/auth.sh Hook '--manual-auth-hook' for costan.ro ran with output: CERTBOT_DOMAIN=costan.ro CERTBOT_VALIDATION=uY5-sXNpcCtIQoVmA7jy-kIg4-ipseBLjjOzyoTQzpI CERTBOT_REMAINING_CHALLENGES=0 CERTBOT_ALL_DOMAINS=costan.ro Sed: replace _acme_challenge in /etc/nsd/zones/costan.ro ...: NSD: sign/reload costan.ro zone... reconfig start, read /etc/nsd/nsd.conf ok ok Wait for DNS propagation... DONE. Waiting for verification... Cleaning up challenges - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations, all renewals succeeded: /etc/letsencrypt/live/costan.ro/fullchain.pem (success) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Testing
certbot certificates
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Found the following certs: Certificate Name: costan.ro Serial Number: 3fc9af33d62a240238dc4c1fb3a3dc616e6 Key Type: ECDSA Domains: *.costan.ro Expiry Date: 2023-06-14 10:13:39+00:00 (VALID: 89 days) Certificate Path: /etc/letsencrypt/live/costan.ro/fullchain.pem Private Key Path: /etc/letsencrypt/live/costan.ro/privkey.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2.3 Fully-automated renewal
Start systemd service that runs twice a day and checks/executes any due renewal.
systemctl start certbot-renew.timer
systemctl enable certbot-renew.timer
After automatic generation ran we end up with brand new certificate.
certbot certificates
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Found the following certs: Certificate Name: costan.ro Serial Number: 3f385ce1c48f559cac06c8ba0f7bd13521c Key Type: ECDSA Domains: *.costan.ro Expiry Date: 2023-09-12 02:57:29+00:00 (VALID: 89 days) Certificate Path: /etc/letsencrypt/live/costan.ro/fullchain.pem Private Key Path: /etc/letsencrypt/live/costan.ro/privkey.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
And now we have 3 certificates in Let's Encrypt archive, one at each 3 months.
ls -l /etc/letsencrypt/archive/costan.ro/
total 60 -rw-r--r-- 1 root root 1558 Dec 19 15:40 cert1.pem -rw-r--r-- 1 root root 1558 Mar 16 13:13 cert2.pem -rw-r--r-- 1 root root 1558 Jun 14 06:57 cert3.pem -rw-r--r-- 1 root root 3749 Dec 19 15:40 chain1.pem -rw-r--r-- 1 root root 3749 Mar 16 13:13 chain2.pem -rw-r--r-- 1 root root 3749 Jun 14 06:57 chain3.pem -rw-r--r-- 1 root root 5307 Dec 19 15:40 fullchain1.pem -rw-r--r-- 1 root root 5307 Mar 16 13:13 fullchain2.pem -rw-r--r-- 1 root root 5307 Jun 14 06:57 fullchain3.pem -rw------- 1 root root 241 Dec 19 15:40 privkey1.pem -rw------- 1 root root 241 Mar 16 13:13 privkey2.pem -rw------- 1 root root 241 Jun 14 06:57 privkey3.pem
3. References
Updates
- [2022-12-19] - initial wildcard certificate
- [2023-03-16] - manual renew of wildcard certificate
- [2023-06-14] - automatic renew of wildcard certificate
- [2024-04-16] - add deploy.sh script