diff --git a/hermes_cli/gateway.py b/hermes_cli/gateway.py index 5c9245889..70b0b7b27 100644 --- a/hermes_cli/gateway.py +++ b/hermes_cli/gateway.py @@ -258,8 +258,11 @@ def _system_service_identity(run_as_user: str | None = None) -> tuple[str, str, username = (run_as_user or os.getenv("SUDO_USER") or os.getenv("USER") or os.getenv("LOGNAME") or getpass.getuser()).strip() if not username: raise ValueError("Could not determine which user the gateway service should run as") + if username == "root" and not run_as_user: + raise ValueError("Refusing to install the gateway system service as root; pass --run-as-user root to override (e.g. in LXC containers)") if username == "root": - raise ValueError("Refusing to install the gateway system service as root; pass --run-as USER") + print_warning("Installing gateway service to run as root.") + print_info(" This is fine for LXC/container environments but not recommended on bare-metal hosts.") try: user_info = pwd.getpwnam(username) @@ -321,9 +324,9 @@ def install_linux_gateway_from_setup(force: bool = False) -> tuple[str | None, b while True: run_as_user = prompt(" Run the system gateway service as which user?", default="") run_as_user = (run_as_user or "").strip() - if run_as_user and run_as_user != "root": + if run_as_user: break - print_error(" Enter a non-root username.") + print_error(" Enter a username.") systemd_install(force=force, system=True, run_as_user=run_as_user) return scope, True diff --git a/tests/hermes_cli/test_gateway_service.py b/tests/hermes_cli/test_gateway_service.py index 06a1cd72c..21c70c589 100644 --- a/tests/hermes_cli/test_gateway_service.py +++ b/tests/hermes_cli/test_gateway_service.py @@ -466,6 +466,51 @@ class TestGeneratedUnitIncludesLocalBin: assert "/.local/bin" in unit +class TestSystemServiceIdentityRootHandling: + """Root user handling in _system_service_identity().""" + + def test_auto_detected_root_is_rejected(self, monkeypatch): + """When root is auto-detected (not explicitly requested), raise.""" + import pwd + import grp + + monkeypatch.delenv("SUDO_USER", raising=False) + monkeypatch.setenv("USER", "root") + monkeypatch.setenv("LOGNAME", "root") + + import pytest + with pytest.raises(ValueError, match="pass --run-as-user root to override"): + gateway_cli._system_service_identity(run_as_user=None) + + def test_explicit_root_is_allowed(self, monkeypatch): + """When root is explicitly passed via --run-as-user root, allow it.""" + import pwd + import grp + + root_info = pwd.getpwnam("root") + root_group = grp.getgrgid(root_info.pw_gid).gr_name + + username, group, home = gateway_cli._system_service_identity(run_as_user="root") + assert username == "root" + assert home == root_info.pw_dir + + def test_non_root_user_passes_through(self, monkeypatch): + """Normal non-root user works as before.""" + import pwd + import grp + + monkeypatch.delenv("SUDO_USER", raising=False) + monkeypatch.setenv("USER", "nobody") + monkeypatch.setenv("LOGNAME", "nobody") + + try: + username, group, home = gateway_cli._system_service_identity(run_as_user=None) + assert username == "nobody" + except ValueError as e: + # "nobody" might not exist on all systems + assert "Unknown user" in str(e) + + class TestEnsureUserSystemdEnv: """Tests for _ensure_user_systemd_env() D-Bus session bus auto-detection."""