8 cfg = config.services.duckdns;
9 duckdns = pkgs.writeShellScriptBin "duckdns" ''
10 DRESPONSE=$(curl -sS --max-time 60 --no-progress-meter -k -K- <<< "url = \"https://www.duckdns.org/update?verbose=true&domains=$DUCKDNS_DOMAINS&token=$DUCKDNS_TOKEN&ip=\"")
11 IPV4=$(echo "$DRESPONSE" | awk 'NR==2')
12 IPV6=$(echo "$DRESPONSE" | awk 'NR==3')
13 RESPONSE=$(echo "$DRESPONSE" | awk 'NR==1')
14 IPCHANGE=$(echo "$DRESPONSE" | awk 'NR==4')
16 if [[ "$RESPONSE" = "OK" ]] && [[ "$IPCHANGE" = "UPDATED" ]]; then
17 if [[ "$IPV4" != "" ]] && [[ "$IPV6" == "" ]]; then
18 echo "Your IP was updated at $(date) to IPv4: $IPV4"
19 elif [[ "$IPV4" == "" ]] && [[ "$IPV6" != "" ]]; then
20 echo "Your IP was updated at $(date) to IPv6: $IPV6"
22 echo "Your IP was updated at $(date) to IPv4: $IPV4 & IPv6 to: $IPV6"
24 elif [[ "$RESPONSE" = "OK" ]] && [[ "$IPCHANGE" = "NOCHANGE" ]]; then
25 echo "DuckDNS request at $(date) successful. IP(s) unchanged."
27 echo -e "Something went wrong, please check your settings\nThe response returned was:\n$DRESPONSE\n"
33 options.services.duckdns = {
34 enable = lib.mkEnableOption "DuckDNS Dynamic DNS Client";
35 tokenFile = lib.mkOption {
37 type = lib.types.path;
39 The path to a file containing the token
40 used to authenticate with DuckDNS.
44 domains = lib.mkOption {
46 type = lib.types.nullOr (lib.types.listOf lib.types.str);
47 example = [ "examplehost" ];
49 The domain(s) to update in DuckDNS
50 (without the .duckdns.org suffix)
54 domainsFile = lib.mkOption {
56 type = lib.types.nullOr lib.types.path;
57 example = lib.literalExpression ''
58 pkgs.writeText "duckdns-domains.txt" '''
65 The path to a file containing a
66 newline-separated list of DuckDNS
67 domain(s) to be updated
68 (without the .duckdns.org suffix)
74 config = lib.mkIf cfg.enable {
77 assertion = cfg.domains != null || cfg.domainsFile != null;
78 message = "Either services.duckdns.domains or services.duckdns.domainsFile has to be defined";
81 assertion = !(cfg.domains != null && cfg.domainsFile != null);
82 message = "services.duckdns.domains and services.duckdns.domainsFile can't both be defined at the same time";
85 assertion = (cfg.tokenFile != null);
86 message = "services.duckdns.tokenFile has to be defined";
90 environment.systemPackages = [ duckdns ];
92 systemd.services.duckdns = {
93 description = "DuckDNS Dynamic DNS Client";
94 after = [ "network.target" ];
95 wantedBy = [ "multi-user.target" ];
107 "DUCKDNS_TOKEN_FILE:${cfg.tokenFile}"
108 ] ++ lib.optionals (cfg.domainsFile != null) [ "DUCKDNS_DOMAINS_FILE:${cfg.domainsFile}" ];
112 export DUCKDNS_TOKEN=$(systemd-creds cat DUCKDNS_TOKEN_FILE)
113 ${lib.optionalString (cfg.domains != null) ''
114 export DUCKDNS_DOMAINS='${lib.strings.concatStringsSep "," cfg.domains}'
116 ${lib.optionalString (cfg.domainsFile != null) ''
117 export DUCKDNS_DOMAINS=$(systemd-creds cat DUCKDNS_DOMAINS_FILE | sed -z 's/\n/,/g')
119 exec ${lib.getExe duckdns}
124 meta.maintainers = with lib.maintainers; [ notthebee ];