diff --git a/config.nix b/config.nix new file mode 100644 index 0000000..5ac15ea --- /dev/null +++ b/config.nix @@ -0,0 +1,67 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.cratedocs-mcp; +in +{ + options.services.cratedocs-mcp = { + enable = lib.mkEnableOption "CrateDocs MCP server"; + + port = lib.mkOption { + type = lib.types.int; + default = 3000; + description = "Port to listen on for HTTP/SSE server"; + }; + + user = lib.mkOption { + type = lib.types.str; + default = "cratedocs-mcp"; + description = "User to run the service as"; + }; + + group = lib.mkOption { + type = lib.types.str; + default = "cratedocs-mcp"; + description = "Group to run the service as"; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.cratedocs-mcp = { + description = "CrateDocs MCP server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ExecStart = "${lib.getExe pkgs.cratedocs-mcp} --port ${toString cfg.port}"; + Restart = "always"; + User = cfg.user; + Group = cfg.group; + DynamicUser = true; + StateDirectory = "cratedocs-mcp"; + CacheDirectory = "cratedocs-mcp"; + + # Security hardening + PrivateTmp = true; + ProtectSystem = "strict"; + ProtectHome = true; + NoNewPrivileges = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RemoveIPC = true; + PrivateMounts = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + SystemCallFilter = "@system-service"; + SystemCallErrorNumber = "EPERM"; + }; + }; + }; +} \ No newline at end of file diff --git a/flake.nix b/flake.nix index 6fd8e11..75f39f3 100644 --- a/flake.nix +++ b/flake.nix @@ -42,5 +42,6 @@ default = cratedocs-mcp; }; }; + nixosModules.default = import ./config.nix; }; }