In the process of moving to secure systemd services. kdc currently broken.

This commit is contained in:
Niten 2021-03-11 01:21:49 +00:00
parent 9c3d00c7d3
commit 8ba00ed209
10 changed files with 547 additions and 251 deletions

View File

@ -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;

View File

@ -38,6 +38,8 @@ in {
};
};
fail2ban.enable = true;
xserver = {
layout = "us";
xkbVariant = "dvp";

View File

@ -3,5 +3,4 @@
{
ip = import ./ip.nix { inherit lib; };
dns = import ./dns.nix { inherit lib; };
system = import ./system.nix { inherit lib; };
}

View File

@ -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 = ''

View File

@ -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 = [ ];
};
};
}

View File

@ -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}";
};
};
};

View File

@ -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";

View File

@ -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;
};
}

View File

@ -1,6 +0,0 @@
{ lib, ... }:
{
ip = import ./lib/ip.nix { };
dns = import ./lib/dns.nix { };
}

View File

@ -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;
};
};
}