fix(docker): salvage operational hardening from #1686 (env readonly + apt deps)
Three independent operational hardening fixes salvaged from PR #1686 (@binhpt310) after the parent PR was deferred over a separate sibling-repo build-context concern unrelated to these fixes: 1. start.sh's .env loader now filters readonly bash vars (UID, GID, EUID, EGID, PPID) before `source`-ing. docker-compose.yml's macOS instructions document `echo "UID=$(id -u)" >> .env` to set host UID/GID for bind-mount permission fixing — that .env was crashing start.sh with `UID: readonly variable` when `set -a; source ...; set +a` tried to assign to those names. Replaced with `source <(grep -vE '^[[:space:]]*(export[[:space:]]+)?(UID|GID|EUID|EGID|PPID)=' "${REPO_ROOT}/.env")`. The bootstrap regression guard at tests/test_bootstrap_dotenv.py:181 still passes — both `source` and `.env` are still on the modified line. 2. start.sh now defensively re-execs as the unprivileged hermeswebui user when invoked as root. Fires only when EUID==0 AND a hermeswebui user actually exists AND sudo is on PATH — so it's a no-op on host machines without the container user setup. The production image's entrypoint (docker_init.bash) already drops to hermeswebui before invoking start.sh, so this is a no-op on the canonical container path; it only matters for `sudo ./start.sh` or accidental root shells inside the container during interactive debugging. 3. Dockerfile installs xz-utils + git apt packages. xz-utils is required to decompress .tar.xz archives (e.g. Node.js distribution tarballs); git is needed for `git describe` (powers WEBUI_VERSION resolution at api/updates.py:_detect_webui_version) and any clone-based agent install path. Both are tiny apt packages on top of python:3.12-slim with no measurable image-size impact. What's NOT in this commit (deferred from #1686): - Pre-baking hermes-agent source into the image via `COPY hermes-agent-desktop/hermes-agent /opt/hermes/` plus a build-context flip to `..`. Requires a sibling-repo layout that breaks the canonical `git clone hermes-webui && cd hermes-webui && docker compose build` flow. The right shape is a build arg gating the COPY behind --build-arg WITH_AGENT_SOURCE=1; left to a separate PR. - Pre-installing Node.js 22 LTS system-wide. Real motivation but worth evaluating the fix shape (full Node bake vs. opt-in vs. layer cache) separately from these three operational fixes. Tests: tests/test_docker_env_readonly_vars.py — 11 tests (4 source-grep on the start.sh filter pattern + 5 behavioral that actually run bash against synthetic .env files containing readonly vars + 2 Dockerfile package-presence tests). All 11 pass. Behavioral tests skip if bash is not on PATH. Full suite: 5028 → 5036 passing (+8 net new after pytest collection counted some behavioral tests under skip), 0 regressions, 147.84s. Closes the operational-hardening portion of #1686. Co-authored-by: binhpt310 <binhpt310@users.noreply.github.com>
This commit is contained in:
1 parent
8a653bac20
commit
57c71e89f3
3 files changed
+271
-1
No files matched your search
@@ -1,12 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# If invoked as root (e.g. via `sudo ./start.sh` or accidental root shell
|
||||
# inside the container), re-exec as the unprivileged hermeswebui user so the
|
||||
# WebUI process never owns root-only file modes on bind-mounted state.
|
||||
# Outside containers the EUID==0 case is rare; inside the production image
|
||||
# the entrypoint drops to hermeswebui itself, so this is a defensive guard.
|
||||
# Sourced from PR #1686 (@binhpt310) — Cluster 1 (operational hardening),
|
||||
# extracted to a focused follow-up after the parent PR was deferred over a
|
||||
# separate sibling-repo build-context concern unrelated to this fix.
|
||||
if [[ ${EUID:-$(id -u)} -eq 0 ]] && id hermeswebui >/dev/null 2>&1 \
|
||||
&& command -v sudo >/dev/null 2>&1; then
|
||||
exec sudo -n -u hermeswebui "$0" "$@"
|
||||
fi
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
if [[ -f "${REPO_ROOT}/.env" ]]; then
|
||||
# Filter out shell-readonly vars (UID, GID, EUID, EGID, PPID) before
|
||||
# `source`ing. docker-compose.yml's macOS instructions document
|
||||
# `echo "UID=$(id -u)" >> .env` to set host UID/GID, which then crashes
|
||||
# `start.sh` with "UID: readonly variable" when bash tries to assign to
|
||||
# those names. Filtering them out lets the .env file carry those entries
|
||||
# for docker-compose's variable substitution while keeping local invocation
|
||||
# of start.sh working. The regression guard at
|
||||
# tests/test_bootstrap_dotenv.py:181 still passes — the line below contains
|
||||
# both `source` and `.env`.
|
||||
# Sourced from PR #1686 (@binhpt310) — Cluster 1 (operational hardening),
|
||||
# extracted to a focused follow-up after the parent PR was deferred.
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
source "${REPO_ROOT}/.env"
|
||||
source <(grep -vE '^[[:space:]]*(export[[:space:]]+)?(UID|GID|EUID|EGID|PPID)=' "${REPO_ROOT}/.env")
|
||||
set +a
|
||||
fi
|
||||
|
||||
|
||||
Reference in new issue
Block a user