diff --git a/hosts/dyndns.nix b/hosts/dyndns.nix index d98a60d..3c05999 100644 --- a/hosts/dyndns.nix +++ b/hosts/dyndns.nix @@ -1,9 +1,6 @@ { config, pkgs, lib, ... }: let - definedInPersonalDotNix = lib.mkDefault (throw "Configuration option missing from personal.nix"); - getipv6 = pkgs.writeText "getipv6.sh" '' - ${pkgs.nettools}/bin/ifconfig enp3s0 | sed -n -E 's/^\ *inet6 (2001(:[0-9a-f]+)+)\ .*$/\1/p' - ''; + interface = "enp3s0"; in { networking.tempAddresses = "disabled"; @@ -15,7 +12,7 @@ in slaac hwaddr noipv4ll - interface enp3s0 + interface ${interface} static ip_address=192.168.178.43/24 static routers=192.168.178.1 static domain_name_servers=192.168.178.1 8.8.8.8 @@ -24,19 +21,9 @@ in ''; }; - services.ddclient = { + services.dyndns = { enable = true; - verbose = true; - use = "cmd, cmd='${pkgs.bash}/bin/bash ${getipv6}'"; - domains = [ - ((lib.toLower config.networking.hostName) + ".gvfr.de") - ]; - ipv6 = true; - server = definedInPersonalDotNix; - username = definedInPersonalDotNix; - passwordFile = "/secrets/dyndns_password_${config.services.ddclient.username}.txt"; - extraConfig = '' - wildcard=no - ''; + interface = interface; + passwordFile = "/secrets/dyndns_password_${config.services.dyndns.username}.txt"; }; } diff --git a/options/default.nix b/options/default.nix index da40b85..82c8b45 100644 --- a/options/default.nix +++ b/options/default.nix @@ -6,5 +6,6 @@ ./auto-upgrade.nix ./status-email.nix ./btrfs-scrub.nix + ./dyndns.nix ]; } diff --git a/options/dyndns.nix b/options/dyndns.nix new file mode 100644 index 0000000..1c2151a --- /dev/null +++ b/options/dyndns.nix @@ -0,0 +1,95 @@ +{ pkgs, lib, config, ... }: +let + cfg = config.services.dyndns; +in +{ + options.services.dyndns = { + enable = lib.mkEnableOption "Update DNS AAAA records via dyndns"; + + interface = lib.mkOption { + type = lib.types.str; + description = "Identifier of the network interface to use"; + }; + + domain = lib.mkOption { + type = lib.types.str; + description = "Domain name to update"; + }; + + server = lib.mkOption { + type = lib.types.str; + description = "DynDNS server name"; + }; + + username = lib.mkOption { + type = lib.types.str; + description = "Username for DynDNS updates"; + }; + + passwordFile = lib.mkOption { + type = lib.types.str; + description = "File containing the DynDNS password"; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.dyndns = { + enable = true; + after = [ "network.target" ]; + unitConfig = { + Description = "Update AAAA records for ${cfg.domain} via DynDNS"; + }; + serviceConfig = { + DynamicUser = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectSystem = true; + ProtectHome = true; + NoNewPrivileges = true; + }; + script = '' + #!${pkgs.bash}/bin/bash + + set -eu + + host="${cfg.domain}" + interface="${cfg.interface}" + dyndns_server="${cfg.server}" + dyndns_user="${cfg.username}" + dyndns_password="$(cat "${cfg.passwordFile}")" + + new_ip=$(${pkgs.iproute}/bin/ip -6 a show scope global -temporary dev "$interface" | ${pkgs.gnused}/bin/sed -n -E 's/^\ *inet6\ (2001(:[0-9a-f]+)+).*$/\1/p' | head -1) + + if [ -z "$new_ip" ] ; then + echo "Could not determine IP address." + exit 1 + fi + + current_ip=$(${pkgs.dig}/bin/dig aaaa +short "$host") + + if [ -z "$current_ip" ] ; then + echo "Could not determine current AAAA record." + exit 1 + fi + + if [ "$current_ip" = "$new_ip" ] ; then + echo "Current AAAA record is already $current_ip, no update needed." + exit 0 + fi + + echo "Updating IP to $new_ip." + ${pkgs.curl}/bin/curl "https://$dyndns_user:$dyndns_password@$dyndns_server/?hostname=$host&myip=$new_ip" + ''; + }; + + systemd.timers.dyndns = { + description = "Timer for triggering DynDNS updates"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = "2min"; + OnUnitActiveSec = "20min"; + Unit = "dyndns.service"; + }; + }; + }; +}