Self-hosted manual DNS create/renew question

I have a mail server running postfix on Ubuntu 22.04 with certbot 2.11.0. The machine doesn't currently allow port 80 connections in or out (nor does it have a web server on it). I also run my own DNS (and can edit my zone files) using bind9.

I need to create (and preferably auto-renew) a cert for this (eg called mx.domain.com).

Reading this, I think I understand I need to use:

sudo certbot certonly --manual --preferred-challeges dns -d mx.domain.com

and when it says:

For example, for the domain example.com, a zone file entry would look like:

_acme-challenge.example.com. 300 IN TXT "gfj9Xq...Rg85nM"

I assume I enter:

_acme-challenge.mx.domain.com. 300 IN TXT "gfj9Xq...Rg85nM"

That is, use the name of the cert.

When it comes to renewal, I'm less clear. But it seems I need to set up a cron job that runs before expiry (monthly?) and uses --manual-auth-hook like this:

certbot certonly --manual --preferred-challenges=dns --manual-auth-hook /path/to/somescript.sh -d mx.domain.com

So in my case somescript.sh needs to grab what certbot gives it for the TXT record, then replaces the existing key string in the zone file.

I think I can do that, but how do I get certbot to give the script the key string to use?


Bonus tangential question while I'm here:

Regardless of the above, if I need to add a domain to an existing cert, I assume I use:

sudo certbot certonly --cert-name mx.domain.com --expand --domains mx.domain.com, smtp.domain.com

Is that right?

Messing with manual script may work for you, but is probably the hardest way to do what you want. The data you're looking for is passed as environment variables to your hook script; check out the hooks part of certbot's documentation.

If you can allow port 80 inbound even though the server doesn't have a web server, you just can just use --standalone to have certbot run a simple temporary web server of its own just for the purpose of replying to the challenge.

If you want to keep the challenge only over DNS, and your DNS is using bind, then check out the RFC 2136 plugin, which is a standard way you can configure bind to allow dynamic updates from certbot, so that certbot can create and delete the TXT records that it needs automatically.

6 Likes

Thanks, yes, the DNS method does seem a huge faff for just one certificate on a small server.

Looking at it in more detail, I think I should be able to add a rule to the inbound firewall to allow the certbot process owner (or does it just run as root when renewing?) on port 80. So if I use:

certbot certonly --standalone -d mx.domain.com

Then renewals will be automatic.

3 Likes

Certbot normally runs as root.

If there's nothing else listening on port 80, the standalone plugin should work.

If you want to disable/enable some things in your firewall before and after the renewal, you might want to take a look at the --pre-hook and --post-hook options.

3 Likes

Hm, just tried a dry-run using --pre-hook and --post-hook to run scripts that manage the firewall, but certbot doesn't seem to run them. The docs say "--pre-hook and --post-hook commands run by default."

certbot -v certonly --dry-run --standalone --pre-hook "/usr/local/bin/add-80.sh" --post-hook "/usr/local/bin/rm-80.sh" -d mx1.domain.com

I assume without the pre/post hooks I can't automatically renew?

EDIT: I think I'll just have a cron job that does this every couple of months, because it seems to work on a dry-run:

#!/bin/bash

# Insert port 80 rules (BTW they need to be both ways):
/usr/local/bin/add-80.sh

certbot -v certonly --standalone --force-renewal -d mx1.domain.com

# Remove them
/usr/local/bin/rm-80.sh

That's a really bad idea. You should be doing sudo certbot renew at least once / day if not twice. You should run that in a cronjob rather than re-issuing your certonly request. In fact, a cronjob or systemd timer may have been setup for this during the Certbot install. See User Guide — Certbot 2.12.0.dev0 documentation

Certs can be revoked by Let's Encrypt for mis-issuance and this happens every now and again. Certbot will check for this each renew so will renew right away. You want the renew running often.

Using --force-renewal frequently will create excessive certs and get you Rate Limited. This is a bad option to rely on.

Lastly, the --standalone server only listens on port 80 during a cert request. These usually only take a few seconds. So, it is listening on that port only a few seconds every 60 days or so. Couldn't you just leave port 80 open all the time knowing that nothing will reply except for this brief moment? And, even then it only replies to ACME challenge requests not anything else.

3 Likes

Ah OK. In that case, perhaps it's better to leave port 80 open all the time and let certbot do the renewals itself.

1 Like

:man_shrugging:t2: Works for me:

server ~ # certbot -v certonly --dry-run --apache --pre-hook "/usr/local/bin/add-80.sh" --post-hook "/usr/local/bin/rm-80.sh" -d mx1.example.com
/usr/local/bin/add-80.sh-hook command pre exists, but is not executable.
Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /tmp/certbot-log-1iir5jp6/log or re-run Certbot with -v for more details.
server ~ # chmod +x /usr/local/bin/*80.sh
server ~ # certbot -v certonly --dry-run --apache --pre-hook "/usr/local/bin/add-80.sh" --post-hook "/usr/local/bin/rm-80.sh" -d mx1.example.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Unable to read ssl_module file; not disabling session tickets.
Plugins selected: Authenticator apache (2.11.0.dev0), Installer apache (2.11.0.dev0)
Running pre-hook command: /usr/local/bin/add-80.sh
Simulating a certificate request for mx1.example.com
Performing the following challenges:
http-01 challenge for mx1.example.com
Waiting for verification...
Cleaning up challenges
Running post-hook command: /usr/local/bin/rm-80.sh
The dry run was successful.
server ~ # cat /usr/local/bin/*80.sh
echo "Pre: $(date)" >> /tmp/prepost.txt
echo "Post: $(date)" >> /tmp/prepost.txt
server ~ # cat /tmp/prepost.txt 
Pre: Sun 16 Jun 16:50:38 CEST 2024
Post: Sun 16 Jun 16:51:03 CEST 2024
server ~ # 

Might be the scripts themselve?

Also, I'd like to reiterate what Mike already said: do NOT use --force-renewal if you don't have a clue about what it does exactly or to what purpose it's meant to be used.

Also also, leaving port 80 open in the firewall while nothing is listening would not be a security issue IMO. But some people are sticklers for irrational things sometimes (e.g. managers/bosses et c.), that's why I suggested the hooks :slight_smile:

3 Likes

Oh BTW should I also open up port 80 on IPv6 as well as IPv4 or is that not necessary/helpful?

1 Like

Yes, if your DNS has an AAAA record Let's Encrypt will use that instead of the A record. It will fallback to IPv4 in a specific situation but best not to rely on that.

4 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.