{ config, pkgs, ... }: { systemd.tmpfiles.rules = [ "d /var/secrets 0750 root wheel" ]; # Needs to be started manually, and the key added to nameservers. # Based on https://nixos.org/manual/nixos/stable/index.html#module-security-acme-config-dns systemd.services.sig0-keygen = { unitConfig = { ConditionPathExists = "!/var/secrets/dyndns"; }; serviceConfig = { Type = "oneshot"; }; scriptArgs = config.networking.fqdn; script = '' mkdir /var/secrets/dyndns cd /var/secrets/dyndns ${pkgs.bind}/bin/dnssec-keygen -a RSASHA512 -b 2048 -n HOST -T KEY $1. > basename ''; }; systemd.services.dyndns = { requires = [ "network-online.target" ]; after = [ "network-online.target" ]; unitConfig = { AssertPathExists = "/var/secrets/dyndns"; # Defer errors for ~45min, throttle e-mails to ~hourly. StartLimitIntervalSec = "1hr"; StartLimitBurst = "45"; OnFailure = "status-email@%n.service"; }; serviceConfig = { Type = "oneshot"; Restart = "on-failure"; RestartSec = "1min"; }; path = [ pkgs.dnsutils pkgs.gawk pkgs.iproute2 ]; scriptArgs = config.networking.fqdn; script = '' RR=''${1%%.*}.dynamic.''${1#*.} IP4=`dig +short @1.1.1.1 ch txt whoami.cloudflare | tr -d \"` if [ -z "$IP4" ]; then echo "Missing IP: $IP4" >&2 exit 100 fi # Follow some RFC 6724 default address guidance, excluding ULA. # It might be more robust to bind a public source socket (RFC 5014). IP6=`ip -6 address show scope global -deprecated | awk -F'[ /]+' '$2 == "inet6" && $3 !~ /^f[cd]/ { print $3; exit }'` OLDIP4=`dig +short @popfresh.mou.fo $RR A 2> /dev/null` OLDIP6=`dig +short @popfresh.mou.fo $RR AAAA 2> /dev/null` # [ "x$IP" = "x$OLDIP4" ] && exit 0 # no update if [ "x$IP4" = "x$OLDIP4" -a "x$IP6" = "x$OLDIP6" ]; then exit 0 fi nsupdate -v -k /var/secrets/dyndns/`< /var/secrets/dyndns/basename`.private <<. update delete $RR. A update add $RR. 300 A $IP4 update delete $RR. AAAA ''${IP6:+update add $RR. 300 AAAA $IP6} update delete $RR. TXT update add $RR. 300 TXT "v=spf1 include:_spf.mou.fo ~all" send . ''; }; systemd.timers.dyndns = { wantedBy = [ "multi-user.target" ]; timerConfig = { OnStartupSec = "10"; OnUnitActiveSec = "1min"; }; }; }