diff --git a/Dockerfile b/Dockerfile index 2de6192715..37070bd991 100644 --- a/Dockerfile +++ b/Dockerfile @@ -298,7 +298,7 @@ RUN uv pip install --no-cache-dir --no-deps -e "." USER root RUN mkdir -p /opt/hermes/bin && \ cp /opt/hermes/docker/hermes-exec-shim.sh /opt/hermes/bin/hermes && \ - chmod 0755 /opt/hermes/bin/hermes && \ + chmod 0755 /opt/hermes /opt/hermes/bin/hermes && \ printf 'docker\n' > /opt/hermes/.install_method # The ``.install_method`` stamp is baked next to the running code (the install # tree), NOT into $HERMES_HOME. $HERMES_HOME (/opt/data) is a shared data diff --git a/hermes_constants.py b/hermes_constants.py index e7af188397..9b6c08a156 100644 --- a/hermes_constants.py +++ b/hermes_constants.py @@ -1023,6 +1023,15 @@ def secure_parent_dir(path: Path) -> None: # Refuse root and its direct children (/usr, /home, /var, /tmp, …). if parent == Path("/") or len(parent.parts) < 3: return + # Refuse /opt/hermes. The install dir lives on the image layer; + # chmodding it to 0700 breaks hermes-user traversal and produces + # spurious "Permission denied" on every new exec until manual + # `chmod 0755 /opt/hermes`. Reproducer: any auth write to a file + # directly under /opt/hermes (e.g. /opt/hermes/auth.json when + # HERMES_HOME resolves there) triggers the 0700 chmod and locks + # out UID 10000. See issue #25821 follow-up. + if str(parent) == "/opt/hermes": + return try: os.chmod(parent, 0o700) except OSError: