Changelog
- Aug 6, 2023
The certificates installation is now based ondehydrated.
The previous documentation based oncertbot
will be left as is at the bottom of this page, but it won't be updated anymore. - May 18, 2023
added the option--key-type rsa
to thecertbot
command, to avoid thatcertbot
will silently default toECDSA
the private key format, which results not understandable by myopenssl-1.1
. In this way the format of the private key will beRSA
. More info here.
To enable HTTPS on your website, you need to get a certificate (a type of file) from a Certificate Authority (CA). Let’s Encrypt is a CA. In order to get a certificate for your website’s domain from Let’s Encrypt, you have to demonstrate control over the domain. With Let’s Encrypt, you do this using software that uses the ACME protocol which typically runs on your web host.
Upgrading notes
In case you have to replace a working installation of certificates based on certbot,
you don't need to take any precautions as they won't be overwritten by the new ones generated by dehydrated.
Therefore it is possible to run our tests on the production server itself. Of course it will be good to use a test domain while tinkering with Apache.
dehydrated
installation
- github dehydrated page
- AlienBob article (for
Slackware
users, but the informations are generic and suitable for allLinux
distributions)
Dehydrated is a client for signing certificates with an ACME-server (e.g. Let's Encrypt) implemented as a relatively simple (zsh-compatible) bash-script. This client supports both ACME v1 and the new ACME v2 including support for wildcard certificates!
dehydrated
is a simple shell program that requires no dependencies, unlike the official certbot
program, which needs a very long set of python
libraries. Maintaining all this volume of programs proved to be quite time expensive, which is why I decided to switch to dehydrated
.
Since your distribution is likely to provide a dehydrated
package, you can install it from there. Otherwise download and install the program from the github site.
Here is a short example of a manual installation in /usr/local (modify as needed):
VERSION=0.7.1 wget https://github.com/dehydrated-io/dehydrated/releases/download/v${VERSION}/dehydrated-${VERSION}.tar.gz tar xzf dehydrated-${VERSION}.tar.gz cd dehydrated-${VERSION} chown -R root:root . cp dehydrated /usr/local/bin/ mkdir -p /usr/local/man/man1 /usr/local/share/doc/dehydrated-${VERSION}/docs/examples cp docs/man/dehydrated.1 /usr/local/man/man1/ mkdir -p /etc/dehydrated cp docs/examples/config /etc/dehydrated/config.new # .new just to avoid to overwrite the existing config. Rename to config cp CHANGELOG* LICENSE* README* /usr/local/share/doc/dehydrated-${VERSION} cp docs/*.md /usr/local/share/doc/dehydrated-${VERSION}/docs cp docs/examples/{domains.txt,hook.sh} /usr/local/share/doc/dehydrated-${VERSION}/docs/examples
Configuration
Go to the /etc/dehydrated folder, where we have the configuration files and where the certificates will be installed. These are my changes to the config file:
CA="letsencrypt-test" CHALLENGETYPE="http-01" WELLKNOWN="/var/www/dehydrated" KEY_ALGO=rsa CONTACT_EMAIL=postmaster@mydomain.tld
CA
contains theURL
to theLet's Encrypt
server, or a parameter (in this caseletsencrypt-test)
that tells the program whichURL
to use. While testing let's leaveletsencrypt-test
so that the server will not be clogged. We would be banned accordingly if we exceed the limit of invalid attempts. Once everything is ok we will change it toCA="letsencrypt"
. More info here.CHALLENGETYPE="http-01"
As we know, we need to prove to have control over the domains we want to get a certificate for. To do this, access will be tested to a verification file placed on our web server, in a folder whereApache
must have access for each domain to be certified.
There are also otherCHALLENGETYPEs.
For exampledns-01
is suitable for certifying domains with wildcards such as *.sagredo.eu, but it is necessary to have programs to automatically handle theDNS
server. This part is therefore not covered here.WELLKNOWN="/var/www/dehydrated"
is the directory wheredehydrated
will install the ‘challenge-tokens’, which will be retrieved viahttp
in order to validate the domain.KEY_ALGO=rsa
specifies the algorithm to be used for the public key.
dehydrated
is able to launch a program of our choice after the successful creation of the certificate (HOOK="${BASEDIR}/hook.sh"
). We will dispense with this opportunity and manually create a file that will handle the post-installation tasks. In fact, I noticed that, during the creation of the certificate, dehydrated
launches hook.sh several times, which is not satisfactory at all.
To use dehydrated
we need to register an account:
# dehydrated --register --accept-terms # INFO: Using main config file /etc/dehydrated/config + Generating account key... + Registering account key with ACME server... + Fetching account URL... + Done!
Account information is saved in the /etc/dehydrated/accounts directory.
Domains configuration
Create a domains.txt file where we will arrange the domains to be certified separated by a blank space. Domains can be entered on separate lines. Those on the same line will refer to the same certificate, whose name will be the first domain in that line.
For example
mx.mydomain.tld mydomain.tld www.mydomain.tld webmail.mydomain.tld otherdomain.tld www.otherdomain.tld webmail.otherdomain.tld
will create two certificates mx.mydomain.tld and mydomain.tld. In what follows the first one is the certificate for the mail server, the other one for Apache
sites (webmail for instance). Of course you may want to have one single certificate for everything, especially if they live in the same host.
It is also possible to write other txt files in the /etc/dehydrated/domains.d folder.
Apache
configuration
In a typical case we will have to certify many domains, some of which are dedicated to the mail server, others to simple web spaces (for example webmail or the qmailadmin
control panel) each with their own VirtualHost
and their own DocumentRoot
folder. We will make sure to save the 'challenge-tokens' in the same directory accessible to all these domains, let's say /var/www/dehydrated, which is the one referred by the WELLKNOWN
parameter in the configuration file.
First, let's create this directory:
mkdir -p /var/www/dehydrated
Since we have to include the same code multiple times (one for each VirtualHost)
it is convenient to automate everything by saving it in a redirect.conf file to be imported into each VirtualHost.
Let's save it to the Apache
configuration folder, i.e. /etc/httpd:
cat > /etc/httpd/redirect.conf << __EOF__ <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge RewriteRule ^(.*) https://%{SERVER_NAME}\$1 [R,L] </IfModule> Alias /.well-known/acme-challenge /var/www/dehydrated <Directory /var/www/dehydrated> Options None AllowOverride None Require all granted </Directory> # Define or change \${LOGDIR} to where your server saves the logs CustomLog "\${LOGDIR}/dehydrated_access.log" combined ErrorLog "\${LOGDIR}/dehydrated.f2b.log" __EOF__
By doing so, requests for files like /.well-known/acme-challenge/* will be served in the folder /var/www/dehydrated, while the rest of the traffic will be redirected (in this example) to the https address with the same domain (SERVER_NAME
). As you can see, this assumes that the Apache
's mod_rewrite
module has been enabled.
Again for matters of order and economy of code, it will be convenient to define some variables in the general configuration file /etc/httpd.conf (or wherever you prefer). For example, we can save there the path of the file to be included and, since we're at it, the file that we will use later to import the SSL
certificates and also the folder that will host the certificates:
Define REDIRECT /etc/httpd/redirect.conf Define SSL /etc/httpd/ssl.conf Define CERTDIR /etc/dehydrated/certs
So here is a schema of the virtual host related to the domain that leads to our mail server:
<VirtualHost *:80> ServerName mx.mydomain.tld Include ${REDIRECT} </VirtualHost>
Certificates creation
It is now the time to create our certificates:
# dehydrated -c # INFO: Using main config file /etc/dehydrated/config Processing mx.mydomain.tld + Creating new directory /etc/dehydrated/certs/mx.mydomain.tld ... + Signing domains... + Generating private key... + Generating signing request... + Requesting new certificate order from CA... + Received 1 authorizations URLs from the CA + Handling authorization for mx.mydomain.tld + 1 pending challenge(s) + Deploying challenge tokens... + Responding to challenge for mx.mydomain.tld authorization... + Challenge is valid! + Cleaning challenge tokens... + Requesting certificate... + Order is processing... + Checking certificate... + Done! + Creating fullchain.pem... + Done!
We should get a similar result for each requested certificate (one for each line in domains.txt). The certificates are saved in the certs folder, whose address we stored in the CERTDIR
variable. Use man
or even dehydrated -h
to see how to delete or revoke certificates.
Once the certificate creation procedure has been successful we can set dehydrated
to connect to the production address (CA="letsencrypt"
in the config file).
At this point the certificates are not yet seen neither by the web-server, nor by qmail
and nor by dovecot
. If you are still testing and have a working certificate in production, use a test domain for the following tests with Apache.
Apache
SSL
configuration
As far as Apache
is concerned, let's create a file with the SSL
settings to import into each VirtualHost:
cat > /etc/httpd/ssl.conf << __EOF__ SSLEngine on SSLCertificateFile \${CERTDIR}/\${CERTNAME}/fullchain.pem SSLCertificateKeyFile \${CERTDIR}/\${CERTNAME}/privkey.pem SSLCertificateChainFile \${CERTDIR}/\${CERTNAME}/fullchain.pem __EOF__
CERTNAME
is the name of the certificate, to pass before including the file, as in the example below. CERTDIR
is a variable that we have already defined above in the httpd.conf file.
This is an example of VirtualHost
configured to host web pages in https, for example the RoundCube
webmail:
<VirtualHost *:80> ServerName webmail.mydomain.tld ServerAlias webmail.otherdomain.tld Include ${REDIRECT} </VirtualHost> <VirtualHost *:443> # The name of the certificate is the first domain of the line in /etc/dehydrated/domains.txt Define CERTNAME mydomain.tld # SSL cannot work until the certificate is in place. Comment it out initially Include ${SSL} ServerName webmail.mydomain.tld ServerAlias webmail.otherdomain.tld ErrorLog ${LOGDIR}/roundcube_error.log CustomLog ${LOGDIR}/roundcube_access.log combined DocumentRoot ${HTDOCS}/roundcube <Directory ${HTDOCS}/roundcube> Require all granted </Directory> </VirtualHost>
Certificate installation on qmail
and dovecot
Prepare a script that runs dehydrated
and handles the certificate installation (download). Let's save it to /usr/local/bin/my_dehydrated.sh:
cat > /usr/local/bin/my_dehydrated.sh << __EOF__ #!/bin/sh CERTNAME=mx.mydomain.tld CERTDIR=/etc/dehydrated/certs/\${CERTNAME} QMAILDIR=/var/qmail QMAILCTL=/usr/local/bin/qmailctl DOVECOTCTL=/usr/local/bin/dovecotctl APACHECTL=/usr/sbin/apachectl DEHYDRATED=/usr/bin/dehydrated # cert renewal \$DEHYDRATED -c if [ \$? -eq 0 ]; then # qmail cert backup if [ ! -d "\${QMAILDIR}/control/certs_backup" ]; then mkdir -p \${QMAILDIR}/control/certs_backup fi echo "Setting up the cert for qmail" cp -p \${QMAILDIR}/control/*.pem \${QMAILDIR}/control/certs_backup/ cat \${CERTDIR}/privkey.pem \${CERTDIR}/fullchain.pem > \${QMAILDIR}/control/servercert.pem chown vpopmail:vchkpw \${QMAILDIR}/control/*.pem chmod o-r \${QMAILDIR}/control/*.pem # restart qmail \$QMAILCTL restart # restart dovecot echo "Restarting dovecot" \$DOVECOTCTL stop sleep 5 \$DOVECOTCTL start # restart apache echo "Restarting apache" \$APACHECTL -k graceful exit 0 else exit 1 fi __EOF__ chmod +x /usr/local/bin/my_dehydrated.sh
As we know, the qmail
certificate (control/servercert.pem file) is the result of the concatenation of the private key and the fullchain. This is done by the above script. As far as qmail
is concerned, we don't have to do anything else.
Regarding Dovecot
, however, it is necessary to correctly set the address of the certificates in the conf.d/10-ssl.conf file:
ssl_cert = </etc/dehydrated/certs/mx.mydomain.tld/fullchain.pem ssl_key = </etc/dehydrated/certs/mx.mydomain.tld/privkey.pem
Letsencrypt recommends renewing certificates automatically when they have a third of their total lifetime left. The current certificate lifetime is 90 days, that means renewing 30 days before expiration. Let's set the cronjob
so that it runs the script once a month:
cat > /etc/cron.d/dehydrated << __EOF__ 40 2 6 * * /usr/local/bin/my_dehydrated.sh >> /var/log/cron 2>&1 __EOF__
You can now remove the old update_tmprsadh crontab
line.
Installing certbot
The documentation concerning certbot
is no longer updated as it has been replaced by the one based on dehydrated.
It is left intact in case it might still be of help to someone.
More info:
Here is how to install and configure a valid certificate from Let's Encrypt for your qmail
and dovecot
servers. The installation will be done by certbot.
Certbot is part of EFF’s effort to encrypt the entire Internet. Secure communication over the Web relies on HTTPS, which requires the use of a digital certificate that lets browsers verify the identity of web servers (e.g., is that really google.com?). Web servers obtain their certificates from trusted third parties called certificate authorities (CAs). Certbot is an easy-to-use client that fetches a certificate from Let’s Encrypt—an open certificate authority launched by the EFF, Mozilla, and others—and deploys it to a web server.
Installing the certificate
It needs tons of prerequisites and a python
v. 3, but there must be a package for your distribution that will do everything for you. Slackware users should refer to the slackbuild from SBO here (if you don't want to bother to manually install all the dependencies, sbotools
is your friend).
The certificate will be provided once you prove to be the owner of the matched domain(s). So certbot
has to install an ACME challenge in a directory of your choice and then retrieve it via http. If the challenge is successfull the certificate will be installed in /etc/letsencrypt.
Create the "webroot" dir where the ACME challenge will be stored:
mkdir -p /path/to/webroot
Now set up an apache
virtual domain. In this example yourdomain.tld is the domain where you are going to do the smtp
connection:
<VirtualHost *:80> ServerName yourdomain.tld DocumentRoot /path/to/webroot <Directory /path/to/webroot> Require all granted </Directory> </VirtualHost>
Prepare a script to install your certs via certbot
as /usr/local/bin/my_certbot.sh:
cat > /usr/local/bin/my_certbot.sh << __EOF__ #!/bin/sh # CERTBOT=/usr/bin/certbot DOMAIN=mydomain.tld \$CERTBOT certonly \\ --webroot \\ --webroot-path /path/to/webroot \\ --preferred-challenges http-01 \\ --key-type rsa \\ -d \${DOMAIN} \\ --email myemail@\${DOMAIN} \\ --renew-by-default \\ --agree-tos \\ --text # qmail cert if [ ! -d "/var/qmail/control/certs_backup" ]; then mkdir -p /var/qmail/control/certs_backup fi cp -p /var/qmail/control/*.pem /var/qmail/control/certs_backup/ cat /etc/letsencrypt/live/\${DOMAIN}/privkey.pem /etc/letsencrypt/live/\${DOMAIN}/fullchain.pem > /var/qmail/control/servercert.pem chown vpopmail:vchkpw /var/qmail/control/*.pem chmod o-r /var/qmail/control/*.pem /usr/local/bin/qmailctl restart # dovecot cert (you have to set the path inside 10-ssl.conf accordingly) /usr/local/bin/dovecotctl restart __EOF__ chmod +x /usr/local/bin/my_certbot.sh
mydomain.tld will be used as the certificate's name. To better understand what certbot can do:
certbot --help all
Remember to set the x flag:
chmod +x /usr/local/bin/my_certbot.sh
Now try to run it and hopefully you'll get the certificate with no errors.
Finally set up a cronjob that renew the cert once a month (the certificate remains valid for three months):
15 2 20 * * /usr/local/bin/my_certbot.sh >> /var/log/cron
Remember to disable the old update_tmprsadh
crontab line.
Configuring qmail
and dovecot
As far as qmail
is concerned the private key and the fullchain must be merged into a single file /var/qmail/control/servercert.pem. This is done by the above script itself.
Concerning dovecot
, you just have to adjust your /usr/local/dovecot/etc/dovecot/conf.d/10-ssl.conf file as follows:
#ssl_cert = </etc/ssl/certs/dovecot.pem #ssl_key = </etc/ssl/private/dovecot.pem ssl_cert = </etc/letsencrypt/live/yourdomain.tld/fullchain.pem ssl_key = </etc/letsencrypt/live/yourdomain.tld/privkey.pem
Restart qmail
and dovecot
to enable the new certificate.
Comments
Crons syntax
Mike January 10, 2024 10:56 CET
Hi Roberto,
you have typo mistake, I guess your's day also 24h ))
Reply | Permalink
Crons syntax
Roberto Puzzanghera Mike January 10, 2024 11:11 CET
Thank you, corrected
Reply | Permalink
How to try if certificate is working and valid?
Joao Savioli August 31, 2023 19:14 CET
Hi,
Is it to possible to try if qmail smtp certificate is a valid and working?
Thanks
Joao
Reply | Permalink
How to try if certificate is working and valid?
Roberto Puzzanghera Joao Savioli August 31, 2023 19:56 CET
you can do
and look for Verify return code: 0 (ok) after the certificate has been presented
Reply | Permalink
How to try if certificate is working and valid?
Joao Savioli Roberto Puzzanghera September 1, 2023 15:36 CET
Hello Roberto, thank you for replying.
I've trid this command from two servers, and they return different results. How is it possible?
PS: I've changed my host for MY_HOST_SERVER.
FROM SERVER 1:
FROM SERVER 2:
Reply | Permalink
How to try if certificate is working and valid?
Roberto Puzzanghera Joao Savioli September 1, 2023 17:42 CET
The first example shows that after the handshake it didn't get almost anything back, but I don't know why (SSL handshake has read 179 bytes and written 351 bytes).
I'm not an ssl expert at all, but it seems like the client is sending a request with the SSL3 protocol, which is not accepted by the server, where the renegotiation could be also forbidden. I would try to upgrade the openssl client and retry, or set it not to use ssl3 anymore. Have also a look here https://github.com/openssl/openssl/issues/18291#issuecomment-1123685518
Reply | Permalink
multiple domain certificates
Ivelin Topalov July 21, 2021 23:13 CET
i have a few domains in same machine served by qmail
the script for letsencrypt 1 domain cert for qmail combines priv key and fullchain in servercert
as i having few - how to make 1 servercert for all domains - just cat all one after another like ke1 chain1 ke2 chain2 ... ?
Reply | Permalink
multiple domain certificates
Roberto Puzzanghera Ivelin Topalov July 22, 2021 07:31 CET
I'm not sure that merging the domains' certificates will work.
I suggest to create a cumulative certificate (which is valid for all your domains) like this
this will work for sure
Reply | Permalink
update_tmprsadh - Termporary certificates
Marco Varanda June 1, 2021 22:05 CET
Hello Roberto,
Your site is great !
When I was setting up my server, I used update_tmprsadh (crontab) and its PEM, but when I create Let's Encrypt REAL certificate, I changed dovecot, site (apache, webmail) .. .everythink is OK. (I prefer DNS Challenge - djbdns)
But .... Crontab and update_tmprsadh continue running.
Maybe, you can include in this page, after you successful with Certbot (Let's Encrypt), disable crontab line, and delete the files (correcting dovecot /etc/dovecot/conf.d/10-ssl.conf) restart, etc
Thanks again
Varanda
Reply | Permalink
update_tmprsadh - Termporary certificates
Roberto Puzzanghera Marco Varanda June 2, 2021 10:08 CET
Thanks for the hint.. I'll do it as soon as possible
Reply | Permalink
cosmetic error
Marco Varanda April 23, 2020 01:52 CET
Crontab script refers to my_certbot.sh
but we created my_cert.sh script
Reply | Permalink
cosmetic error
Roberto Puzzanghera Marco Varanda April 23, 2020 08:43 CET
Corrected. Thank you
Reply | Permalink
Certbot Error....
Elcio Bortolin October 19, 2018 14:44 CET
I'm trying to use certbot. I installed all dependencies. But an error occurs with zope.interface.
Can you help me?
Reply | Permalink
Certbot Error....
Roberto Puzzanghera Elcio Bortolin October 19, 2018 19:03 CET
it seems like zope.interface is not installed... try "pip install zope.interface"
Reply | Permalink
sacha August 30, 2018 18:59 CET
Hi, I got Apache & qmail (netqmail-1.06) server on 1 ip, 7 domain name managed by SNI, and using for a while letsencrypt for https.
I would like to set up qmail with certificate from Let's Encrypt
I hope you could help me to configure it, as you seems the qmail master.
About qmail, using netqmail-1.06 do I have to patch anything as netqmail-1.06-tls+auth-20151215_20160609.patch or large-dns-response.patch?
As i already got 1 cert by domain for Apache, could I reuse it for qmail?
In this case, should copy .pem file in /control/ as:
Otherwise, how could I specify each .pem file for each Domain?
Best
Reply | Permalink
Roberto Puzzanghera sacha August 30, 2018 19:19 CET
Hi, no patch is needed for qmail. You can use one single cert for all your domain, provided you create your cert with
Be aware that the new letsencrypt provides wildcards to allow subdomains, but a TXT record in your DNS is required.
And no, you can't use your apache's cert as is. You have to combine that cert with the private key, as shown above inside the script.
Reply | Permalink
sacha Roberto Puzzanghera August 30, 2018 20:53 CET
Thanks a lot Roberto for you answer.
Well note about the TXT record
I guess it must be something as:
acme-challenge.mydomain1.com. IN TXT "yB_EQ-w[…]E"
acme-challenge.www.mydomain1.com. IN TXT "yB_EQ-w[…]E"
About cert, if I understood what you said.
I have to create a new certificate only for Qmail, with -d parameter with all domain.
Reply | Permalink
Roberto Puzzanghera sacha August 30, 2018 21:31 CET
Yes to both questions
Reply | Permalink
sacha Roberto Puzzanghera August 31, 2018 07:35 CET
Thanks
Sorry to insist, but if qmail doesn't need any patch to run tls.
For what netqmail-1.06-tls+auth-20151215_20160609.patch is use for?
Reply | Permalink
Roberto Puzzanghera sacha August 31, 2018 15:35 CET
Sorry I thought at first reading that you were asking if modifications were needed to that patch. Of course you need qmail to be patched for tls, so the only patch that makes sense if you want to test letsencrypt certificates is qmail-tls
Reply | Permalink
sacha Roberto Puzzanghera September 1, 2018 14:50 CET
Hi roberto.
No problem, that's my fault.
As I'm french and english isn't my mother tongue I guess my question wasn't clear.
So to get tls on my qmail, I need to patch netqmail with:
Qmail-tls
http://inoa.net/qmail-tls/netqmail-1.06-tls-20160918.patch
or
your patch including smtp-auth + qmail-tls + forcetls
https://notes.sagredo.eu/files/qmail/patches/roberto-netqmail-1.06_auth_tls_force-tls.patch-latest
or you combined patch
https://notes.sagredo.eu/files/qmail/patches/roberto-netqmail-1.06.patch-latest.gz
Is that right?
Reply | Permalink
Roberto Puzzanghera sacha September 1, 2018 15:20 CET
yes, of course one reason to use TLS is securing the auth, that's the reason why I made a package with auth and tls patches together. My big patch includes both.
Reply | Permalink
Let's Encrpyt
Eric June 18, 2018 17:58 CET
Roberto,
Isn't 'DOMAIN=smtp.yourdomain.tld' in the above script (/usr/local/bin/my-cert.sh) supposed to be 'DOMAIN=yourdomain.tld'
Eric
Reply | Permalink
Let's Encrpyt
Roberto Puzzanghera Eric June 18, 2018 21:46 CET
Hi Eric,
it is the domain name that you use to connect to your server.. in my example I'm imaging that there are different servers for smtp imap and so on
Reply | Permalink
Reason 3 aliases needed
Bart Koppers March 25, 2018 13:22 CET
Hi! Usefull post about using the LE certificate for qmail.
I was wondering: is there a specific reason you stated the aliases: smtp, pop3, imap?
IMHO, if it is 1 host, could you not (better) use 1 alias, for example mail.mydomain.mynet ?
regards, Bart
Reply | Permalink
Reason 3 aliases needed
Roberto Puzzanghera Bart Koppers March 25, 2018 13:33 CET
Hi! Because I'm not excluding that the three could live in three separated (virtual) servers
Reply | Permalink