-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Module for handling dns-01 challenges by Dynamic DNS updates #90
base: master
Are you sure you want to change the base?
Conversation
NAME Crypt::LE::Challenge::DDNS - use dynamic DNS for ACME challenges SYNOPSIS use Crypt::LE; use Crypt::LE::Challenge::DDNS; ... my $le = Crypt::LE->new(); my $ddns_challenge = Crypt::LE::Challenge::DDNS->new(...); .. $le->accept_challenge($ddns_challenge, ...); $le->verify_challenge($ddns_challenge, ...); # Shell command line: $ le.pl ... --handle-as dns --handle-with Crypt::LE::Challenge::DDNS \ --handle-params '{"server": "127.0.0.1", "keyfile": "/var/named/keys/_le.example.org.key", "zone": "_le.example.org"}' DESCRIPTION This module uses Dynamic DNS (DDNS) updates for storing the ACME challenges for DNS-01 validation. Recommended mode of operation is to set up a Dynamic DNS subdomain solely for ACME challenges (for example, "_le.example.org"), and for domain names which would use Let's Encrypt certificates map their "_acme-challenge.$fqdn" into this domain. For example, to get a certificate for "myhost.example.org", create the following static DNS record in the "example.org" zone: _acme-challenge.myhost.example.org. IN CNAME myhost.example.org._le.example.org. This module will then ask the ACME server (Let's Encrypt CA) for a DNS-based challenge, and will store it using DDNS update to "myhost.example.org._le.example.org." TXT record. LE will then try to verify the challenge at "_acme-challenge.myhost.example.org" and will find it after being redirected by the above CNAME record. Note that ""example.org"" string is used both in the DDNS domain name, and inside that name. This is intentional - this way one common DDNS domain "_le.example.org" can serve for ACME challenges for multiple real DNS domains. If you want the renaming to be done in a different way, feel free to override the rr_from_fqdn() function in this module. The module accepts the following parameters (usable in "--handle-params" from the "le.pl" command line): server IP address of the DDNS server, where challenges will be stored. keyfile Authentication key for DDNS. This will be used for signing the DDNS update requests. Any key file format supported by "Net::DNS::RR::TSIG" will do. zone DDNS zone to which challenges will be written ("_le.example.org" in the above examples). If "zone" is a suffix of the host name for which the certificate is being created (e.g. "example.org"), then the challenge will be stored directly to _acme-challenge.$that_host_name instead of mapping to a different zone as described above. DDNS ZONE SETUP A quick and dirty tutorial how to create a Dynamic DNS zone and key in BIND. Firstly, create directories and the key file: BIND_DIR=/var/named DDNS_DOMAIN=_le.example.org install -d -u named -g named -m 775 $BIND_DIR/dynamic install -d -u root -g named -m 755 $BIND_DIR/keys tsig-keygen $DDNS_DOMAIN > $BIND_DIR/keys/$DDNS_DOMAIN.key chown root:named $BIND_DIR/keys/$DDNS_DOMAIN.key chmod 640 $BIND_DIR/keys/$DDNS_DOMAIN.key Create a zone file: cat > $BIND_DIR/dynamic/$DDNS_DOMAIN <<'EOF' $TTL 300 IN SOA ns.example.org. root.example.org. ( 1 ; serial 1H ; refresh 3H ; retry 2W ; expire 1 ; negative ttl ) IN NS ns.example.org. EOF Use the key and zone file in your named.conf: include "keys/_le.example.org.key"; zone "_le.example.org" { type master; file "dynamic/_le.example.org"; allow-query { any; }; allow-update { !{ !127.0.0.1; any; }; key _le.example.org; }; journal "dynamic/_le.example.org.jnl"; } Reload named and verify that it works: rndc reload nsupdate -k $BIND_DIR/keys/$DDNS_DOMAIN.key > server 127.0.0.1 > add test._le.example.org. 300 TXT "my test record" > send host -t any test._le.example.org. 127.0.0.1 SEE ALSO <https://letsencrypt.org/docs/challenge-types/>, Crypt::LE, Net::DNS::RR::TSIG, Crypt::LE::Challenge::Simple, nsupdate(1), tsig-keygen(1) AUTHOR Jan "Yenya" Kasprzak "<kas you_know_what yenya.net>". Based on "Crypt::LE::Challenge::Simple" by Alexander Yezhov.
For wildcard certificates (*.$fqdn), ACME queries _acme-challenge.$fqdn, without the "*." prefix, obviously. This has to be taken into account when remapping the wildcard name to a different DDNS subdomain. Moreover, when getting certificate for both $fqdn and *.$fqdn, Crypt::LE calls ->handle_challenge_dns() twice for the same $fqdn, and ACME expects _two_ TXT records. So we should not blindly delete the TXT record at the beginning of ->handle_challenge_dns(). My approach is to count the number of times we've got called for each $fqdn, store it in $self->{fqdn_seen}->{$fqdn}, and delete the TXT only in the first handle_challenge_dns() call, and in the last handle_verification_dns() call.
Suggested documentation improvement: Change one of the two example domains to "example.com", this makes it easier to see which domain names are supposed to be the same, and gets rid of the awkward "used both in the DDNS domain name, |
@jb-wisemo that might be a good idea as a second ticket |
Hello, Alexander,
I have written a module for storing dns-01 ACME challenges in the Dynamic DNS domain. Do you think it is suitable for inclusion in the Crypt::LE distribution?
Thanks,
-Yenya