Files
nix-files/hosts/common/systemd.nix

176 lines
6.9 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{ config, lib, pkgs, ... }:
let
# N.B.: systemd doesn't like to honor its timeout settings.
# a timeout of 20s is actually closer to 70s,
# because it allows 20s, then after the 20s passes decides to allow 40s, then 60s,
# finally it peacefully kills stuff, and then 10s later actually kills shit.
haltTimeoutSec = 10;
in
{
# sane.persist.sys.byStore.ephemeral = [
# "/var/lib/systemd/coredump" #< persist if coredump.conf's `Storage` != `none`
# ];
# systemd.coredump.enable = false; #< disable because when system is unstable, coredumpctl can bring it to its knees (saturates I/O)
systemd.coredump.extraConfig = ''
# man 5 coredump.conf
# Storage = none => don't persist coredumps to disk; but still log their stack trace.
# note that they may be written to /var/lib/systemd/coredump (and are typically ~1M) -- but only _temporarily_
Storage=none
# Compress=no because the underlying fs is compressed (is it??)
# Compress=no
'';
security.polkit.extraConfig = ''
// allow ordinary users to:
// - reboot
// - shutdown
// source: <https://nixos.wiki/wiki/Polkit>
polkit.addRule(function(action, subject) {
if (subject.isInGroup("users")) {
switch (action.id) {
case "org.freedesktop.login1.reboot":
case "org.freedesktop.login1.reboot-multiple-sessions":
case "org.freedesktop.login1.power-off":
case "org.freedesktop.login1.power-off-multiple-sessions":
return polkit.Result.YES;
default:
}
}
})
// allow members of wheel to:
// - systemctl daemon-reload
polkit.addRule(function(action, subject) {
if (subject.isInGroup("wheel") && action.id == "org.freedesktop.systemd1.reload-daemon") {
return polkit.Result.YES;
}
})
// allow members of wheel to:
// - systemctl restart|start|stop SERVICE
polkit.addRule(function(action, subject) {
if (subject.isInGroup("wheel") && action.id == "org.freedesktop.systemd1.manage-units") {
switch (action.lookup("verb")) {
// case "cancel":
// case "reenable":
case "restart":
// case "reload":
// case "reload-or-restart":
case "start":
case "stop":
// case "try-reload-or-restart":
// case "try-restart":
return polkit.Result.YES;
default:
}
}
})
'';
sane.persist.sys.byPath."/var/log/journal" = {
# prefer to persist to private, because the journal can have sensitive info in it.
# but plaintext also works for scenarios where the system can't be unlocked.
store = "private";
# store = "plaintext";
# ordering of bind mounts is tricky for private store, so prefer symlink
method = "symlink";
acl.mode = "0750";
};
# systemd-journal-flush.service switches journald from volatile to persistent storage partway through boot
# ExecStart=journalctl --flush
# ExecStop=journalctl --smart-relinquish-var
# - before `--flush`, data is at /run/systemd/journal
# flush _copies_ data from /run... to /var/log/journal
#
# BUT: systemd-journal-flush.service ships with `Before=systemd-tmpfiles-setup.service`, which causes a circular dependency
# that's low-level enough to break boot (sysinit.target).
#
# no way to _remove_ an upstream `Before` attribute.
# instead, neuter the service and re-implement the key bits.
systemd.services.systemd-journal-flush.serviceConfig.ExecStart = [
"" #< clear original `ExecStart`
];
systemd.services.systemd-journal-flush-sane = {
description = "flush early logging data to persistent storage, but later in the boot than normal to avoid cycles";
wantedBy = [ "systemd-journal-flush.service" ];
serviceConfig.ExecStart = "journalctl --flush";
# ensure /var/log/journal symlink exists before starting:
serviceConfig.ExecStartPre = "-${lib.getExe' pkgs.systemd "systemd-tmpfiles"} --prefix=/var/log/journal --boot --create --graceful";
unitConfig.DefaultDependencies = false;
unitConfig.RequiresMountsFor = lib.optionals config.sane.persist.enable [
config.sane.persist.sys.byPath."/var/log/journal".store.origin
];
};
services.journald.extraConfig = ''
# docs: `man journald.conf`
# merged journald config is deployed to /etc/systemd/journald.conf
[Journal]
# disable journal compression for better debugging (besides, my fs is already compressed)
Compress=no
'';
# see: `man logind.conf`
# dont shutdown when power button is short-pressed (commonly done an accident, or by cats).
# but do on long-press: useful to gracefully power-off server.
services.logind.settings.Login.HandlePowerKey = "lock";
services.logind.settings.Login.HandlePowerKeyLongPress = "poweroff";
services.logind.settings.Login.HandleLidSwitch = "lock";
# under logind, 'uaccess' tag would grant the logged in user access to a device.
# outside logind, map uaccess tag -> plugdev group to grant that access.
services.udev.extraRules = ''
TAG=="uaccess" GROUP="plugdev"
'';
systemd.settings.Manager = {
# DefaultTimeoutStopSec defaults to 90s, and frequently blocks overall system shutdown.
DefaultTimeoutStopSec = "${builtins.toString haltTimeoutSec}s";
};
# fixes "Cannot open access to console, the root account is locked" on systemd init failure.
# see: <https://github.com/systemd/systemd/commit/33eb44fe4a8d7971b5614bc4c2d90f8d91cce66c>
# - emergency: kill (or don't start) everything; drop into root shell.
# - rescue: start sysinit.target (which mounts the local-fs, and others), and drop into root shell.
# enable emergency.target; configure elsewhere everything sensitive (e.g. /mnt/persist/private) to conflict with it.
# because of `rescue.target`'s `Requires=sysinit.target`, we can't (easily) allow its root shell safely.
systemd.services.emergency.environment.SYSTEMD_SULOGIN_FORCE = "1";
# systemd.services.rescue.environment.SYSTEMD_SULOGIN_FORCE = "1";
# harden base systemd services
# see: `systemd-analyze security`
systemd.services.systemd-rfkill.serviceConfig = {
AmbientCapabilities = "";
CapabilityBoundingSet = "";
DevicePolicy = "closed";
IPAddressDeny = "any";
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateMounts = true;
PrivateNetwork = true;
PrivateTmp = true;
PrivateUsers = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = "AF_UNIX";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
};
}