Lego is a Let’s Encrypt client written in Go, hence the name. It serves as an alternative to Let’s Encrypt’s own client certbot
which can be found in many Linux distribution repositories to install. It is relatively simple to handle and manages SSL certificates just fine, as long as the domain is publically reachable, meaning you can reach it from anywhere by just putting it in your browser. So using certbot
is usually fine and works for the occasion.
But what about domains that are not public? Like for your own local development? Usually, these domains look like wordpress.local
for example and you won’t get SSL certificates for them. That is because they only exist in your local network and certbot
can’t reach them for its DNS challenge. In other words, certbot
can’t verify them and cannot create an SSL certificate for them.
Sure, you can try to trick certbot
a bit by using a subdomain of your live domain for example and use certbot
with the --certonly
option. That works somehow, but the SSL certificate generated this way can’t be renewed automatically and you have to do this dance every 3 months. Pretty annoying, trust me, been there, done that.
Now wouldn’t it be nice if there is a way to get proper SSL certificates for your local domains as well and renew them automatically? Yes, it would, and there is! Lego to the rescue!
First off, some assumptions and preparations. This still will not work with a .local
domain. You need a valid live domain, in my case ppfeufer.de
. And this domain needs to be with a provider that is in Logo’s list of DNS Providers. Because what Lego does is, query your DNS provider and obtain the certificate for you.
So, let’s play this step by step on an example in my case. My local WordPress development is wp-local.ppfeufer.de
and I like to have an SSL certificate for it. Why? Because I’d like to test if everything works with SSL, simple as that. Also, it’s 2022, no reason not to have SSL certificates, even for local domains :-P
Another assumption we have to make is that you are working on a Linux system. I have no idea how and even if this all will work on Windows at all. Sorry.
Install Lego
First, you need to install Lego. This can be done by simply cloning their GitHub repository and installing it from there.
Make sure you have Golang (>=1.17) installed as well, it’s needed, we’re installing a piece of software written in Go after all.
Clone the GitHub repository into a directory of your choice with:
git clone git@github.com:go-acme/lego.git
Now let’s change into the repository and compile the Lego client.
cd lego
export GO111MODULE=on
make build
This will take a while. This command pulls all the needed Go modules and compiled the Lego client, which can be found in the dist
directory when done.
Now that the Lego client has been compiled, you’ll find it in the dist
directory inside the git repository. Go ahead and make the file executable if it isn’t already (Seems to be depending on the distribution)
chmod +x lego
Making It Available on the System
After successfully compiling the Lego client, it needs to be made available to the command prompt. This means we have to move it somewhere where it can be executed by simply typing lego
in our console. I for once have it in /usr/bin/
, just a personal preference.
cd /usr/bin/
ln -s /path/to/the/legorepository/dist/lego .
As you can see, I’m not moving the file, I just link it. This has the advantage that when I update the Lego client, I don’t have to copy or move the file again. It’s linked and with that, it will always be the up-to-date version.
Usage
Now that the preparations are done, time to use the Lego client.
Pick your DNS provider from this list right here, the one I use in this example is Hetzner and as you can see, there is already an example given on how to obtain the SSL certificate.
Example:
cd /home/your_username
HETZNER_API_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
lego --email your@email.com --dns hetzner --domains your.domain.com run
Of course, you need an API key from Hetzner first, which you can create in your DNS console on Hetzner’s website. Make sure to never share this key with anyone!
Now you’ve got an SSL certificate which you can configure your web server with.
All you need to do is add an entry to your /etc/hosts
file, to make sure your local machine doesn’t bother your ISP’s DNS server for the domain name and resolves it locally.
My example:
127.0.0.1 your.domain.com
From now on, every time your local machine queries this domain it resolves to its local address where your web server is listening.
Renew the Certificate
Let’s Encrypt SSL certificates are valid for 3 months, so it needs to be renewed regularly. For this it’s best to make use of a cron task.
Add the following to your crontab:
##
# Certbot for renewing SSL certificates
##
0 */6 * * * /home/your_username/.lego/renew-scripts/01_renew_certificates
As you can see, we make use of a shell script here. Change the file name to whatever you think is best.
The shell script we call with the cron task looks like this:
#!/usr/bin/env bash
# Load the Hetzner API key
export HETZNER_API_KEY_FILE=/home/your_username/.lego/hetzer-api-key
LEGO_ACCOUNT_EMAIL=your@email.com
LEGO_PATH="/home/your_username/.lego"
LEGO_CERTIFICATES_PATH="${LEGO_PATH}/certificates"
LEGO_RENEW_DAYS=10
LEGO_DNS_PROVIDER=hetzner
CERTIFICATE_RENEWED=False
for LEGO_CERTIFICATE_NAME in `/usr/bin/lego --path ${LEGO_PATH} list --names`
do
original=$(date -r "${LEGO_CERTIFICATES_PATH}/${LEGO_CERTIFICATE_NAME}.crt")
/usr/bin/lego \
--path ${LEGO_PATH} \
--email ${LEGO_ACCOUNT_EMAIL} \
--accept-tos \
--dns ${LEGO_DNS_PROVIDER} \
--domains ${LEGO_CERTIFICATE_NAME} \
renew --days ${LEGO_RENEW_DAYS}
actual=$(date -r "${LEGO_CERTIFICATES_PATH}/${LEGO_CERTIFICATE_NAME}.crt")
if [ "${original}" != "${actual}" ]
then
CERTIFICATE_RENEWED=True
fi
done
if [ ${CERTIFICATE_RENEWED} == True ]
then
systemctl restart apache2.service
fi
exit $?
The script needs to be executable, so run:
chmod +x /home/your_username/.lego/renew-scripts/01_renew_certificates
Update the Lego Client
Updating the Lego client is pretty straightforward. All you need to do is pull the latest from GitHub and compile it again.
cd /path/to/the/legorepository/
git pull
export GO111MODULE=on
make build
That’s it.