2024-05-15 15:35:15 +00:00
|
|
|
{ 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 = "File to include additional settings for this provider from.";
|
|
|
|
type = nullOr path;
|
|
|
|
};
|
|
|
|
ssl = mkOption {
|
|
|
|
default = true;
|
|
|
|
description = "Whether to use HTTPS for this DDNS provider.";
|
|
|
|
type = bool;
|
|
|
|
};
|
|
|
|
username = mkOption {
|
|
|
|
default = null;
|
|
|
|
description = "Username for this DDNS provider.";
|
|
|
|
type = nullOr str;
|
|
|
|
};
|
|
|
|
password = mkOption {
|
|
|
|
default = null;
|
|
|
|
description = ''
|
|
|
|
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 = "Hostname alias(es).";
|
|
|
|
type = either str (listOf str);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
in
|
|
|
|
{
|
|
|
|
enable = mkEnableOption (''
|
|
|
|
synchronise your machine's IP address with a dynamic DNS provider using inadyn
|
|
|
|
'');
|
|
|
|
user = mkOption {
|
|
|
|
default = "inadyn";
|
|
|
|
type = types.str;
|
|
|
|
description = ''
|
|
|
|
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 = ''
|
|
|
|
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 = ''
|
|
|
|
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 = "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 = "Whether to get IPv6 addresses from interfaces.";
|
|
|
|
type = bool;
|
|
|
|
};
|
|
|
|
forced-update = mkOption {
|
|
|
|
default = 2592000;
|
|
|
|
description = "Duration (in seconds) after which an update is forced.";
|
|
|
|
type = ints.positive;
|
|
|
|
};
|
|
|
|
provider = mkOption {
|
|
|
|
default = { };
|
|
|
|
description = ''
|
|
|
|
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 = ''
|
|
|
|
Settings for custom DNS providers.
|
|
|
|
'';
|
|
|
|
type = attrsOf (submodule {
|
|
|
|
freeformType = attrs;
|
|
|
|
options = providerOptions // {
|
|
|
|
ddns-server = mkOption {
|
|
|
|
description = "DDNS server name.";
|
|
|
|
type = str;
|
|
|
|
};
|
|
|
|
ddns-path = mkOption {
|
|
|
|
description = ''
|
|
|
|
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 = ''
|
|
|
|
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";
|
2024-06-20 14:57:18 +00:00
|
|
|
ExecStart = ''${lib.getExe pkgs.inadyn} -f ${configFile} --cache-dir ''${CACHE_DIRECTORY} -1 --foreground -l ${cfg.logLevel}'';
|
2024-05-15 15:35:15 +00:00
|
|
|
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 = { };
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|