feat: nucnix
This commit is contained in:
parent
1d2e32b8b0
commit
d4e2805a87
55
config/support/server.nix
Normal file
55
config/support/server.nix
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
environment = {
|
||||
# Print the URL instead on servers
|
||||
variables.BROWSER = "echo";
|
||||
# Don't install the /lib/ld-linux.so.2 and /lib64/ld-linux-x86-64.so.2
|
||||
# stubs. Server users should know what they are doing.
|
||||
stub-ld.enable = false;
|
||||
};
|
||||
# Given that our systems are headless, emergency mode is useless.
|
||||
# We prefer the system to attempt to continue booting so
|
||||
# that we can hopefully still access it remotely.
|
||||
boot.initrd.systemd.suppressedUnits = [
|
||||
"emergency.service"
|
||||
"emergency.target"
|
||||
];
|
||||
# Given that our systems are headless, emergency mode is useless.
|
||||
# We prefer the system to attempt to continue booting so
|
||||
# that we can hopefully still access it remotely.
|
||||
systemd.enableEmergencyMode = false;
|
||||
|
||||
documentation.nixos.enable = false;
|
||||
|
||||
# No need for fonts on a server
|
||||
fonts.fontconfig.enable = false;
|
||||
|
||||
programs.command-not-found.enable = false;
|
||||
|
||||
# freedesktop xdg files
|
||||
xdg.autostart.enable = false;
|
||||
xdg.icons.enable = false;
|
||||
xdg.menus.enable = false;
|
||||
xdg.mime.enable = false;
|
||||
xdg.sounds.enable = false;
|
||||
|
||||
systemd = {
|
||||
|
||||
# For more detail, see:
|
||||
# https://0pointer.de/blog/projects/watchdog.html
|
||||
watchdog = {
|
||||
# systemd will send a signal to the hardware watchdog at half
|
||||
# the interval defined here, so every 7.5s.
|
||||
# If the hardware watchdog does not get a signal for 15s,
|
||||
# it will forcefully reboot the system.
|
||||
runtimeTime = "15s";
|
||||
# Forcefully reboot if the final stage of the reboot
|
||||
# hangs without progress for more than 30s.
|
||||
# For more info, see:
|
||||
# https://utcc.utoronto.ca/~cks/space/blog/linux/SystemdShutdownWatchdog
|
||||
rebootTime = "30s";
|
||||
# Forcefully reboot when a host hangs after kexec.
|
||||
# This may be the case when the firmware does not support kexec.
|
||||
kexecTime = "1m";
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1642,11 +1642,11 @@
|
|||
"treefmt-nix": "treefmt-nix_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733348844,
|
||||
"narHash": "sha256-glufwHZDCoXjPrfvYSw8PrwQLyFVsg933gt/Gg4hlLE=",
|
||||
"lastModified": 1734202825,
|
||||
"narHash": "sha256-/9r2lRpVLG81uF7zxuk4LDnPZN0kk93tTclMA5KQK0E=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "3052ba7b255b8e3c333fcb318e79ce15c88dd2a7",
|
||||
"revCount": 22,
|
||||
"rev": "09fb938cb462681aaf6d7016e35a90d4995aad8c",
|
||||
"revCount": 23,
|
||||
"type": "git",
|
||||
"url": "https://forge.lel.lol/patrick/nixp-meta.git"
|
||||
},
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
../../config/support/initrd-ssh.nix
|
||||
../../config/support/physical.nix
|
||||
../../config/support/secureboot.nix
|
||||
../../config/support/server.nix
|
||||
../../config/support/zfs.nix
|
||||
|
||||
./net.nix
|
||||
|
@ -28,58 +29,5 @@
|
|||
};
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
|
||||
# Given that our systems are headless, emergency mode is useless.
|
||||
# We prefer the system to attempt to continue booting so
|
||||
# that we can hopefully still access it remotely.
|
||||
boot.initrd.systemd.suppressedUnits = [
|
||||
"emergency.service"
|
||||
"emergency.target"
|
||||
];
|
||||
environment = {
|
||||
# Print the URL instead on servers
|
||||
variables.BROWSER = "echo";
|
||||
# Don't install the /lib/ld-linux.so.2 and /lib64/ld-linux-x86-64.so.2
|
||||
# stubs. Server users should know what they are doing.
|
||||
stub-ld.enable = false;
|
||||
};
|
||||
# Given that our systems are headless, emergency mode is useless.
|
||||
# We prefer the system to attempt to continue booting so
|
||||
# that we can hopefully still access it remotely.
|
||||
systemd.enableEmergencyMode = false;
|
||||
|
||||
documentation.nixos.enable = false;
|
||||
|
||||
# No need for fonts on a server
|
||||
fonts.fontconfig.enable = false;
|
||||
|
||||
programs.command-not-found.enable = false;
|
||||
|
||||
# freedesktop xdg files
|
||||
xdg.autostart.enable = false;
|
||||
xdg.icons.enable = false;
|
||||
xdg.menus.enable = false;
|
||||
xdg.mime.enable = false;
|
||||
xdg.sounds.enable = false;
|
||||
|
||||
systemd = {
|
||||
|
||||
# For more detail, see:
|
||||
# https://0pointer.de/blog/projects/watchdog.html
|
||||
watchdog = {
|
||||
# systemd will send a signal to the hardware watchdog at half
|
||||
# the interval defined here, so every 7.5s.
|
||||
# If the hardware watchdog does not get a signal for 15s,
|
||||
# it will forcefully reboot the system.
|
||||
runtimeTime = "15s";
|
||||
# Forcefully reboot if the final stage of the reboot
|
||||
# hangs without progress for more than 30s.
|
||||
# For more info, see:
|
||||
# https://utcc.utoronto.ca/~cks/space/blog/linux/SystemdShutdownWatchdog
|
||||
rebootTime = "30s";
|
||||
# Forcefully reboot when a host hangs after kexec.
|
||||
# This may be the case when the firmware does not support kexec.
|
||||
kexecTime = "1m";
|
||||
};
|
||||
};
|
||||
topology.self.interfaces.lan.network = "home";
|
||||
}
|
||||
|
|
|
@ -20,14 +20,6 @@
|
|||
MulticastDNS = true;
|
||||
};
|
||||
};
|
||||
"40-lan01" = {
|
||||
dhcpV6Config.UseDNS = false;
|
||||
dhcpV4Config.UseDNS = false;
|
||||
ipv6AcceptRAConfig.UseDNS = false;
|
||||
networkConfig = {
|
||||
MulticastDNS = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
boot.initrd.systemd.network = {
|
||||
enable = true;
|
||||
|
|
31
hosts/nucnix/default.nix
Normal file
31
hosts/nucnix/default.nix
Normal file
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
inputs,
|
||||
minimal,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
inputs.nixos-hardware.nixosModules.common-pc
|
||||
inputs.nixos-hardware.nixosModules.common-pc-ssd
|
||||
inputs.nixos-hardware.nixosModules.common-cpu-intel
|
||||
|
||||
../../config/basic
|
||||
|
||||
../../config/support/initrd-ssh.nix
|
||||
../../config/support/physical.nix
|
||||
../../config/support/zfs.nix
|
||||
../../config/support/server.nix
|
||||
|
||||
./net.nix
|
||||
./fs.nix
|
||||
] ++ lib.lists.optionals (!minimal) [ ./guests.nix ];
|
||||
services.xserver = {
|
||||
xkb = {
|
||||
layout = "de";
|
||||
};
|
||||
};
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
|
||||
topology.self.interfaces.lan.network = "home";
|
||||
}
|
91
hosts/nucnix/fs.nix
Normal file
91
hosts/nucnix/fs.nix
Normal file
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
disko.devices = {
|
||||
disk = {
|
||||
ssd = rec {
|
||||
type = "disk";
|
||||
device = "/dev/disk/by-id/${config.secrets.secrets.local.disko.nvme}";
|
||||
content = with lib.disko.gpt; {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
boot = (partEfi "1G") // {
|
||||
device = "${device}-part1";
|
||||
};
|
||||
rpool = (partLuksZfs "ssd" "rpool" "100%") // {
|
||||
device = "${device}-part2";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
zpool = with lib.disko.zfs; {
|
||||
rpool = mkZpool { datasets = impermanenceZfsDatasets; };
|
||||
};
|
||||
};
|
||||
|
||||
boot.kernel.sysctl."fs.inotify.max_user_instances" = 1024;
|
||||
|
||||
services.zrepl = {
|
||||
enable = true;
|
||||
settings = {
|
||||
global = {
|
||||
logging = [
|
||||
{
|
||||
type = "syslog";
|
||||
level = "info";
|
||||
format = "human";
|
||||
}
|
||||
];
|
||||
# TODO Monitoring
|
||||
};
|
||||
jobs = [
|
||||
#{
|
||||
# type = "push";
|
||||
# name = "push-to-remote";
|
||||
#}
|
||||
{
|
||||
type = "snap";
|
||||
name = "mach-schnipp-schusss";
|
||||
filesystems = {
|
||||
"rpool/local/state<" = true;
|
||||
"rpool/local/guests<" = true;
|
||||
"rpool/safe<" = true;
|
||||
};
|
||||
snapshotting = {
|
||||
type = "periodic";
|
||||
prefix = "zrepl-";
|
||||
interval = "10m";
|
||||
timestamp_format = "iso-8601";
|
||||
};
|
||||
pruning = {
|
||||
keep = [
|
||||
{
|
||||
type = "regex";
|
||||
regex = "^zrepl-.*$";
|
||||
negate = true;
|
||||
}
|
||||
{
|
||||
type = "grid";
|
||||
grid = lib.concatStringsSep " | " [
|
||||
"1x1d(keep=all)"
|
||||
"142x1h(keep=2)"
|
||||
"90x1d(keep=2)"
|
||||
"500x7d"
|
||||
];
|
||||
regex = "^zrepl-.*$";
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
fileSystems."/state".neededForBoot = true;
|
||||
fileSystems."/persist".neededForBoot = true;
|
||||
}
|
182
hosts/nucnix/guests.nix
Normal file
182
hosts/nucnix/guests.nix
Normal file
|
@ -0,0 +1,182 @@
|
|||
{
|
||||
config,
|
||||
stateVersion,
|
||||
inputs,
|
||||
lib,
|
||||
minimal,
|
||||
nodes,
|
||||
...
|
||||
}:
|
||||
let
|
||||
domainOf =
|
||||
hostName:
|
||||
let
|
||||
domains =
|
||||
{
|
||||
};
|
||||
in
|
||||
"${domains.${hostName}}.${config.secrets.secrets.global.domains.web}";
|
||||
# TODO hard coded elisabeth nicht so schön
|
||||
ipOf = hostName: nodes."elisabeth-${hostName}".config.wireguard.elisabeth.ipv4;
|
||||
in
|
||||
{
|
||||
services.nginx =
|
||||
let
|
||||
blockOf =
|
||||
hostName:
|
||||
{
|
||||
virtualHostExtraConfig ? "",
|
||||
maxBodySize ? "500M",
|
||||
port ? 3000,
|
||||
upstream ? hostName,
|
||||
protocol ? "http",
|
||||
}:
|
||||
{
|
||||
upstreams.${hostName} = {
|
||||
servers."${ipOf upstream}:${toString port}" = { };
|
||||
extraConfig = ''
|
||||
zone ${hostName} 64k ;
|
||||
keepalive 5 ;
|
||||
'';
|
||||
};
|
||||
virtualHosts.${domainOf hostName} = {
|
||||
forceSSL = true;
|
||||
useACMEHost = "web";
|
||||
locations."/" = {
|
||||
proxyPass = "${protocol}://${hostName}";
|
||||
proxyWebsockets = true;
|
||||
X-Frame-Options = "SAMEORIGIN";
|
||||
};
|
||||
extraConfig =
|
||||
''
|
||||
client_max_body_size ${maxBodySize} ;
|
||||
''
|
||||
+ virtualHostExtraConfig;
|
||||
};
|
||||
};
|
||||
proxyProtect =
|
||||
hostName: cfg: allowedGroup:
|
||||
lib.mkMerge [
|
||||
(blockOf hostName cfg)
|
||||
{
|
||||
virtualHosts.${domainOf hostName} = {
|
||||
locations."/".extraConfig = ''
|
||||
auth_request /oauth2/auth;
|
||||
error_page 401 = /oauth2/sign_in;
|
||||
|
||||
# pass information via X-User and X-Email headers to backend,
|
||||
# requires running with --set-xauthrequest flag
|
||||
auth_request_set $user $upstream_http_x_auth_request_preferred_username;
|
||||
# Set the email to our own domain in case user change their mail
|
||||
auth_request_set $email "''${upstream_http_x_auth_request_preferred_username}@${config.secrets.secrets.global.domains.web}";
|
||||
proxy_set_header X-User $user;
|
||||
proxy_set_header X-Email $email;
|
||||
|
||||
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
|
||||
auth_request_set $auth_cookie $upstream_http_set_cookie;
|
||||
add_header Set-Cookie $auth_cookie;
|
||||
'';
|
||||
locations."/oauth2/" = {
|
||||
proxyPass = "http://oauth2-proxy";
|
||||
extraConfig = ''
|
||||
proxy_set_header X-Scheme $scheme;
|
||||
proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
|
||||
'';
|
||||
};
|
||||
|
||||
locations."= /oauth2/auth" = {
|
||||
proxyPass =
|
||||
"http://oauth2-proxy/oauth2/auth"
|
||||
+ lib.optionalString allowedGroup "?allowed_groups=${hostName}_access";
|
||||
extraConfig = ''
|
||||
internal;
|
||||
|
||||
proxy_set_header X-Scheme $scheme;
|
||||
# nginx auth_request includes headers but not body
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_pass_request_body off;
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
in
|
||||
lib.mkMerge [
|
||||
{
|
||||
enable = false;
|
||||
recommendedSetup = true;
|
||||
}
|
||||
];
|
||||
|
||||
guests =
|
||||
let
|
||||
mkGuest = guestName: {
|
||||
autostart = true;
|
||||
zfs."/state" = {
|
||||
pool = "rpool";
|
||||
dataset = "local/guests/${guestName}";
|
||||
};
|
||||
zfs."/persist" = {
|
||||
pool = "rpool";
|
||||
dataset = "safe/guests/${guestName}";
|
||||
};
|
||||
modules = [
|
||||
../../config/basic
|
||||
../../config/services/${guestName}.nix
|
||||
{
|
||||
node.secretsDir = config.node.secretsDir + "/${guestName}";
|
||||
networking.nftables.firewall.zones.untrusted.interfaces = [
|
||||
config.guests.${guestName}.networking.mainLinkName
|
||||
];
|
||||
systemd.network.networks."10-${config.guests.${guestName}.networking.mainLinkName}" = {
|
||||
DHCP = lib.mkForce "no";
|
||||
address = [
|
||||
(lib.net.cidr.hostCidr
|
||||
config.secrets.secrets.global.net.ips."${config.guests.${guestName}.nodeName}"
|
||||
config.secrets.secrets.global.net.privateSubnetv4
|
||||
)
|
||||
(lib.net.cidr.hostCidr
|
||||
config.secrets.secrets.global.net.ips."${config.guests.${guestName}.nodeName}"
|
||||
config.secrets.secrets.global.net.privateSubnetv6
|
||||
)
|
||||
];
|
||||
gateway = [ (lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4) ];
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
mkMicrovm = guestName: cfg: {
|
||||
${guestName} = mkGuest guestName cfg // {
|
||||
backend = "microvm";
|
||||
microvm = {
|
||||
system = "x86_64-linux";
|
||||
macvtap = "lan";
|
||||
baseMac = config.secrets.secrets.local.networking.interfaces.lan01.mac;
|
||||
};
|
||||
extraSpecialArgs = {
|
||||
inherit (inputs.self) nodes;
|
||||
inherit (inputs.self.pkgs.x86_64-linux) lib;
|
||||
inherit inputs minimal stateVersion;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mkContainer = guestName: cfg: {
|
||||
${guestName} = mkGuest guestName cfg // {
|
||||
backend = "container";
|
||||
container.macvlan = "lan";
|
||||
extraSpecialArgs = {
|
||||
inherit
|
||||
lib
|
||||
nodes
|
||||
inputs
|
||||
minimal
|
||||
stateVersion
|
||||
;
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{ };
|
||||
}
|
54
hosts/nucnix/net.nix
Normal file
54
hosts/nucnix/net.nix
Normal file
|
@ -0,0 +1,54 @@
|
|||
{ config, lib, ... }:
|
||||
{
|
||||
networking = {
|
||||
inherit (config.secrets.secrets.local.networking) hostId;
|
||||
};
|
||||
systemd.network.networks = {
|
||||
"10-lan01" = {
|
||||
address = [
|
||||
(lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips.${config.node.name}
|
||||
config.secrets.secrets.global.net.privateSubnetv4
|
||||
)
|
||||
];
|
||||
gateway = [ (lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4) ];
|
||||
#matchConfig.MACAddress = config.secrets.secrets.local.networking.interfaces.lan01.mac;
|
||||
matchConfig.Name = "lan";
|
||||
dhcpV6Config.UseDNS = false;
|
||||
dhcpV4Config.UseDNS = false;
|
||||
ipv6AcceptRAConfig.UseDNS = false;
|
||||
networkConfig = {
|
||||
MulticastDNS = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
boot.initrd.systemd.network = {
|
||||
enable = true;
|
||||
networks = {
|
||||
# redo the network cause the livesystem has macvlans
|
||||
"10-lan01" = {
|
||||
address = [
|
||||
(lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips.${config.node.name}
|
||||
config.secrets.secrets.global.net.privateSubnetv4
|
||||
)
|
||||
];
|
||||
gateway = [ (lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4) ];
|
||||
matchConfig.MACAddress = config.secrets.secrets.local.networking.interfaces.lan01.mac;
|
||||
dhcpV6Config.UseDNS = false;
|
||||
dhcpV4Config.UseDNS = false;
|
||||
ipv6AcceptRAConfig.UseDNS = false;
|
||||
networkConfig = {
|
||||
IPv6PrivacyExtensions = "yes";
|
||||
MulticastDNS = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
networking.nftables.firewall.zones.untrusted.interfaces = [ "lan" ];
|
||||
|
||||
# To be able to ping containers from the host, it is necessary
|
||||
# to create a macvlan on the host on the VLAN 1 network.
|
||||
networking.macvlans.lan = {
|
||||
interface = "lan01";
|
||||
mode = "bridge";
|
||||
};
|
||||
}
|
BIN
hosts/nucnix/secrets/generated/initrd_host_ed25519_key.age
Normal file
BIN
hosts/nucnix/secrets/generated/initrd_host_ed25519_key.age
Normal file
Binary file not shown.
1
hosts/nucnix/secrets/host.pub
Normal file
1
hosts/nucnix/secrets/host.pub
Normal file
|
@ -0,0 +1 @@
|
|||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDS0gxZD8aIAAKBtt7gyMHZ2KloQPlHxS+LsQY/62SzE
|
BIN
hosts/nucnix/secrets/secrets.nix.age
Normal file
BIN
hosts/nucnix/secrets/secrets.nix.age
Normal file
Binary file not shown.
|
@ -21,6 +21,7 @@ in
|
|||
name = "Home-manager options for the main user";
|
||||
merge = _loc: defs: (map (x: x.value) defs);
|
||||
};
|
||||
default = { };
|
||||
};
|
||||
hm-all = mkOption {
|
||||
description = "Home-manager options for the primary User and root.";
|
||||
|
@ -28,6 +29,7 @@ in
|
|||
name = "Home-manager options for the all users";
|
||||
merge = _loc: defs: (map (x: x.value) defs);
|
||||
};
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
config.home-manager.users = mkMerge [
|
||||
|
|
|
@ -1,154 +0,0 @@
|
|||
{
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
getExe
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
mkPackageOption
|
||||
types
|
||||
;
|
||||
|
||||
cfg = config.services.actual;
|
||||
configFile = formatType.generate "config.json" cfg.settings;
|
||||
dataDir = "/var/lib/actual";
|
||||
|
||||
formatType = pkgs.formats.json { };
|
||||
in
|
||||
{
|
||||
options.services.actual = {
|
||||
enable = mkEnableOption "actual, a privacy focused app for managing your finances";
|
||||
package = mkPackageOption pkgs "actual-server" { };
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "actual";
|
||||
description = ''
|
||||
User to run actual as.
|
||||
|
||||
::: {.note}
|
||||
If left as the default value this user will automatically be created
|
||||
on system activation, otherwise the sysadmin is responsible for
|
||||
ensuring the user exists.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "actual";
|
||||
description = ''
|
||||
Group under which to run.
|
||||
|
||||
::: {.note}
|
||||
If left as the default value this group will automatically be created
|
||||
on system activation, otherwise the sysadmin is responsible for
|
||||
ensuring the user exists.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = "Whether to open the firewall for the specified port.";
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
default = { };
|
||||
description = "Server settings, refer to (the documentation)[https://actualbudget.org/docs/config/] for available options.";
|
||||
type = types.submodule {
|
||||
freeformType = formatType.type;
|
||||
|
||||
options = {
|
||||
hostname = mkOption {
|
||||
type = types.str;
|
||||
description = "The address to listen on";
|
||||
default = "::";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
description = "The port to listen on";
|
||||
default = 3000;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
serverFiles = "${dataDir}/server-files";
|
||||
userFiles = "${dataDir}/user-files";
|
||||
inherit dataDir;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.port ];
|
||||
|
||||
users.groups = mkIf (cfg.group == "actual") {
|
||||
${cfg.group} = { };
|
||||
};
|
||||
|
||||
users.users = mkIf (cfg.user == "actual") {
|
||||
${cfg.user} = {
|
||||
isSystemUser = true;
|
||||
inherit (cfg) group;
|
||||
home = dataDir;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.actual = {
|
||||
description = "Actual server, a local-first personal finance app";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment.ACTUAL_CONFIG_PATH = configFile;
|
||||
serviceConfig = {
|
||||
ExecStart = getExe cfg.package;
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
StateDirectory = "actual";
|
||||
WorkingDirectory = dataDir;
|
||||
LimitNOFILE = "1048576";
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
StateDirectoryMode = "0700";
|
||||
Restart = "always";
|
||||
|
||||
# Hardening
|
||||
CapabilityBoundingSet = "";
|
||||
LockPersonality = true;
|
||||
#MemoryDenyWriteExecute = true; # Leads to coredump because V8 does JIT
|
||||
PrivateUsers = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
ProcSubset = "pid";
|
||||
ProtectSystem = "strict";
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_NETLINK"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"@pkey"
|
||||
];
|
||||
UMask = "0077";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
Binary file not shown.
Loading…
Reference in a new issue