{ 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 = let stateDirectory = "dyndns"; homeDirectory = "/var/lib/${stateDirectory}"; in { 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; ReadWriteDirectories = [ homeDirectory ]; StateDirectory = stateDirectory; }; # preStart = '' # #!${pkgs.bash}/bin/bash # [ -d "${homeDirectory}" ] || mkdir -p "${homeDirectory}" # ''; 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}")" state_file="${homeDirectory}/current_ipv6" 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 if [ ! -f "$state_file" ] ; then echo "No state file found, determining currently set IP via DNS query." ${pkgs.dig}/bin/dig aaaa +short "$host" > "$state_file" fi current_ip=$(cat "$state_file") 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" echo "$new_ip" > "$state_file" ''; }; systemd.timers.dyndns = { description = "Timer for triggering DynDNS updates"; wantedBy = [ "timers.target" ]; timerConfig = { OnBootSec = "2min"; OnUnitActiveSec = "20min"; Unit = "dyndns.service"; }; }; }; }