Skip to content

Commit 1d95cb5

Browse files
authored
nixos/duckdns: init module (#294489)
2 parents f304923 + 9563f46 commit 1d95cb5

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

maintainers/maintainer-list.nix

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15915,6 +15915,12 @@
1591515915
githubId = 30374463;
1591615916
name = "Michal S.";
1591715917
};
15918+
notthebee = {
15919+
email = "[email protected]";
15920+
github = "notthebee";
15921+
githubId = 30384331;
15922+
name = "Wolfgang";
15923+
};
1591815924
notthemessiah = {
1591915925
email = "[email protected]";
1592015926
github = "NOTtheMessiah";
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
{
2+
config,
3+
pkgs,
4+
lib,
5+
...
6+
}:
7+
let
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+
'';
31+
in
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+
}

0 commit comments

Comments
 (0)