My situation
I have shopped tech-tales.blog
at World4You. The only challenge I face here is that World4You does not provide API access and hence doing a DNS verification for wildcard certificates does not work. Essentially, I would like to automatically generate a certificate for *.tech-tales.blog
, which I can use for my internal services.
Solution: Second domain!
For some time already, I shopped a secondary domain, this one at Hetzner. Let’s call that tech-tales-alternative.blog
.
Using the acme.sh
script, I can use this secondary domain to verify the first domain! This post is about the method I use to do that.
Explanation
If I ask Let’s Encrypt for a certificate for *.tech-tales.blog
and want to do the verification via DNS, it tells me to place a TXT DNS entry at _acme-challenge.tech-tales.blog
with a given contents. After I have done so, Let’s encrypt verifies that this entry is actually there as expected and if so, it issues a certificate for me.
Now what I will use is the following: Instead of placing the TXT record at _acme-challenge.tech-tales.blog
, I will place it to _acme-challenge.tech-tales-alternative.blog
. Further, I add a CNAME:
_acme-challenge.tech-tales.blog IN CNAME _acme-challenge.tech-tales-alternative.blog.
Now Let’s Encrypt understands that and follows the CNAME, so I can use this way to verify my domain.
The script
Assuming that acme.sh
is already installed, first go to the Hetzner DNS Console. There, find “API Tokens” and add a new one. Then, execute on the command line:
# Insert your token here
EXPORT HETZNER_TOKEN=abcdefghijklmnopqrstuvwxyz
acme.sh \
--issue \
--server letsencrypt_test \
-m admin@tech-tales.blog \
-d *.tech-tales.blog \
--dns dns_hetzner \
--challenge-alias tech-tales-alternative.blog
Note that I am using --server letsencrypt_test
here. As soon as I am sure this works, I will update that to --server letsencrypt
.
This will take some seconds (as it does a lot of waiting). For this use case, there are some important lines in the output of the command:
Adding TXT value: ... for domain: _acme-challenge.tech-tales-alternative.blog
- This shows that the DNS record is placed where it should be placed.Success for domain tech-tales.blog '_acme-challenge.tech-tales-alternative.blog
- Entering the DNS entry was successful.- Next, the CA (so, Let’s encrypt) verifies the domain. This could take some time and then hopefully returns
Sucess
The DNS record is also deleted again later, so in the end, nothing is left behind.
Profit
Now go to ~/.acme.sh/*.tech-tales.blog
and find a ca.cer
and a fullchain.cer
in there. These can now be deployed everywhere I need them.
Addendum: Automatic deployment via a Renew Hook
Another argument I learned about is the --renew-hook /path/to/some/script.sh
. This means that if a renewal was done successfully, then the script you pass in here gets executed, which helps in getting the certificate to the right places and restarting the services as needed.
Addendum: Switch from test to prod environment
Let’s Encrypt recommends using their testing environment (as seen in --server letsencrypt_test
) as long as we are not done. The TLS certificate that is returned is typically not trusted by most servers, but on the other hand, this server does not have a rate limit.
Switching to the production environment of Let’s Encrypt is kind of hard - we would have to remember the whole command we used for issuing the certificate. But there is an easier (and a bit hacky?) way to fix that:
- Go to
~/.acme.sh/*.tech-tales.blog
- In this directory, you will find a file
*.tech-tales.blog.conf
. Open that. - You here have one important line:
LE_API=https://acme-staging-v2.api.letsencrypt.org/directory
. Change this URL tohttps://acme-v2.api.letsencrypt.org/directory
. Leave the rest of the file unchanged.
In particular, note thatLE_OrderFinalize
,LE_LinkOrder
andLE_LinkCert
are not required to be changed - you can just leave them as they are. - Finally, do
acme.sh --renew --force *.tech-tales.blog
. Note the--force
keyword here.