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 perhaps more illustrative description how name lookup is performed (src, src2):
/etc/nsswitch.conf
specifies how hostname resolution should be performed, amongst others.hosts: files mdns4 dns
, which specifies the file /etc/hosts
should be looked up first, followed by mDNS (multicast DNS), then lastly the DNS nameservers in /etc/resolv.conf
./etc/resolv.conf
lists the nameservers to query for DNS.man 3 resolver
)./etc/systemd/resolved.conf
is part of the systemd suite of services.192.168.1.1#pyuxiang.com
with custom domain resolution Domains=~.pyuxiang.com
./etc/resolvconf.conf
, /etc/resolvconf/*
seems to be another mechanism using OpenResolv...?systemd-networkd
This section is not complete yet... (2024-11-05)
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
Note that the domain associated with the certificate is the first domain in each line of domains.txt
, i.e. certificate deployment requires matching to the correct domain.
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" ...