Letsencrypt with acme.sh and Apache
I use the software acme.sh for getting certificates, a simple single shell script.
Features and benefits of this installation
This article describes a generic setup for Apache that has the following advantages:
- The Apache configuration is never manipulated at runtime for fetching certificates.
- Port 80 is only used for Letsencrypt.
- All other web accesses are redirected from central to the respective https web pages.
- We don’t need access to the DNS, as described here.
This is a great setup because it avoids complexity (no dynamic reconfiguration of components and no interface to DNS). This also makes it more secure.
Preparations
In order for all this to work, we need the following:
- A Linux system, for servers I like to use Debian.
- An installed Apache webserver
- Switch on mod_rewrite, on Debian with
a2enmod rewrite
. - Also of course mod_ssl for https, on debian with
a2enmod ssl
. - An installed version of acme.sh, I described the installation here. But we don’t use DNS-Challenge here.
Setup and configuration
First we create a directory where acme.sh can fight the so-called challenge with Letsencrypt:
Translated with www.DeepL.com/Translator
mkdir -p /var/www/letsencrypt
chown -R www-data:www-data /var/www/letsencrypt
The verification works in such a way that we use acme.sh to request a certificate from Letsencrypt and Letsencrypt then looks to see if it can retrieve a secret what we put in /var/www/letsencrypt. If so, it verifies that the domain belongs to us. For this we have to make this retrieval possible. We do this centrally for all web pages or virtual hosts on this web server.
Therefore we set up our own virtual central host in Apache, which intercepts all accesses to port 80, i.e. normal HTTP, and then does one of two things:
- if it is a Letsencrypt challenge, deliver the secret.
- if it is an access over port 80, forward to the requested web page, but with HTTPS, so a redirect.
We now create the file /etc/apache2/sites-available/000-default.conf
or modify it if it already exists.
<VirtualHost *:80>
# Put this in 000-default.conf
Alias /.well-known/acme-challenge/ /var/www/letsencrypt/.well-known/acme-challenge/
<Directory "/var/www/letsencrypt/.well-known/acme-challenge/">
Options None
AllowOverride None
ForceType text/plain
RedirectMatch 404 "^(?!/\.well-known/acme-challenge/[\w-]{43}$)"
</Directory>
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge [NC]
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>
Please delete the part for port 80 from all other possibly configured virtual hosts, so that there are no more individual configurations that refer to something other than port 443. The whole thing also works for pure forwarding domains, which also have to be SSL-encrypted.
Now we switch on our configuration, check it for correctness and start or restart the Apache webserver if everything fits:
a2ensite 000-default.conf
apachectl configtest
systemctl restart apache2
Get the Certificates with acme.sh
Now everything is ready and we can retrieve the certificates for our respective domain for the first time, first as a test:
/etc/acme.sh/acme.sh --test --issue -d meine.domain.de -w /var/www/letsencrypt
and if it works:
/etc/acme.sh/acme.sh --issue -d meine.domain.de -w /var/www/letsencrypt
We now do this for every website we host on our web server.
Embedding the Certificates in the VHost Configurations
Now that we have the certificates, we can add or integrate them for our domains or VHosts as follows:
<VirtualHost *:443>
ServerName meine.domain.de:443
DocumentRoot /var/www/sites/support
<Directory /var/www/sites/support>
AllowOverride All
</Directory>
ErrorLog /dev/null
CustomLog /dev/null common
SSLEngine on
SSLCertificateFile /etc/acme.sh/certs/meine.domain.de/fullchain.cer
SSLCertificateKeyFile /etc/acme.sh/certs/meine.domain.de/meine.domain.de.key
</VirtualHost>
After that, don’t forget to get the Apache started:
systemctl restart apache2
Automation with cron
To make this all run automatically from now on, we now create an entry in cron by calling crontab -e
, here every night at 0:17 o’clock:
17 0 * * * /etc/acme.sh/acme.sh --cron --home "/etc/acme.sh" --config-home "/etc/acme.sh/config" > /dev/null
The --home
and --config-home
parameters may differ depending on your setup of acme.sh.
That’s it. Have fun with it!