fix(#2422): harden NixOS packaged runtime

This commit is contained in:
Rod Boev authored and nesquena-hermes committed 2026-07-14 01:06:49 +00:00
1 parent 1f505aad88
commit 70514549c4
7 files changed
+152 -32

No files matched your search

+45 -25
View File
@@ -15,6 +15,9 @@ let
"HERMES_WEBUI_PYTHON"
];
defaultUser = "hermes-webui";
defaultGroup = "hermes-webui";
protectedEnvironmentFileCheck = pkgs.writeShellScript "hermes-webui-protected-envfile-check" ''
set -eu
for env_file in "$@"; do
@@ -47,8 +50,16 @@ let
inferredAgentDir =
if cfg.agent.package == null then
null
else if (cfg.agent.package ? passthru) && (cfg.agent.package.passthru ? hermesVenv) then
"${cfg.agent.package.passthru.hermesVenv}/lib/python3.12/site-packages"
else
"${cfg.agent.package}/share/hermes-agent";
null;
configuredAgentPython =
if cfg.agent.python != null then
cfg.agent.python
else
inferredAgentPython;
mappedEnvironment = (lib.mapAttrsToList
(name: value: "${name}=${value}")
@@ -66,27 +77,18 @@ let
// lib.optionalAttrs (cfg.agent.dir == null && inferredAgentDir != null) {
HERMES_WEBUI_AGENT_DIR = inferredAgentDir;
}
// lib.optionalAttrs (cfg.agent.dir == null && inferredAgentPython != null) {
HERMES_WEBUI_PYTHON = inferredAgentPython;
// lib.optionalAttrs (configuredAgentPython != null) {
HERMES_WEBUI_PYTHON = configuredAgentPython;
}
// lib.filterAttrs
(name: _: !(lib.elem name protectedEnvironment))
cfg.extraEnvironment));
needsWritableStateDir = cfg.stateDir != defaultStateDir;
writableServiceDirs =
lib.optionals needsWritableStateDir [ cfg.stateDir ]
++ lib.optionals (cfg.hermesHome != null) [ cfg.hermesHome ];
userspaceDirs = lib.optionals (cfg.hermesHome != null) [
"d ${cfg.hermesHome} 2770 ${cfg.user} ${cfg.group} - -"
writableServiceDirs = lib.optionals needsWritableStateDir [ cfg.stateDir ];
tmpfilesRules = lib.optionals needsWritableStateDir [
"d ${cfg.stateDir} 2770 ${cfg.user} ${cfg.group} - -"
];
tmpfilesRules =
(lib.optionals needsWritableStateDir [
"d ${cfg.stateDir} 2770 ${cfg.user} ${cfg.group} - -"
])
++ userspaceDirs;
in
{
options.services.hermes-webui = {
@@ -101,19 +103,19 @@ in
user = lib.mkOption {
type = lib.types.str;
default = "hermes-webui";
default = defaultUser;
description = "User that runs the Hermes WebUI service.";
};
group = lib.mkOption {
type = lib.types.str;
default = "hermes-webui";
default = defaultGroup;
description = "Group that runs the Hermes WebUI service.";
};
host = lib.mkOption {
type = lib.types.str;
default = "0.0.0.0";
default = "127.0.0.1";
description = "Value for HERMES_WEBUI_HOST.";
};
@@ -123,6 +125,12 @@ in
description = "Value for HERMES_WEBUI_PORT.";
};
openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Open the configured TCP port in the NixOS firewall.";
};
stateDir = lib.mkOption {
type = lib.types.strMatching "^/.+";
default = defaultStateDir;
@@ -140,7 +148,7 @@ in
package = lib.mkOption {
type = lib.types.nullOr lib.types.package;
default = null;
description = "Package to derive HERMES_WEBUI_AGENT_DIR from when `agent.dir` is unset.";
description = "Package to derive HERMES_WEBUI_AGENT_DIR and HERMES_WEBUI_PYTHON from when it exposes passthru.hermesVenv.";
};
dir = lib.mkOption {
@@ -148,6 +156,12 @@ in
default = null;
description = "Explicit path for HERMES_WEBUI_AGENT_DIR.";
};
python = lib.mkOption {
type = lib.types.nullOr (lib.types.strMatching "^/.+");
default = null;
description = "Explicit path for HERMES_WEBUI_PYTHON when the service must run with agent dependencies from a separately managed environment.";
};
};
environmentFiles = lib.mkOption {
@@ -187,13 +201,19 @@ in
};
};
users.groups.${cfg.group} = { };
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ];
users.users.${cfg.user} = {
isSystemUser = true;
group = cfg.group;
createHome = true;
home = cfg.stateDir;
users.groups = lib.mkIf (cfg.group == defaultGroup) {
${cfg.group} = { };
};
users.users = lib.mkIf (cfg.user == defaultUser) {
${cfg.user} = {
isSystemUser = true;
group = cfg.group;
createHome = true;
home = cfg.stateDir;
};
};
systemd.tmpfiles.rules = tmpfilesRules;
+3 -2
View File
@@ -27,10 +27,11 @@ pkgs.stdenv.mkDerivation {
cp "${./../server.py}" "$out/${runtimeDir}/server.py"
cp "${./../mcp_server.py}" "$out/${runtimeDir}/mcp_server.py"
cp "${./../requirements.txt}" "$out/${runtimeDir}/requirements.txt"
cp -r "${./../api}" "$out/${runtimeDir}/"
cp -r "${./../static}" "$out/${runtimeDir}/"
cp -r "${./../api}" "$out/${runtimeDir}/api"
cp -r "${./../static}" "$out/${runtimeDir}/static"
makeWrapper ${pythonEnv}/bin/python3 "$out/bin/hermes-webui" \
--set HERMES_WEBUI_DISABLE_LOCAL_VENV 1 \
--add-flags "$out/${runtimeDir}/bootstrap.py --foreground --no-browser --skip-agent-install"
runHook postInstall