Skip to content

Commit 8712c34

Browse files
authored
fix: allow running gateway service as root for LXC/container environments (NousResearch#4732)
Previously, `hermes gateway install --system` hard-refused to create a service running as root, even when explicitly requested via `--run-as-user root`. This forced LXC/container users (where root is the only user) to either create throwaway users or comment out the check in source. Changes: - Auto-detected root (no explicit --run-as-user) still raises, but with a message explaining how to override - Explicit `--run-as-user root` now allowed with a warning about security implications - Interactive setup wizard prompt accepts 'root' as a valid username (warning comes from _system_service_identity downstream) - Added tests for all three paths: auto-detected root rejection, explicit root allowance, and normal non-root passthrough
1 parent 616ac92 commit 8712c34

2 files changed

Lines changed: 51 additions & 3 deletions

File tree

hermes_cli/gateway.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,11 @@ def _system_service_identity(run_as_user: str | None = None) -> tuple[str, str,
258258
username = (run_as_user or os.getenv("SUDO_USER") or os.getenv("USER") or os.getenv("LOGNAME") or getpass.getuser()).strip()
259259
if not username:
260260
raise ValueError("Could not determine which user the gateway service should run as")
261+
if username == "root" and not run_as_user:
262+
raise ValueError("Refusing to install the gateway system service as root; pass --run-as-user root to override (e.g. in LXC containers)")
261263
if username == "root":
262-
raise ValueError("Refusing to install the gateway system service as root; pass --run-as USER")
264+
print_warning("Installing gateway service to run as root.")
265+
print_info(" This is fine for LXC/container environments but not recommended on bare-metal hosts.")
263266

264267
try:
265268
user_info = pwd.getpwnam(username)
@@ -321,9 +324,9 @@ def install_linux_gateway_from_setup(force: bool = False) -> tuple[str | None, b
321324
while True:
322325
run_as_user = prompt(" Run the system gateway service as which user?", default="")
323326
run_as_user = (run_as_user or "").strip()
324-
if run_as_user and run_as_user != "root":
327+
if run_as_user:
325328
break
326-
print_error(" Enter a non-root username.")
329+
print_error(" Enter a username.")
327330

328331
systemd_install(force=force, system=True, run_as_user=run_as_user)
329332
return scope, True

tests/hermes_cli/test_gateway_service.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,51 @@ def test_system_unit_includes_local_bin_in_path(self):
466466
assert "/.local/bin" in unit
467467

468468

469+
class TestSystemServiceIdentityRootHandling:
470+
"""Root user handling in _system_service_identity()."""
471+
472+
def test_auto_detected_root_is_rejected(self, monkeypatch):
473+
"""When root is auto-detected (not explicitly requested), raise."""
474+
import pwd
475+
import grp
476+
477+
monkeypatch.delenv("SUDO_USER", raising=False)
478+
monkeypatch.setenv("USER", "root")
479+
monkeypatch.setenv("LOGNAME", "root")
480+
481+
import pytest
482+
with pytest.raises(ValueError, match="pass --run-as-user root to override"):
483+
gateway_cli._system_service_identity(run_as_user=None)
484+
485+
def test_explicit_root_is_allowed(self, monkeypatch):
486+
"""When root is explicitly passed via --run-as-user root, allow it."""
487+
import pwd
488+
import grp
489+
490+
root_info = pwd.getpwnam("root")
491+
root_group = grp.getgrgid(root_info.pw_gid).gr_name
492+
493+
username, group, home = gateway_cli._system_service_identity(run_as_user="root")
494+
assert username == "root"
495+
assert home == root_info.pw_dir
496+
497+
def test_non_root_user_passes_through(self, monkeypatch):
498+
"""Normal non-root user works as before."""
499+
import pwd
500+
import grp
501+
502+
monkeypatch.delenv("SUDO_USER", raising=False)
503+
monkeypatch.setenv("USER", "nobody")
504+
monkeypatch.setenv("LOGNAME", "nobody")
505+
506+
try:
507+
username, group, home = gateway_cli._system_service_identity(run_as_user=None)
508+
assert username == "nobody"
509+
except ValueError as e:
510+
# "nobody" might not exist on all systems
511+
assert "Unknown user" in str(e)
512+
513+
469514
class TestEnsureUserSystemdEnv:
470515
"""Tests for _ensure_user_systemd_env() D-Bus session bus auto-detection."""
471516

0 commit comments

Comments
 (0)