nix: harden Systemd service in NixOS module
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I086cdbfcd0a0d1d87173a2cf97f5b5416a6a6964
This commit is contained in:
parent
3eb5ccf61c
commit
70443c83ce
1 changed files with 116 additions and 26 deletions
|
|
@ -25,59 +25,149 @@ in {
|
|||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = str;
|
||||
default = "troutbot";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = str;
|
||||
default = "troutbot";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = port;
|
||||
default = 3000;
|
||||
};
|
||||
|
||||
environmentFile = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Environment file for specifying additional settings such as secrets.
|
||||
'';
|
||||
};
|
||||
|
||||
configPath = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
File path containing the Troutbot Typescript config.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = {
|
||||
port = mkOption {
|
||||
type = port;
|
||||
default = 3000;
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = str;
|
||||
default = "troutbot";
|
||||
description = "User to run Troutbot under";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = str;
|
||||
default = "troutbot";
|
||||
description = "Group to run Troutbot under";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
users.users.${cfg.user} = {
|
||||
isSystemUser = true;
|
||||
group = cfg.group;
|
||||
users = {
|
||||
groups.${cfg.settings.group} = {};
|
||||
users.${cfg.settings.user} = {
|
||||
isSystemUser = true;
|
||||
inherit (cfg.settings) group;
|
||||
};
|
||||
};
|
||||
|
||||
users.groups.${cfg.group} = {};
|
||||
|
||||
systemd.services.troutbot = {
|
||||
description = "Troutbot";
|
||||
after = ["network.target"];
|
||||
description = "Troutbot - GitHub PR/Issue Analysis Bot";
|
||||
documentation = ["https://github.com/notashelf/troutbot"];
|
||||
after = ["network-online.target"];
|
||||
wants = ["network-online.target"];
|
||||
wantedBy = ["multi-user.target"];
|
||||
|
||||
environment = {
|
||||
NODE_ENV = "production";
|
||||
CONFIG_PATH = cfg.configPath;
|
||||
PORT = toString cfg.port;
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
ExecStart = "${lib.getExe cfg.package}";
|
||||
|
||||
# Restart policy with rate limiting
|
||||
Restart = "on-failure";
|
||||
EnvironmentFile = cfg.environmentFile;
|
||||
NODE_ENV = "production";
|
||||
CONFIG_PATH = cfg.configPath;
|
||||
PORT = toString cfg.port;
|
||||
RestartSec = "5s";
|
||||
StartLimitInterval = "60s";
|
||||
StartLimitBurst = 3;
|
||||
|
||||
# Timeouts
|
||||
TimeoutStartSec = "30s";
|
||||
TimeoutStopSec = "30s";
|
||||
|
||||
# Working directory and state
|
||||
WorkingDirectory = "/var/lib/troutbot";
|
||||
StateDirectory = "troutbot";
|
||||
StateDirectoryMode = "0750";
|
||||
|
||||
# Environment file for secrets
|
||||
EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
|
||||
|
||||
# Security hardening
|
||||
# Filesystem restrictions
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
|
||||
# Process restrictions
|
||||
NoNewPrivileges = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectClock = true;
|
||||
|
||||
# Memory and execution restrictions
|
||||
MemoryDenyWriteExecute = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
LockPersonality = true;
|
||||
|
||||
# IPC cleanup
|
||||
RemoveIPC = true;
|
||||
|
||||
# Capabilities
|
||||
CapabilityBoundingSet = [""]; # no capabilities needed
|
||||
AmbientCapabilities = []; # no ambient capabilities
|
||||
|
||||
# System call filtering
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@privileged"
|
||||
"~@resources"
|
||||
];
|
||||
SystemCallErrorNumber = "EPERM";
|
||||
|
||||
# Address families, only IPv4/IPv6 needed
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_UNIX"
|
||||
"AF_NETLINK" # for DNS resolution
|
||||
];
|
||||
|
||||
UMask = "0027";
|
||||
DeviceAllow = [];
|
||||
};
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = cfg.settings.user;
|
||||
Group = cfg.settings.group;
|
||||
ExecStart = "${lib.getExe cfg.package}";
|
||||
Restart = "on-failure";
|
||||
EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
|
||||
WorkingDirectory = "/var/lib/troutbot";
|
||||
StateDirectory = "troutbot";
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
PrivateTmp = true;
|
||||
NoNewPrivileges = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue