{ config, lib, pkgs, ... }: let cfg = config.services.tickwatch; forEachMonitor = f: lib.mapAttrs' (name: cfg: { name = "tickwatch-${name}"; value = f cfg; }) cfg.monitors; monitor.options = { enable = lib.mkOption { type = lib.types.bool; default = true; description = "Whether to enable this monitor."; }; monitor = lib.mkOption { type = lib.types.str; example = "ping"; description = "Type of this monitor."; }; target = lib.mkOption { type = lib.types.str; example = "6/example.net"; description = "Target of this monitor."; }; settings = lib.mkOption { type = with lib.types; attrsOf (oneOf [ str int bool ]); example = { timestamp = "none"; range = "0:100"; }; description = "Settings for this monitor."; }; logFile = lib.mkOption { type = lib.types.str; default = ""; description = '' Path to output log file for this monitor. Must be writable by this monitor's user. Leave empty to log to the service's journald. ''; }; user = lib.mkOption { type = lib.types.str; default = ""; description = '' User as which to run this monitor. Leave empty to use a restricted dynamically-allocated user. ''; }; group = lib.mkOption { type = lib.types.str; default = ""; description = '' Group as which to run this monitor. Leave empty to use a restricted dynamically-allocated group. ''; }; }; in { meta = { maintainers = with lib.maintainers; [ euxane ]; }; options.services.tickwatch = { package = lib.mkPackageOption pkgs "tickwatch" { }; monitors = lib.mkOption { type = with lib.types; attrsOf (submodule monitor); default = { }; description = "Configuration for tickwatch monitors."; }; }; config = lib.mkIf (cfg.monitors != { }) { systemd.services = forEachMonitor (monCfg: { wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; script = '' ${lib.getExe cfg.package} ${ lib.escapeShellArgs ( (lib.cli.toGNUCommandLine { optionValueSeparator = "="; } monCfg.settings) ++ [ monCfg.monitor monCfg.target ] ) } ${lib.optionalString (monCfg.logFile != "") ">> ${monCfg.logFile}"} ''; serviceConfig = { Restart = "on-failure"; RestartSec = 30; # for ping monitor CapabilityBoundingSet = [ "CAP_NET_RAW" ]; AmbientCapabilities = [ "CAP_NET_RAW" ]; PrivateNetwork = false; # service hardening PrivateTmp = true; PrivateDevices = true; ProtectKernelTunables = true; ProtectControlGroups = true; RestrictSUIDSGID = true; ProtectKernelModules = true; SystemCallArchitectures = "native"; PrivateMounts = true; LockPersonality = true; ProtectHostname = true; RestrictRealtime = true; ProtectSystem = "strict"; ProtectHome = true; NoNewPrivileges = true; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; MemoryDenyWriteExecute = true; PrivateUsers = false; SystemCallErrorNumber = "EPERM"; SystemCallFilter = [ "@system-service" ]; ProtectKernelLogs = true; DevicePolicy = "closed"; ProtectClock = true; ProtectProc = "noaccess"; ProcSubset = "pid"; RestrictNamespaces = true; RemoveIPC = true; UMask = "0077"; } // ( if (monCfg.user != "" || monCfg.group != "") then { User = monCfg.user; Group = monCfg.group; } else { DynamicUser = true; } ); }); }; }