{ config, pkgs, ... }: { sane.services.trust-dns.enable = true; sane.services.trust-dns.listenAddrsIPv4 = [ # specify each address explicitly, instead of using "*". # this ensures responses are sent from the address at which the request was received. "192.168.0.5" "10.0.1.5" ]; sane.services.trust-dns.zones."uninsane.org".TTL = 900; # SOA record structure: # SOA MNAME RNAME (... rest) # MNAME = Master name server for this zone. this is where update requests should be sent. # RNAME = admin contact (encoded email address) # Serial = YYYYMMDDNN, where N is incremented every time this file changes, to trigger secondary NS to re-fetch it. # Refresh = how frequently secondary NS should query master # Retry = how long secondary NS should wait until re-querying master after a failure (must be < Refresh) # Expire = how long secondary NS should continue to reply to queries after master fails (> Refresh + Retry) sane.services.trust-dns.zones."uninsane.org".inet = { SOA."@" = ['' ns1.uninsane.org. admin-dns.uninsane.org. ( 2022121601 ; Serial 4h ; Refresh 30m ; Retry 7d ; Expire 5m) ; Negative response TTL '']; TXT."rev" = [ "2022121801" ]; # XXX NS records must also not be CNAME # it's best that we keep this identical, or a superset of, what org. lists as our NS. # so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here. # A."ns1" = [ "%NATIVE%" ]; A."ns2" = [ "185.157.162.178" ]; A."ns3" = [ "185.157.162.178" ]; A."ovpns" = [ "185.157.162.178" ]; NS."@" = [ "ns1.uninsane.org." "ns2.uninsane.org." "ns3.uninsane.org." ]; }; sane.services.trust-dns.zones."uninsane.org".include = [ "/var/lib/trust-dns/native.uninsane.org.zone" ]; systemd.services.ddns-trust-dns = { description = "update dynamic DNS entries for self-hosted trust-dns"; after = [ "network.target" ]; wantedBy = [ "trust-dns.service" ]; restartTriggers = [( builtins.toJSON config.sane.services.trust-dns )]; serviceConfig.Type = "oneshot"; script = let check-ip = "${pkgs.sane-scripts}/bin/sane-ip-check-router-wan"; sed = "${pkgs.gnused}/bin/sed"; zone-dir = "/var/lib/trust-dns"; zone-out = "${zone-dir}/native.uninsane.org.zone"; diff = "${pkgs.diffutils}/bin/diff"; systemctl = "${pkgs.systemd}/bin/systemctl"; zone-template = pkgs.writeText "native.uninsane.org.zone.in" '' @ A %NATIVE% ns1 A %NATIVE% native A %NATIVE% ''; in '' set -ex mkdir -p ${zone-dir} ip=$(${check-ip}) ${sed} s/%NATIVE%/$ip/ ${zone-template} > ${zone-out}.new # see if anything changed # TODO: instead of diffing, we could `dig` against the actual deployment. # - that could be more resilient to races. touch ${zone-out} # in case it didn't exist yet cp ${zone-out} ${zone-out}.old mv ${zone-out}.new ${zone-out} # if so, restart trust-dns if [ ${diff} -u ${zone-out}.old ${zone-out} ] then echo "zone unchanged. ip: $ip" else echo "zone changed." status=$(${systemctl} is-active trust-dns.service || true) echo $status if [ "$status" = "active" ] then echo "restarting trust-dns." ${systemctl} restart trust-dns.service fi fi ''; }; systemd.timers.ddns-trust-dns = { # wantedBy = [ "multi-user.target" ]; wantedBy = [ "trust-dns.service" ]; timerConfig = { OnStartupSec = "10min"; OnUnitActiveSec = "10min"; }; }; }