From 121490461b1c4228bdb2e315e77c1e14a57dc653 Mon Sep 17 00:00:00 2001 From: lunik1 Date: Sun, 31 Mar 2024 23:33:19 +0100 Subject: [PATCH] nixos/inadyn: init --- .../manual/release-notes/rl-2405.section.md | 2 + nixos/modules/module-list.nix | 1 + nixos/modules/services/networking/inadyn.nix | 250 ++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 nixos/modules/services/networking/inadyn.nix diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md index 06d6ebbd2b0a..610a42f273f4 100644 --- a/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixos/doc/manual/release-notes/rl-2405.section.md @@ -122,6 +122,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - [microsocks](https://github.com/rofl0r/microsocks), a tiny, portable SOCKS5 server with very moderate resource usage. Available as [services.microsocks]($opt-services-microsocks.enable). +- [inadyn](https://github.com/troglobit/inadyn), a Dynamic DNS client with built-in support for multiple providers. Available as [services.inadyn](#opt-services.inadyn.enable). + - [Clevis](https://github.com/latchset/clevis), a pluggable framework for automated decryption, used to unlock encrypted devices in initrd. Available as [boot.initrd.clevis.enable](#opt-boot.initrd.clevis.enable). - [fritz-exporter](https://github.com/pdreker/fritz_exporter), a Prometheus exporter for extracting metrics from [FRITZ!](https://avm.de/produkte/) devices. Available as [services.prometheus.exporters.fritz](#opt-services.prometheus.exporters.fritz.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 0a15360f6ea5..b89cb5a44742 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -998,6 +998,7 @@ ./services/networking/icecream/daemon.nix ./services/networking/icecream/scheduler.nix ./services/networking/imaginary.nix + ./services/networking/inadyn.nix ./services/networking/inspircd.nix ./services/networking/iodine.nix ./services/networking/iperf3.nix diff --git a/nixos/modules/services/networking/inadyn.nix b/nixos/modules/services/networking/inadyn.nix new file mode 100644 index 000000000000..5af1ec9c91ef --- /dev/null +++ b/nixos/modules/services/networking/inadyn.nix @@ -0,0 +1,250 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.inadyn; + + # check if a value of an attrset is not null or an empty collection + nonEmptyValue = _: v: v != null && v != [ ] && v != { }; + + renderOption = k: v: + if builtins.elem k [ "provider" "custom" ] then + lib.concatStringsSep "\n" + (mapAttrsToList + (name: config: '' + ${k} ${name} { + ${lib.concatStringsSep "\n " (mapAttrsToList renderOption (filterAttrs nonEmptyValue config))} + }'') + v) + else if k == "include" then + "${k}(\"${v}\")" + else if k == "hostname" && builtins.isList v then + "${k} = { ${builtins.concatStringsSep ", " (map (s: "\"${s}\"") v)} }" + else if builtins.isBool v then + "${k} = ${boolToString v}" + else if builtins.isString v then + "${k} = \"${v}\"" + else + "${k} = ${toString v}"; + + configFile' = pkgs.writeText "inadyn.conf" + '' + # This file was generated by nix + # do not edit + + ${(lib.concatStringsSep "\n" (mapAttrsToList renderOption (filterAttrs nonEmptyValue cfg.settings)))} + ''; + + configFile = if (cfg.configFile != null) then cfg.configFile else configFile'; +in +{ + options.services.inadyn = with types; + let + providerOptions = + { + include = mkOption { + default = null; + description = mdDoc "File to include additional settings for this provider from."; + type = nullOr path; + }; + ssl = mkOption { + default = true; + description = mdDoc "Whether to use HTTPS for this DDNS provider."; + type = bool; + }; + username = mkOption { + default = null; + description = mdDoc "Username for this DDNS provider."; + type = nullOr str; + }; + password = mkOption { + default = null; + description = mdDoc '' + Password for this DDNS provider. + + WARNING: This will be world-readable in the nix store. + To store credentials securely, use the `include` or `configFile` options. + ''; + type = nullOr str; + }; + hostname = mkOption { + default = "*"; + example = "your.cool-domain.com"; + description = mdDoc "Hostname alias(es)."; + type = either str (listOf str); + }; + }; + in + { + enable = mkEnableOption (mdDoc '' + synchronise your machine's IP address with a dynamic DNS provider using inadyn + ''); + user = mkOption { + default = "inadyn"; + type = types.str; + description = lib.mdDoc '' + User account under which inadyn runs. + + ::: {.note} + If left as the default value this user will automatically be created + on system activation, otherwise you are responsible for + ensuring the user exists before the inadyn service starts. + ::: + ''; + }; + group = mkOption { + default = "inadyn"; + type = types.str; + description = lib.mdDoc '' + Group account under which inadyn runs. + + ::: {.note} + If left as the default value this user will automatically be created + on system activation, otherwise you are responsible for + ensuring the user exists before the inadyn service starts. + ::: + ''; + }; + interval = mkOption { + default = "*-*-* *:*:00"; + description = mdDoc '' + How often to check the current IP. + Uses the format described in {manpage}`systemd.time(7)`"; + ''; + type = str; + }; + logLevel = lib.mkOption { + type = lib.types.enum [ "none" "err" "warning" "info" "notice" "debug" ]; + default = "notice"; + description = lib.mdDoc "Set inadyn's log level."; + }; + settings = mkOption { + default = { }; + description = "See `inadyn.conf (5)`"; + type = submodule { + freeformType = attrs; + options = { + allow-ipv6 = mkOption { + default = config.networking.enableIPv6; + defaultText = "`config.networking.enableIPv6`"; + description = mdDoc "Whether to get IPv6 addresses from interfaces."; + type = bool; + }; + forced-update = mkOption { + default = 2592000; + description = mdDoc "Duration (in seconds) after which an update is forced."; + type = ints.positive; + }; + provider = mkOption { + default = { }; + description = mdDoc '' + Settings for DDNS providers built-in to inadyn. + + For a list of built-in providers, see `inadyn.conf (5)`. + ''; + type = attrsOf (submodule { + freeformType = attrs; + options = providerOptions; + }); + }; + custom = mkOption { + default = { }; + description = mdDoc '' + Settings for custom DNS providers. + ''; + type = attrsOf (submodule { + freeformType = attrs; + options = providerOptions // { + ddns-server = mkOption { + description = mdDoc "DDNS server name."; + type = str; + }; + ddns-path = mkOption { + description = mdDoc '' + DDNS server path. + + See `inadnyn.conf (5)` for a list for format specifiers that can be used. + ''; + example = "/update?user=%u&password=%p&domain=%h&myip=%i"; + type = str; + }; + }; + }); + }; + }; + }; + }; + configFile = mkOption { + default = null; + description = mdDoc '' + Configuration file for inadyn. + + Setting this will override all other configuration options. + + Passed to the inadyn service using LoadCredential. + ''; + type = nullOr path; + }; + }; + + config = lib.mkIf cfg.enable { + systemd = { + services.inadyn = { + description = "Update nameservers using inadyn"; + documentation = [ + "man:inadyn" + "man:inadyn.conf" + "file:${pkgs.inadyn}/share/doc/inadyn/README.md" + ]; + requires = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + startAt = cfg.interval; + serviceConfig = { + Type = "oneshot"; + ExecStart = ''${lib.getExe pkgs.inadyn} -f ${configFile} --cache-dir ''${CACHE_DIRECTORY}/inadyn -1 --foreground -l ${cfg.logLevel}''; + LoadCredential = "config:${configFile}"; + CacheDirectory = "inadyn"; + + User = cfg.user; + Group = cfg.group; + UMask = "0177"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK"; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectSystem = "strict"; + ProtectProc = "invisible"; + ProtectHome = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = "@system-service"; + CapabilityBoundingSet = ""; + }; + }; + + timers.inadyn.timerConfig.Persistent = true; + }; + + users.users.inadyn = mkIf (cfg.user == "inadyn") { + group = cfg.group; + isSystemUser = true; + }; + + users.groups = mkIf (cfg.group == "inadyn") { + inadyn = { }; + }; + }; +}