at 25.11-pre 3.2 kB view raw
1{ 2 lib, 3 pkgs, 4 config, 5 ... 6}: 7let 8 inherit (lib) 9 getExe 10 mkDefault 11 mkEnableOption 12 mkIf 13 mkOption 14 mkPackageOption 15 types 16 ; 17 18 cfg = config.services.actual; 19 configFile = formatType.generate "config.json" cfg.settings; 20 dataDir = "/var/lib/actual"; 21 22 formatType = pkgs.formats.json { }; 23in 24{ 25 options.services.actual = { 26 enable = mkEnableOption "actual, a privacy focused app for managing your finances"; 27 package = mkPackageOption pkgs "actual-server" { }; 28 29 openFirewall = mkOption { 30 default = false; 31 type = types.bool; 32 description = "Whether to open the firewall for the specified port."; 33 }; 34 35 settings = mkOption { 36 default = { }; 37 description = "Server settings, refer to [the documentation](https://actualbudget.org/docs/config/) for available options."; 38 type = types.submodule { 39 freeformType = formatType.type; 40 41 options = { 42 hostname = mkOption { 43 type = types.str; 44 description = "The address to listen on"; 45 default = "::"; 46 }; 47 48 port = mkOption { 49 type = types.port; 50 description = "The port to listen on"; 51 default = 3000; 52 }; 53 }; 54 55 config = { 56 serverFiles = mkDefault "${dataDir}/server-files"; 57 userFiles = mkDefault "${dataDir}/user-files"; 58 dataDir = mkDefault dataDir; 59 }; 60 }; 61 }; 62 }; 63 64 config = mkIf cfg.enable { 65 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.port ]; 66 67 systemd.services.actual = { 68 description = "Actual server, a local-first personal finance app"; 69 after = [ "network.target" ]; 70 wantedBy = [ "multi-user.target" ]; 71 environment.ACTUAL_CONFIG_PATH = configFile; 72 serviceConfig = { 73 ExecStart = getExe cfg.package; 74 DynamicUser = true; 75 User = "actual"; 76 Group = "actual"; 77 StateDirectory = "actual"; 78 WorkingDirectory = dataDir; 79 LimitNOFILE = "1048576"; 80 PrivateTmp = true; 81 PrivateDevices = true; 82 StateDirectoryMode = "0700"; 83 Restart = "always"; 84 85 # Hardening 86 CapabilityBoundingSet = ""; 87 LockPersonality = true; 88 #MemoryDenyWriteExecute = true; # Leads to coredump because V8 does JIT 89 PrivateUsers = true; 90 ProtectClock = true; 91 ProtectControlGroups = true; 92 ProtectHome = true; 93 ProtectHostname = true; 94 ProtectKernelLogs = true; 95 ProtectKernelModules = true; 96 ProtectKernelTunables = true; 97 ProtectProc = "invisible"; 98 ProcSubset = "pid"; 99 ProtectSystem = "strict"; 100 RestrictAddressFamilies = [ 101 "AF_INET" 102 "AF_INET6" 103 "AF_NETLINK" 104 ]; 105 RestrictNamespaces = true; 106 RestrictRealtime = true; 107 SystemCallArchitectures = "native"; 108 SystemCallFilter = [ 109 "@system-service" 110 "@pkey" 111 ]; 112 UMask = "0077"; 113 }; 114 }; 115 }; 116 117 meta.maintainers = [ 118 lib.maintainers.oddlama 119 lib.maintainers.patrickdag 120 ]; 121}