at 25.11-pre 8.2 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 cfg = config.services.inadyn; 9 10 # check if a value of an attrset is not null or an empty collection 11 nonEmptyValue = _: v: v != null && v != [ ] && v != { }; 12 13 renderOption = 14 k: v: 15 if 16 builtins.elem k [ 17 "provider" 18 "custom" 19 ] 20 then 21 lib.concatStringsSep "\n" ( 22 lib.mapAttrsToList (name: config: '' 23 ${k} ${name} { 24 ${lib.concatStringsSep "\n " ( 25 lib.mapAttrsToList renderOption (lib.filterAttrs nonEmptyValue config) 26 )} 27 }'') v 28 ) 29 else if k == "include" then 30 "${k}(\"${v}\")" 31 else if k == "hostname" && builtins.isList v then 32 "${k} = { ${builtins.concatStringsSep ", " (map (s: "\"${s}\"") v)} }" 33 else if builtins.isBool v then 34 "${k} = ${lib.boolToString v}" 35 else if builtins.isString v then 36 "${k} = \"${v}\"" 37 else 38 "${k} = ${toString v}"; 39 40 configFile' = pkgs.writeText "inadyn.conf" '' 41 # This file was generated by nix 42 # do not edit 43 44 ${ 45 (lib.concatStringsSep "\n" ( 46 lib.mapAttrsToList renderOption (lib.filterAttrs nonEmptyValue cfg.settings) 47 )) 48 } 49 ''; 50 51 configFile = if (cfg.configFile != null) then cfg.configFile else configFile'; 52in 53{ 54 options.services.inadyn = 55 with lib.types; 56 let 57 providerOptions = { 58 include = lib.mkOption { 59 default = null; 60 description = "File to include additional settings for this provider from."; 61 type = nullOr path; 62 }; 63 ssl = lib.mkOption { 64 default = true; 65 description = "Whether to use HTTPS for this DDNS provider."; 66 type = bool; 67 }; 68 username = lib.mkOption { 69 default = null; 70 description = "Username for this DDNS provider."; 71 type = nullOr str; 72 }; 73 password = lib.mkOption { 74 default = null; 75 description = '' 76 Password for this DDNS provider. 77 78 WARNING: This will be world-readable in the nix store. 79 To store credentials securely, use the `include` or `configFile` options. 80 ''; 81 type = nullOr str; 82 }; 83 hostname = lib.mkOption { 84 default = "*"; 85 example = "your.cool-domain.com"; 86 description = "Hostname alias(es)."; 87 type = either str (listOf str); 88 }; 89 }; 90 in 91 { 92 enable = lib.mkEnableOption ('' 93 synchronise your machine's IP address with a dynamic DNS provider using inadyn 94 ''); 95 user = lib.mkOption { 96 default = "inadyn"; 97 type = lib.types.str; 98 description = '' 99 User account under which inadyn runs. 100 101 ::: {.note} 102 If left as the default value this user will automatically be created 103 on system activation, otherwise you are responsible for 104 ensuring the user exists before the inadyn service starts. 105 ::: 106 ''; 107 }; 108 group = lib.mkOption { 109 default = "inadyn"; 110 type = lib.types.str; 111 description = '' 112 Group account under which inadyn runs. 113 114 ::: {.note} 115 If left as the default value this user will automatically be created 116 on system activation, otherwise you are responsible for 117 ensuring the user exists before the inadyn service starts. 118 ::: 119 ''; 120 }; 121 interval = lib.mkOption { 122 default = "*-*-* *:*:00"; 123 description = '' 124 How often to check the current IP. 125 Uses the format described in {manpage}`systemd.time(7)`"; 126 ''; 127 type = str; 128 }; 129 logLevel = lib.mkOption { 130 type = lib.types.enum [ 131 "none" 132 "err" 133 "warning" 134 "info" 135 "notice" 136 "debug" 137 ]; 138 default = "notice"; 139 description = "Set inadyn's log level."; 140 }; 141 settings = lib.mkOption { 142 default = { }; 143 description = "See `inadyn.conf (5)`"; 144 type = submodule { 145 freeformType = attrs; 146 options = { 147 allow-ipv6 = lib.mkOption { 148 default = config.networking.enableIPv6; 149 defaultText = lib.literalExpression "config.networking.enableIPv6"; 150 description = "Whether to get IPv6 addresses from interfaces."; 151 type = bool; 152 }; 153 forced-update = lib.mkOption { 154 default = 2592000; 155 description = "Duration (in seconds) after which an update is forced."; 156 type = ints.positive; 157 }; 158 provider = lib.mkOption { 159 default = { }; 160 description = '' 161 Settings for DDNS providers built-in to inadyn. 162 163 For a list of built-in providers, see `inadyn.conf (5)`. 164 ''; 165 type = attrsOf (submodule { 166 freeformType = attrs; 167 options = providerOptions; 168 }); 169 }; 170 custom = lib.mkOption { 171 default = { }; 172 description = '' 173 Settings for custom DNS providers. 174 ''; 175 type = attrsOf (submodule { 176 freeformType = attrs; 177 options = providerOptions // { 178 ddns-server = lib.mkOption { 179 description = "DDNS server name."; 180 type = str; 181 }; 182 ddns-path = lib.mkOption { 183 description = '' 184 DDNS server path. 185 186 See `inadnyn.conf (5)` for a list for format specifiers that can be used. 187 ''; 188 example = "/update?user=%u&password=%p&domain=%h&myip=%i"; 189 type = str; 190 }; 191 }; 192 }); 193 }; 194 }; 195 }; 196 }; 197 configFile = lib.mkOption { 198 default = null; 199 description = '' 200 Configuration file for inadyn. 201 202 Setting this will override all other configuration options. 203 204 Passed to the inadyn service using LoadCredential. 205 ''; 206 type = nullOr path; 207 }; 208 }; 209 210 config = lib.mkIf cfg.enable { 211 systemd = { 212 services.inadyn = { 213 description = "Update nameservers using inadyn"; 214 documentation = [ 215 "man:inadyn" 216 "man:inadyn.conf" 217 "file:${pkgs.inadyn}/share/doc/inadyn/README.md" 218 ]; 219 requires = [ "network-online.target" ]; 220 wantedBy = [ "multi-user.target" ]; 221 startAt = cfg.interval; 222 serviceConfig = { 223 Type = "oneshot"; 224 ExecStart = ''${lib.getExe pkgs.inadyn} -f ${configFile} --cache-dir ''${CACHE_DIRECTORY} -1 --foreground -l ${cfg.logLevel}''; 225 LoadCredential = "config:${configFile}"; 226 CacheDirectory = "inadyn"; 227 228 User = cfg.user; 229 Group = cfg.group; 230 UMask = "0177"; 231 LockPersonality = true; 232 MemoryDenyWriteExecute = true; 233 RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK"; 234 NoNewPrivileges = true; 235 PrivateDevices = true; 236 PrivateTmp = true; 237 PrivateUsers = true; 238 ProtectSystem = "strict"; 239 ProtectProc = "invisible"; 240 ProtectHome = true; 241 ProtectClock = true; 242 ProtectControlGroups = true; 243 ProtectHostname = true; 244 ProtectKernelLogs = true; 245 ProtectKernelModules = true; 246 ProtectKernelTunables = true; 247 RestrictNamespaces = true; 248 RestrictRealtime = true; 249 RestrictSUIDSGID = true; 250 SystemCallArchitectures = "native"; 251 SystemCallErrorNumber = "EPERM"; 252 SystemCallFilter = "@system-service"; 253 CapabilityBoundingSet = ""; 254 }; 255 }; 256 257 timers.inadyn.timerConfig.Persistent = true; 258 }; 259 260 users.users.inadyn = lib.mkIf (cfg.user == "inadyn") { 261 group = cfg.group; 262 isSystemUser = true; 263 }; 264 265 users.groups = lib.mkIf (cfg.group == "inadyn") { 266 inadyn = { }; 267 }; 268 }; 269}