Changelog
The preferred method of assigning an IP address and hostname, in order of preference:
There are expectations for how each name should be resolved:
Now consider the following scenarios:
This yields, in the case of electric
hostname and pikachu
domain name (ideally these two should be equivalent, by using assignment by DNS):
127.0.0.1 localhost 192.168.1.3 pikachu.pyuxiang.com pikachu electric # If no domain name is needed/assigned: # 127.0.1.1 electric
Some useful commands to test NS resolution behaviour: hostname -f
and nslookup
/ dig
.
One method to control how DNS flows is using Unbound. Alternatively, if using the stub listener, refer to this instead. Some conflicting information, but main takeaways:
[Match] Name=wg0 [Network] DNS=10.253.253.253 Domains=~pyuxiang.com
A quick rundown of the ACME process helps with context: ACME (Automated Certificate Management Environment) is fundamentally a protocol between clients and CA that automates certificates issuance, which used to require human intervention. Developed as part of Let's Encrypt (an ACME-supported CA), and formalised in RFC8555.
The ACMEv2 protocol is as follows:
The server in question is a CA (e.g. Let's Encrypt, ZeroSSL, or self-hosted ACME-supported step-ca
), while the ACME client is a script that ideally automates the steps above. Since the protocol is language-agnostic, many ACME clients exist: a few popular ones that commonly pop up include lego
(Go), certbot
(Python), acme.sh
(Bash) and dehydrated
(Bash). Just pick a favorite and go.
dehydrated
is a single Bash script (~3k lines) that accepts three pieces of configuration located in /etc/dehydrated
:
domains.txt
: A list of domain/aliases to generate certificates for.config
: Configuration for dehydrated.hook.sh
: Hook configuration.
Certficate generation/renewal is simply dehydrated -c
.
The ACME client can automate certificate/key management and communicate with the ACME server for challenge tokens. Additional functionality of (1) modifying DNS records, (2) uploading certificates to webserver, needs to be patched in using hooks/plugins. In the case of dehydrated
, the hooks are implemented as an external client Bash script, which receives a command verb and relevant arguments. The documentation provides a limited list of commands and an hook boilerplate example.
An example set of commands to write for, using ACMEv2 DNS-01:
If using joohoi's acme-dns service, there is no need to start a DNS server, nor clean challenges. If running webserver on the same machine, one can also reference the certificate symlink directly, at /etc/dehydrated/certs/$DOMAIN/fullchain.pem
, so the script effectively reduces to just "deploy_challenge":
#!/usr/bin/env bash # Register with: # curl -s -X POST https://auth.acme-dns.io/register # then create CNAME DNS record: '.acme-challenge.$DOMAIN CNAME $DNS_SUBDOMAIN.acme-dns.io' DNS_ENDPOINT="https://auth.acme-dns.io/update" DNS_SUBDOMAIN="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" DNS_USER="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" DNS_PASS="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" HANDLER="${1}" if [ "$HANDLER" = "deploy_challenge" ]; then TOKEN="${4}" curl -X POST $DNS_ENDPOINT -H "X-Api-User: $DNS_USER" -H "X-Api-Key: $DNS_PASS" \ --data "{\"subdomain\": \"$DNS_SUBDOMAIN\", \"txt\": \"$TOKEN\"}" >/dev/null elif [ "$HANDLER" = "deploy_cert" ]; then KEYFILE="${3}" CERTFILE="${4}" FULLCHAINFILE="${5}" scp {$KEYFILE,$CERTFILE,$FULLCHAINFILE} webserver:/var/www/certs/ fi
Most of the defaults generally work fine. The full config is available, though I only needed to populate these variables:
# dehydrated configuration BASEDIR=/etc/dehydrated HOOK=/etc/dehydrated/hook.sh DEHYDRATED_USER=xxxxxx DEHYDRATED_GROUP=xxxxxx # ACME server configuration # Use "letsencrypt-test" staging server to avoid rate limits during testing CA="letsencrypt" CHALLENGETYPE="dns-01" CONTACT_EMAIL=xxxxxx
For a single wildcard certificate, the domains file is straightfoward: sign for the base domain, with the wildcard as a SAN. The exact file I use is attached below:
pyuxiang.com *.pyuxiang.com
First register with ./dehydrated --register --accept-terms
, then put ./dehydrated -c
into a cron job and be done!
acme-dns is a simple DNS server that serves TXT records for the ACME challenges, and also supports basic authentication for updating said records over HTTP.
First sign up for the service:
user:~$ curl -sX POST https://auth.acme-dns.io/register { "subdomain": "9d2c7b32-4af4-482c-9e46-718acf50539e", "username": "3d97e467-dd67-41d4-871f-5590b3d03c05", "password": "cLzdpV031ieuZzAE7jVNnX08uMqV0OsyIbf6Cqfm", ... }
Push ACME challenge tokens:
user:~$ cat header.txt X-Api-User: 3d97e467-dd67-41d4-871f-5590b3d03c05 X-Api-Key: cLzdpV031ieuZzAE7jVNnX08uMqV0OsyIbf6Cqfm user:~$ cat body.txt { "subdomain": "9d2c7b32-4af4-482c-9e46-718acf50539e", "txt": "w3LreTPDfo3GbaoRmoneFgKbmpGdweWbrlpg04-1xY0" } user:~$ curl -sX POST -H @header.txt --data @body.txt https://auth.acme-dns.io/update { "txt": "w3LreTPDfo3GbaoRmoneFgKbmpGdweWbrlpg04-1xY0" }
Verify challenge tokens are updated. Note that up to two TXT challenges can be cached in the "auth.acme-dns.io" service (typically needed for simultaneous root domain and wildcard domain validation).
user:~$ dig _acme-challenge.pyuxiang.com TXT ... ;; QUESTION SECTION: ;_acme-challenge.pyuxiang.com. IN TXT ;; ANSWER SECTION: _acme-challenge.pyuxiang.com. 3430 IN CNAME 9d2c7b32-4af4-482c-9e46-718acf50539e.auth.acme-dns.io. 9d2c7b32-4af4-482c-9e46-718acf50539e.auth.acme-dns.io. 1 IN TXT "w3LreTPDfo3GbaoRmoneFgKbmpGdweWbrlpg04-1xY0" ...