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 to https://acme-v2.api.letsencrypt.org/directory. Leave the rest of the file unchanged.
    In particular, note that LE_OrderFinalize, LE_LinkOrder and LE_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.