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