diff options
| author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-03-20 01:03:33 +0100 |
|---|---|---|
| committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-03-20 01:03:33 +0100 |
| commit | 9a20fb5c803bf4e3e72a4073a19c6c30576adda0 (patch) | |
| tree | 2ed4a520afcabebba0fd48c35db22b084fad7989 /module/default.nix | |
| download | nix-9a20fb5c803bf4e3e72a4073a19c6c30576adda0.zip | |
feat: Initial commit
Diffstat (limited to 'module/default.nix')
| -rw-r--r-- | module/default.nix | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/module/default.nix b/module/default.nix new file mode 100644 index 0000000..608559b --- /dev/null +++ b/module/default.nix @@ -0,0 +1,140 @@ +{ + rocie-mobile, + rocie-server, +}: { + lib, + config, + ... +}: let + cfg = config.rocie; +in { + options.rocie = { + enable = lib.mkEnableOption "rocie"; + domain = lib.mkOption { + description = "Under which domain (vhost) to deploy rocie?"; + type = lib.types.str; + }; + + port = lib.mkOption { + description = "Which port to use"; + type = lib.types.int.port; + default = 8543; + }; + address = lib.mkOption { + description = "Which address to use"; + type = lib.types.str; + default = "127.0.0.1"; + }; + + dbPath = lib.mkOption { + description = "Where to store the database"; + type = lib.types.str; + default = "$STATE_DIRECTORY/storage.db"; + }; + + secretKeyFile = lib.mkOption { + description = "Which file to use as signing key for user log-ins"; + type = lib.types.nullOr lib.types.str; + }; + + webFrontEnd = lib.mkOption { + description = "Which package to use for the web front end"; + type = lib.types.package; + default = rocie-mobile; + }; + server = lib.mkOption { + description = "Which package to use for the server"; + type = lib.types.package; + default = rocie-server; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.packages = [cfg.server]; + systemd.services.rocie = { + wantedBy = ["default.target"]; + serviceConfig = + { + StateDirectory = "rocie"; + + # Hardening + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateIPC = true; + PrivateTmp = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "full"; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "~@mount" + "~@swap" + "~@resources" + "~@reboot" + "~@raw-io" + "~@obsolete" + "~@module" + "~@debug" + "~@cpu-emulation" + "~@clock" + "~@privileged" + ]; + UMask = "0027"; + + ExecStart = [ + "" + "${lib.getExe cfg.server} \ + serve \ + --port ${cfg.port} \ + ${lib.strings.optionalString (cfg.secretKeyFile != null) "--secret-key-file ${cfg.secretKeyFile}"} \ + --host ${cfg.address} \ + --db-path ${cfg.dbPath}" + ]; + } + // ( + if (cfg.port < 1024) + then { + AmbientCapabilities = ["CAP_NET_BIND_SERVICE"]; + CapabilityBoundingSet = ["CAP_NET_BIND_SERVICE"]; + } + else { + # A private user cannot have process capabilities on the host's user + # namespace and thus CAP_NET_BIND_SERVICE has no effect. + PrivateUsers = true; + CapabilityBoundingSet = false; + } + ); + }; + + services.nginx.virtualHosts."${cfg.domain}" = { + locations."/api" = { + proxyPass = "http://127.0.0.1:${cfg.port}"; + + recommendedProxySettings = true; + proxyWebsockets = true; + }; + + root = "${cfg.webFrontEnd}"; + + enableACME = true; + forceSSL = true; + }; + }; +} |
