In the process of moving to secure systemd services. kdc currently broken.
This commit is contained in:
parent
9c3d00c7d3
commit
8ba00ed209
@ -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;
|
||||
|
@ -38,6 +38,8 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
fail2ban.enable = true;
|
||||
|
||||
xserver = {
|
||||
layout = "us";
|
||||
xkbVariant = "dvp";
|
||||
|
@ -3,5 +3,4 @@
|
||||
{
|
||||
ip = import ./ip.nix { inherit lib; };
|
||||
dns = import ./dns.nix { inherit lib; };
|
||||
system = import ./system.nix { inherit lib; };
|
||||
}
|
||||
|
@ -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 = ''
|
||||
|
@ -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 = [ ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
143
lib/fudo/kdc.nix
143
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}";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
ip = import ./lib/ip.nix { };
|
||||
dns = import ./lib/dns.nix { };
|
||||
}
|
125
lib/system.nix
125
lib/system.nix
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user