at 25.11-pre 4.0 kB view raw
1{ 2 config, 3 pkgs, 4 lib, 5 ... 6}: 7let 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') 15 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" 21 else 22 echo "Your IP was updated at $(date) to IPv4: $IPV4 & IPv6 to: $IPV6" 23 fi 24 elif [[ "$RESPONSE" = "OK" ]] && [[ "$IPCHANGE" = "NOCHANGE" ]]; then 25 echo "DuckDNS request at $(date) successful. IP(s) unchanged." 26 else 27 echo -e "Something went wrong, please check your settings\nThe response returned was:\n$DRESPONSE\n" 28 exit 1 29 fi 30 ''; 31in 32{ 33 options.services.duckdns = { 34 enable = lib.mkEnableOption "DuckDNS Dynamic DNS Client"; 35 tokenFile = lib.mkOption { 36 default = null; 37 type = lib.types.path; 38 description = '' 39 The path to a file containing the token 40 used to authenticate with DuckDNS. 41 ''; 42 }; 43 44 domains = lib.mkOption { 45 default = null; 46 type = lib.types.nullOr (lib.types.listOf lib.types.str); 47 example = [ "examplehost" ]; 48 description = '' 49 The domain(s) to update in DuckDNS 50 (without the .duckdns.org suffix) 51 ''; 52 }; 53 54 domainsFile = lib.mkOption { 55 default = null; 56 type = lib.types.nullOr lib.types.path; 57 example = lib.literalExpression '' 58 pkgs.writeText "duckdns-domains.txt" ''' 59 examplehost 60 examplehost2 61 examplehost3 62 ''' 63 ''; 64 description = '' 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) 69 ''; 70 }; 71 72 }; 73 74 config = lib.mkIf cfg.enable { 75 assertions = [ 76 { 77 assertion = cfg.domains != null || cfg.domainsFile != null; 78 message = "Either services.duckdns.domains or services.duckdns.domainsFile has to be defined"; 79 } 80 { 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"; 83 } 84 { 85 assertion = (cfg.tokenFile != null); 86 message = "services.duckdns.tokenFile has to be defined"; 87 } 88 ]; 89 90 environment.systemPackages = [ duckdns ]; 91 92 systemd.services.duckdns = { 93 description = "DuckDNS Dynamic DNS Client"; 94 after = [ "network.target" ]; 95 wantedBy = [ "multi-user.target" ]; 96 startAt = "*:0/5"; 97 path = [ 98 pkgs.gnused 99 pkgs.systemd 100 pkgs.curl 101 pkgs.gawk 102 duckdns 103 ]; 104 serviceConfig = { 105 Type = "simple"; 106 LoadCredential = [ 107 "DUCKDNS_TOKEN_FILE:${cfg.tokenFile}" 108 ] ++ lib.optionals (cfg.domainsFile != null) [ "DUCKDNS_DOMAINS_FILE:${cfg.domainsFile}" ]; 109 DynamicUser = true; 110 }; 111 script = '' 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}' 115 ''} 116 ${lib.optionalString (cfg.domainsFile != null) '' 117 export DUCKDNS_DOMAINS=$(systemd-creds cat DUCKDNS_DOMAINS_FILE | sed -z 's/\n/,/g') 118 ''} 119 exec ${lib.getExe duckdns} 120 ''; 121 }; 122 }; 123 124 meta.maintainers = with lib.maintainers; [ notthebee ]; 125}