diff --git a/config/hosts/clunk.nix b/config/hosts/clunk.nix index c455ca7..2e4fabc 100644 --- a/config/hosts/clunk.nix +++ b/config/hosts/clunk.nix @@ -86,6 +86,13 @@ in { timing = "weekly"; }; + auth.kdc = { + enable = true; + realm = "SELBY.CA"; + acl-file = "/var/heimdal/access.acl"; + bind-addresses = [ "10.0.0.1" "127.0.0.1" "::1" ]; + }; + secure-dns-proxy = { enable = true; listen-port = dns-proxy-port; diff --git a/config/profiles/common.nix b/config/profiles/common.nix index 19f4789..d09767f 100644 --- a/config/profiles/common.nix +++ b/config/profiles/common.nix @@ -38,6 +38,8 @@ in { }; }; + fail2ban.enable = true; + xserver = { layout = "us"; xkbVariant = "dvp"; diff --git a/lib/fudo-lib.nix b/lib/fudo-lib.nix index 3371561..2aa313d 100644 --- a/lib/fudo-lib.nix +++ b/lib/fudo-lib.nix @@ -3,5 +3,4 @@ { ip = import ./ip.nix { inherit lib; }; dns = import ./dns.nix { inherit lib; }; - system = import ./system.nix { inherit lib; }; } diff --git a/lib/fudo/chat.nix b/lib/fudo/chat.nix index ecc03ed..121d46b 100644 --- a/lib/fudo/chat.nix +++ b/lib/fudo/chat.nix @@ -1,8 +1,7 @@ { pkgs, lib, config, ... }: with lib; -let - cfg = config.fudo.chat; +let cfg = config.fudo.chat; in { options.fudo.chat = { @@ -33,7 +32,8 @@ in { smtp-password-file = mkOption { type = types.str; - description = "Path to a file containing the password to use while connecting to the SMTP server."; + description = + "Path to a file containing the password to use while connecting to the SMTP server."; }; state-directory = mkOption { @@ -97,10 +97,12 @@ in { }; EnableEmailInvitations = true; SqlSettings.DriverName = "postgres"; - SqlSettings.DataSource = - "postgres://${cfg.database.user}:${fileContents cfg.database.password-file}@${cfg.database.hostname}:5432/${cfg.database.name}"; + SqlSettings.DataSource = "postgres://${cfg.database.user}:${ + fileContents cfg.database.password-file + }@${cfg.database.hostname}:5432/${cfg.database.name}"; }; - mattermost-config-file = pkgs.writeText "mattermost-config.json" (builtins.toJSON modified-config); + mattermost-config-file = + pkgs.writeText "mattermost-config.json" (builtins.toJSON modified-config); mattermost-user = "mattermost"; mattermost-group = "mattermost"; @@ -113,11 +115,7 @@ in { }; }; - groups = { - ${mattermost-group} = { - members = [ mattermost-user ]; - }; - }; + groups = { ${mattermost-group} = { members = [ mattermost-user ]; }; }; }; system.activationScripts.mattermost = '' diff --git a/lib/fudo/garbage-collector.nix b/lib/fudo/garbage-collector.nix index 3eafdd6..dfa2488 100644 --- a/lib/fudo/garbage-collector.nix +++ b/lib/fudo/garbage-collector.nix @@ -1,8 +1,7 @@ { config, lib, pkgs, ... }: with lib; -let - cfg = config.fudo.garbage-collector; +let cfg = config.fudo.garbage-collector; in { @@ -12,7 +11,8 @@ in { timing = mkOption { type = types.str; default = "weekly"; - description = "Period (systemd format) at which to run garbage collector."; + description = + "Period (systemd format) at which to run garbage collector."; }; age = mkOption { @@ -23,27 +23,13 @@ in { }; config = mkIf cfg.enable { - systemd = { - timers.fudo-garbage-collector = { - enable = true; - description = "Collect NixOS garbage older than ${cfg.age}"; - partOf = [ "fudo-garbage-collector.service" ]; - wantedBy = [ "timers.target" ]; - timerConfig = { - OnCalendar = cfg.timing; - }; - }; - - services.fudo-garbage-collector = { - enable = true; - serviceConfig = { - Type = "oneshot"; - StandardOutput = "journal"; - }; - script = '' - ${pkgs.nix}/bin/nix-collect-garbage --delete-older-than ${cfg.age} - ''; - }; + fudo.system.services.fudo-garbage-collector = { + description = "Collect NixOS garbage older than ${cfg.age}."; + onCalendar = cfg.timing; + type = "oneshot"; + script = + "${pkgs.nix}/bin/nix-collect-garbage --delete-older-than ${cfg.age}"; + addressFamilies = [ ]; }; }; } diff --git a/lib/fudo/kdc.nix b/lib/fudo/kdc.nix index b8a4124..c1b59da 100644 --- a/lib/fudo/kdc.nix +++ b/lib/fudo/kdc.nix @@ -2,73 +2,90 @@ with lib; let - cfg = config.fudo.auth.kdc; - stringJoin = joiner: attrList: - if (length attrList) == 0 then - "" - else - foldr(lAttr: rAttr: "${lAttr}${joiner}${rAttr}") (last attrList) (init attrList); + initialize-db = realm: key-file: + pkgs.writeShellScript "initialize-heimdal-db.sh" '' + if [ ! -e ${key-file} ]; then + ${pkgs.heimdalFull}/bin/kadmin -l init --realm=${realm} --realm-max-ticket-life=1d --realm-max-renewable-life=2w ${realm} + fi + ''; in { - options.fudo.auth.kdc = { + options.fudo.auth.kdc = with types; { enable = mkEnableOption "Fudo KDC"; - database-path = mkOption { - type = types.str; - description = '' - The path at which to store the database files. - ''; - default = "/var/heimdal/heimdal"; - }; - realm = mkOption { - type = types.str; - description = '' - The realm for which we are the acting KDC. - ''; + type = str; + description = "The realm for which we are the acting KDC."; }; - mkey-file = mkOption { - type = types.str; - description = '' - The path to the master key file. - ''; + config-file = mkOption { + type = str; + description = "Path to configuartion file."; + default = "/etc/krb5.conf"; + }; + + master-key-file = mkOption { + type = str; + description = "The path to the master key file."; + default = "/var/heimdal/master.key"; }; acl-file = mkOption { - type = types.str; - description = '' - The path to the Access Control file. - ''; + type = str; + description = "The path to the Access Control file."; }; bind-addresses = mkOption { - type = with types; listOf str; - description = '' - A list of IP addresses on which to bind. - ''; - default = []; + type = listOf str; + description = "A list of IP addresses on which to bind."; + default = [ ]; + }; + + user = mkOption { + type = str; + description = "User as which to run Heimdal servers."; + default = "kerberos"; + }; + + group = mkOption { + type = str; + description = "Group as which to run Heimdal servers."; + default = "kerberos"; + }; + + state-directory = mkOption { + type = str; + description = "Path at which to store kerberos database."; + default = "/srv/kerberos"; }; }; config = mkIf cfg.enable { + users = { + users.${cfg.user} = { + isSystemUser = true; + home = "/var/heimdal"; + group = cfg.group; + }; + + groups.${cfg.group} = { members = [ cfg.user ]; }; + }; + environment = { - systemPackages = [ - pkgs.heimdalFull - ]; + systemPackages = [ pkgs.heimdalFull ]; etc."krb5.conf" = { text = mkAfter '' [kdc] database = { realm = ${cfg.realm} - mkey_file = ${cfg.mkey-file} + mkey_file = ${cfg.master-key-file} acl_file = ${cfg.acl-file} } - addresses = ${stringJoin " " cfg.bind-addresses} + addresses = ${concatStringsSep " " cfg.bind-addresses} # Binds to port 80! enable-http = false @@ -76,24 +93,44 @@ in { }; }; - systemd.services = { - heimdal-kdc = { - enable = true; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - description = "Heimdal Kerberos Key Distribution Center (ticket server)"; - serviceConfig = { - ExecStart = ''${pkgs.heimdalFull}/libexec/heimdal/kdc''; + systemd = { + tmpfiles.rules = [ "L /var/heimdal - - - - ${cfg.state-directory}" ]; + }; + + fudo.system = { + ensure-directories = { + "${cfg.state-directory}" = { + user = cfg.user; + group = cfg.group; }; }; - heimdal-admin-server = { - enable = true; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - description = "Heimdal Kerberos Remote Administration Server"; - serviceConfig = { - ExecStart = ''${pkgs.heimdalFull}/libexec/heimdal/kadmind''; + services = { + heimdal-kdc = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = + "Heimdal Kerberos Key Distribution Center (ticket server)."; + execStart = + "${pkgs.heimdalFull}/libexec/heimdal/kdc --config-file=${cfg.config-file}"; + privateNetwork = false; + user = cfg.user; + group = cfg.group; + workingDirectory = cfg.state-directory; + preStart = "${initialize-db cfg.realm cfg.master-key-file}"; + }; + + heimdal-admin-server = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "Heimdal Kerberos Remote Administration Server."; + execStart = + "${pkgs.heimdalFull}/libexec/heimdal/kadmind --config-file=${cfg.config-file} --key-file=${cfg.master-key-file}"; + privateNetwork = false; + user = cfg.user; + group = cfg.group; + workingDirectory = cfg.state-directory; + preStart = "${initialize-db cfg.realm cfg.master-key-file}"; }; }; }; diff --git a/lib/fudo/secure-dns-proxy.nix b/lib/fudo/secure-dns-proxy.nix index c433d56..9269ed3 100644 --- a/lib/fudo/secure-dns-proxy.nix +++ b/lib/fudo/secure-dns-proxy.nix @@ -51,10 +51,10 @@ in { config = mkIf cfg.enable { environment.systemPackages = with pkgs; [ dnsproxy ]; - systemd.services.secure-dns-proxy = fudo-lib.system.default-service { + fudo.system.services.secure-dns-proxy = { + description = "DNS Proxy for secure DNS-over-HTTPS lookups."; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; - description = "DNS Proxy for secure DNS-over-HTTPS lookups."; privateNetwork = false; requiredCapabilities = [ ]; restartWhen = "always"; diff --git a/lib/fudo/system.nix b/lib/fudo/system.nix index 5303a6c..0856e1a 100644 --- a/lib/fudo/system.nix +++ b/lib/fudo/system.nix @@ -3,41 +3,439 @@ with lib; let cfg = config.fudo.system; -in { - options.fudo.system = { - disableTransparentHugePages = mkOption { - type = types.bool; - description = '' - Disable transparent huge pages (recommended for database loads, in - particular for Redis. - ''; - default = false; - }; - postHugePageServices = mkOption { - type = with types; listOf str; - description = "List of systemd services that should wait until after THP are disabled."; - default = []; - example = ["redis.service"]; - }; - - tmpOnTmpfs = mkOption { + mkDisableOption = description: + mkOption { type = types.bool; - description = "Put tmp filesystem on tmpfs (needs enough RAM)."; default = true; + description = description; }; - }; - config = mkIf cfg.disableTransparentHugePages { - systemd.services.disableHugePages = { - description = "Turn off Transparent Huge Pages (https://www.kernel.org/doc/Documentation/vm/transhuge.txt)"; - after = [ "sysinit.target" "localfs-target" ]; - before = cfg.postHugePageServices; - enable = true; - serviceConfig = { - ExecStart = "/bin/sh -c 'echo never | tee /sys/kernel/mm/transparent_hugepage/enabled > /dev/null"; - Type = "oneshot"; + isEmpty = lst: 0 == (length lst); + + serviceOpts = { name, ... }: + with types; { + options = { + after = mkOption { + type = listOf str; + description = "List of services to start before this one."; + default = [ ]; + }; + script = mkOption { + type = nullOr str; + description = "Simple shell script for the service to run."; + default = null; + }; + reloadScript = mkOption { + type = nullOr str; + description = "Script to run whenever the service is restarted."; + default = null; + }; + before = mkOption { + type = listOf str; + description = + "List of services before which this service should be started."; + default = [ ]; + }; + requires = mkOption { + type = listOf str; + description = + "List of services on which this service depends. If they fail to start, this service won't start."; + default = [ ]; + }; + preStart = mkOption { + type = nullOr str; + description = "Script to run prior to starting this service."; + default = null; + }; + postStart = mkOption { + type = nullOr str; + description = "Script to run after starting this service."; + default = null; + }; + preStop = mkOption { + type = nullOr str; + description = "Script to run prior to stopping this service."; + default = null; + }; + postStop = mkOption { + type = nullOr str; + description = "Script to run after stopping this service."; + default = null; + }; + requiredBy = mkOption { + type = listOf str; + description = + "List of services which require this service, and should fail without it."; + default = [ ]; + }; + wantedBy = mkOption { + type = listOf str; + default = [ ]; + description = + "List of services before which this service should be started."; + }; + environment = mkOption { + type = attrsOf str; + description = "Environment variables supplied to this service."; + default = { }; + }; + environment-file = mkOption { + type = nullOr str; + description = + "File containing environment variables supplied to this service."; + default = null; + }; + description = mkOption { + type = str; + description = "Description of the service."; + }; + path = mkOption { + type = listOf package; + description = + "A list of packages which should be in the service PATH."; + default = [ ]; + }; + restartIfChanged = + mkDisableOption "Restart the service if the definition changes."; + dynamicUser = mkDisableOption "Create a new user for this service."; + privateNetwork = mkDisableOption "Only allow access to localhost."; + privateUsers = + mkDisableOption "Don't allow access to system user list."; + privateDevices = mkDisableOption + "Restrict access to system devices other than basics."; + privateTmp = mkDisableOption "Limit service to a private tmp dir."; + protectControlGroups = + mkDisableOption "Don't allow service to modify control groups."; + protectClock = + mkDisableOption "Don't allow service to modify system clock."; + restrictSuidSgid = + mkDisableOption "Don't allow service to suid or sgid binaries."; + protectKernelTunables = + mkDisableOption "Don't allow service to modify kernel tunables."; + privateMounts = + mkDisableOption "Don't allow service to access mounted devices."; + protectKernelModules = mkDisableOption + "Don't allow service to load or evict kernel modules."; + protectHome = mkDisableOption "Limit access to home directories."; + protectHostname = + mkDisableOption "Don't allow service to modify hostname."; + protectKernelLogs = + mkDisableOption "Don't allow access to kernel logs."; + lockPersonality = mkDisableOption "Lock service 'personality'."; + restrictRealtime = + mkDisableOption "Restrict service from using realtime functionality."; + restrictNamespaces = + mkDisableOption "Restrict service from using namespaces."; + memoryDenyWriteExecute = mkDisableOption + "Restrict process from executing from writable memory."; + keyringMode = mkOption { + type = str; + default = "private"; + description = "Sharing state of process keyring."; + }; + requiredCapabilities = mkOption { + type = listOf (enum capabilities); + default = [ ]; + description = "List of capabilities granted to the service."; + }; + restartWhen = mkOption { + type = str; + default = "on-failure"; + description = "Conditions under which process should be restarted."; + }; + restartSec = mkOption { + type = int; + default = 10; + description = "Number of seconds to wait before restarting service."; + }; + execStart = mkOption { + type = nullOr str; + default = null; + description = "Command to run to launch the service."; + }; + protectSystem = mkOption { + type = enum [ "true" "false" "full" "strict" ]; + default = "full"; + description = + "Level of protection to apply to the system for this service."; + }; + addressFamilies = mkOption { + type = listOf (enum address-families); + default = [ ]; + description = "List of address families which the service can use."; + }; + workingDirectory = mkOption { + type = nullOr path; + default = null; + description = "Directory in which to launch the service."; + }; + user = mkOption { + type = nullOr str; + default = null; + description = "User as which to launch this service."; + }; + group = mkOption { + type = nullOr str; + default = null; + description = "Primary group as which to launch this service."; + }; + type = mkOption { + type = + enum [ "simple" "exec" "forking" "oneshot" "dbus" "notify" "idle" ]; + default = "simple"; + description = "Systemd service type of this service."; + }; + partOf = mkOption { + type = listOf str; + default = [ ]; + description = + "List of targets to which this service belongs (and with which it should be restarted)."; + }; + standardOutput = mkOption { + type = str; + default = "journal"; + description = "Destination of standard output for this service."; + }; + standardError = mkOption { + type = str; + default = "journal"; + description = "Destination of standard error for this service."; + }; + pidFile = mkOption { + type = nullOr str; + default = null; + description = "Service PID file."; + }; + networkWhitelist = mkOption { + type = nullOr (listOf str); + default = null; + description = + "A list of networks with which this process may communicate."; + }; + allowedSyscalls = mkOption { + type = listOf (enum syscalls); + default = [ ]; + description = "System calls which the service is permitted to make."; + }; + maximumUmask = mkOption { + type = str; + default = "0077"; + description = "Umask to apply to files created by the service."; + }; + startOnlyPerms = mkDisableOption "Disable perms after startup."; + onCalendar = mkOption { + type = nullOr str; + description = + "Schedule on which the job should be invoked. See: man systemd.time(7)."; + default = null; + }; + }; + }; + + # See: man capabilities(7) + capabilities = [ + "CAP_AUDIT_CONTROL" + "CAP_AUDIT_READ" + "CAP_AUDIT_WRITE" + "CAP_BLOCK_SUSPEND" + "CAP_BPF" + "CAP_CHECKPOINT_RESTORE" + "CAP_CHOWN" + "CAP_DAC_OVERRIDE" + "CAP_DAC_READ_SEARCH" + "CAP_FOWNER" + "CAP_FSETID" + "CAP_IPC_LOCK" + "CAP_IPC_OWNER" + "CAP_KILL" + "CAP_LEASE" + "CAP_LINUX_IMMUTABLE" + "CAP_MAC_ADMIN" + "CAP_MAC_OVERRIDE" + "CAP_MKNOD" + "CAP_NET_ADMIN" + "CAP_NET_BIND_SERVICE" + "CAP_NET_BROADCAST" + "CAP_NET_RAW" + "CAP_PERFMON" + "CAP_SETGID" + "CAP_SETFCAP" + "CAP_SETPCAP" + "CAP_SETUID" + "CAP_SYS_ADMIN" + "CAP_SYS_BOOT" + "CAP_SYS_CHROOT" + "CAP_SYS_MODULE" + "CAP_SYS_NICE" + "CAP_SYS_PACCT" + "CAP_SYS_PTRACE" + "CAP_SYS_RAWIO" + "CAP_SYS_RESOURCE" + "CAP_SYS_TIME" + "CAP_SYS_TTY_CONFIG" + "CAP_SYSLOG" + "CAP_WAKE_ALARM" + ]; + + syscalls = [ + "@clock" + "@debug" + "@module" + "@mount" + "@raw-io" + "@reboot" + "@swap" + "@privileged" + "@resources" + "@cpu-emulation" + "@obsolete" + ]; + + address-families = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + + restrict-capabilities = allowed: + if (allowed == [ ]) then + "~${concatStringsSep " " capabilities}" + else + concatStringsSep " " allowed; + + restrict-syscalls = allowed: + if (allowed == [ ]) then + "~${concatStringsSep " " syscalls}" + else + concatStringsSep " " allowed; + + restrict-address-families = allowed: + if (allowed == [ ]) then + "~${concatStringsSep " " address-families}" + else + concatStringsSep " " allowed; + + dirOpts = { path, ... }: { + options = with types; { + user = mkOption { + type = str; + description = "User by whom the directory will be owned."; + default = "nobody"; + }; + group = mkOption { + type = str; + description = "Group by which the directory will be owned."; + default = "nogroup"; + }; + perms = mkOption { + type = str; + description = "Permission bits to apply to the directory."; + default = "0770"; }; }; }; + +in { + options.fudo.system = with types; { + services = mkOption { + type = attrsOf (submodule serviceOpts); + description = "Fudo system service definitions, with secure defaults."; + default = { }; + }; + + tmpOnTmpfs = mkOption { + type = bool; + description = "Put tmp filesystem on tmpfs (needs enough RAM)."; + default = true; + }; + + ensure-directories = mkOption { + type = attrsOf (submodule dirOpts); + description = "A map of required directories to directory properties."; + default = { }; + }; + }; + + config = { + # systemd.slices = mapAttrs (name: opts: { + # sliceConfig = { + # IpAddressAllow = opts.networkWhitelist; + # IpAddressDeny = "any"; + # }; + # }) (filterAttrs (name: opts: opts.networkWhitelist != null) cfg.services); + + systemd.timers = mapAttrs (name: opts: { + enable = true; + description = opts.description; + partOf = [ "${name}.timer" ]; + wantedBy = [ "timers.target" ]; + timerConfig = { OnCalendar = opts.onCalendar; }; + }) (filterAttrs (name: opts: opts.onCalendar != null) cfg.services); + + systemd.tmpfiles.rules = mapAttrsToList + (path: opts: "d ${path} ${opts.perms} ${opts.user} ${opts.group} - -") + cfg.ensure-directories; + + systemd.targets.fudo-init = { wantedBy = [ "multi-user.target" ]; }; + + systemd.services = mapAttrs (name: opts: { + enable = true; + script = mkIf (opts.script != null) opts.script; + reload = mkIf (opts.reloadScript != null) opts.reloadScript; + after = opts.after ++ [ "fudo-init.target" ]; + before = opts.before; + requires = opts.requires; + wantedBy = opts.wantedBy; + preStart = mkIf (opts.preStart != null) opts.preStart; + postStart = mkIf (opts.postStart != null) opts.postStart; + postStop = mkIf (opts.postStop != null) opts.postStop; + preStop = mkIf (opts.preStop != null) opts.preStop; + partOf = opts.partOf; + requiredBy = opts.requiredBy; + environment = opts.environment; + description = opts.description; + restartIfChanged = opts.restartIfChanged; + path = opts.path; + serviceConfig = { + PrivateNetwork = opts.privateNetwork; + PrivateUsers = opts.privateUsers; + PrivateDevices = opts.privateDevices; + PrivateTmp = opts.privateTmp; + PrivateMounts = opts.privateMounts; + ProtectControlGroups = opts.protectControlGroups; + ProtectKernelTunables = opts.protectKernelTunables; + ProtectKernelModules = opts.protectKernelModules; + ProtectSystem = opts.protectSystem; + ProtectHostname = opts.protectHostname; + ProtectHome = opts.protectHome; + ProtectClock = opts.protectClock; + ProtectKernelLogs = opts.protectKernelLogs; + KeyringMode = opts.keyringMode; + EnvironmentFile = opts.environment-file; + # This is more complicated than it looks... + CapabilityBoundingSet = restrict-capabilities opts.requiredCapabilities; + DynamicUser = opts.dynamicUser; + Restart = opts.restartWhen; + WorkingDirectory = + mkIf (opts.workingDirectory != null) opts.workingDirectory; + RestrictAddressFamilies = + restrict-address-families opts.addressFamilies; + RestrictNamespaces = opts.restrictNamespaces; + User = mkIf (opts.user != null) opts.user; + Group = mkIf (opts.group != null) opts.group; + Type = opts.type; + StandardOutput = opts.standardOutput; + PIDFile = mkIf (opts.pidFile != null) opts.pidFile; + LockPersonality = opts.lockPersonality; + RestrictRealtime = opts.restrictRealtime; + ExecStart = mkIf (opts.execStart != null) opts.execStart; + MemoryDenyWriteExecute = opts.memoryDenyWriteExecute; + SystemCallFilter = restrict-syscalls opts.allowedSyscalls; + UMask = opts.maximumUmask; + + IpAddressAllow = + mkIf (opts.networkWhitelist != null) opts.networkWhitelist; + IpAddressDeny = mkIf (opts.networkWhitelist != null) "any"; + LimitNOFILE = "49152"; + PermissionsStartOnly = opts.startOnlyPerms; + }; + }) config.fudo.system.services; + }; } diff --git a/lib/lib.nix b/lib/lib.nix deleted file mode 100644 index 1e2d6e8..0000000 --- a/lib/lib.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ lib, ... }: - -{ - ip = import ./lib/ip.nix { }; - dns = import ./lib/dns.nix { }; -} diff --git a/lib/system.nix b/lib/system.nix deleted file mode 100644 index b25c2d7..0000000 --- a/lib/system.nix +++ /dev/null @@ -1,125 +0,0 @@ -{ lib, ... }: - -with lib; -let - # See: man capabilities(7) - capabilities = [ - "CAP_AUDIT_CONTROL" - "CAP_AUDIT_READ" - "CAP_AUDIT_WRITE" - "CAP_BLOCK_SUSPEND" - "CAP_BPF" - "CAP_CHECKPOINT_RESTORE" - "CAP_CHOWN" - "CAP_DAC_OVERRIDE" - "CAP_DAC_READ_SEARCH" - "CAP_FOWNER" - "CAP_FSETID" - "CAP_IPC_LOCK" - "CAP_IPC_OWNER" - "CAP_KILL" - "CAP_LEASE" - "CAP_LINUX_IMMUTABLE" - "CAP_MAC_ADMIN" - "CAP_MAC_OVERRIDE" - "CAP_MKNOD" - "CAP_NET_ADMIN" - "CAP_NET_BIND_SERVICE" - "CAP_NET_BROADCAST" - "CAP_NET_RAW" - "CAP_PERFMON" - "CAP_SETGID" - "CAP_SETFCAP" - "CAP_SETPCAP" - "CAP_SETUID" - "CAP_SYS_ADMIN" - "CAP_SYS_BOOT" - "CAP_SYS_CHROOT" - "CAP_SYS_MODULE" - "CAP_SYS_NICE" - "CAP_SYS_PACCT" - "CAP_SYS_PTRACE" - "CAP_SYS_RAWIO" - "CAP_SYS_RESOURCE" - "CAP_SYS_TIME" - "CAP_SYS_TTY_CONFIG" - "CAP_SYSLOG" - "CAP_WAKE_ALARM" - ]; - - restrict-capabilities = allowed: - if (allowed == [ ]) then - "~${concatStringsSep " " capabilities}" - else - concatStringsSep " " allowed; - -in { - timed-service = { ... }: false; - - default-service = { after ? [ ], script ? null, reloadScript ? null - , before ? [ ], requires ? [ ], preStart ? null, postStop ? null - , preStop ? null, postStart ? null, requiredBy ? [ ], environment ? { } - , description, restartIfChanged ? true, confine ? false, path ? [ ] - , privateNetwork ? true, dynamicUser ? true, privateUsers ? true - , privateDevices ? true, privateTmp ? true, protectControlGroups ? true - , restrictSuidSgid ? true, protectKernelTunables ? true - , privateMounts ? true, protectKernelModules ? true, protectHome ? true - , protectHostname ? true, keyringMode ? "private" - , requiredCapabilities ? [ ], restartWhen ? "on-failure", restartSec ? "10" - , execStart ? null, protectSystem ? "full", addressFamilies ? null - , wantedBy ? [ ], workingDirectory ? null, user ? null, group ? null - , type ? "simple", partOf ? [ ], standardOutput ? "journal", pidFile ? null - , lockPersonality ? true, restrictRealtime ? true, networkWhitelist ? null - , memoryDenyWriteExecute ? true, ... }: { - enable = true; - script = mkIf (script != null) script; - reload = mkIf (reloadScript != null) reloadScript; - after = after; - before = before; - requires = requires; - wantedBy = wantedBy; - preStart = mkIf (preStart != null) preStart; - postStart = mkIf (postStart != null) postStart; - postStop = mkIf (postStop != null) postStop; - preStop = mkIf (preStop != null) preStop; - partOf = partOf; - requiredBy = requiredBy; - environment = environment; - description = description; - restartIfChanged = restartIfChanged; - confinement = mkIf confine { enable = true; }; - path = path; - serviceConfig = { - PrivateNetwork = privateNetwork; - PrivateUsers = privateUsers; - PrivateDevices = privateDevices; - PrivateTmp = privateTmp; - PrivateMounts = privateMounts; - ProtectControlGroups = protectControlGroups; - ProtectKernelTunables = protectKernelTunables; - ProtectKernelModules = protectKernelModules; - ProtectSystem = protectSystem; - ProtectHostname = protectHostname; - ProtectHome = protectHome; - KeyringMode = keyringMode; - # This is more complicated than it looks... - CapabilityBoundingSet = restrict-capabilities requiredCapabilities; - DynamicUser = dynamicUser; - Restart = restartWhen; - WorkingDirectory = mkIf (workingDirectory != null) workingDirectory; - RestrictAddressFamilies = - mkIf (addressFamilies != null) (concatStringsSep " " addressFamilies); - User = mkIf (user != null) user; - Group = mkIf (group != null) group; - Type = type; - StandardOutput = standardOutput; - PIDFile = mkIf (pidFile != null) pidFile; - LockPersonality = lockPersonality; - RestrictRealtime = restrictRealtime; - IpAddressAllow = mkIf (networkWhitelist != null) networkWhitelist; - IpAddressDeny = mkIf (networkWhitelist != null) "any"; - ExecStart = mkIf (execStart != null) execStart; - MemoryDenyWriteExecute = memoryDenyWriteExecute; - }; - }; -}