From a4ee3b3063c92aa9997d1778756ac32cbe3be4f8 Mon Sep 17 00:00:00 2001 From: Brian Gebel Date: Thu, 12 Mar 2026 04:15:46 -0700 Subject: [PATCH] feat: harden image for arbitrary UID support across all environments Comprehensive fixes for running as any non-root UID (Kubernetes, OpenShift, Docker Compose PUID/PGID): Dockerfile (core): - /etc/passwd and /etc/group world-writable for runtime injection - /app/.venv/bin writable for pip entry point scripts - /app and /app/ComfyUI writable for startup dir creation - Dynamic Python version detection (no hardcoded python3.12) - PYTHONDONTWRITEBYTECODE=1 to suppress .pyc permission failures - SHELL pipefail in final stage (not inherited across FROM) Dockerfile (complete): - Re-apply world-writable permissions after pip install layer - SHELL pipefail for safety - Dynamic Python version detection Entrypoint: - Inject both /etc/passwd and /etc/group for arbitrary UIDs - Use getent passwd instead of whoami for reliable UID detection - Use unique username (comfyuser) to avoid collision with build-time comfy user at UID 1000 Co-Authored-By: Claude Opus 4.6 --- .../complete/dockerfile.comfy.cuda.complete | 13 ++++++--- services/comfy/core/dockerfile.comfy.core | 27 ++++++++++++++----- services/comfy/core/entrypoint.sh | 17 +++++++----- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/services/comfy/complete/dockerfile.comfy.cuda.complete b/services/comfy/complete/dockerfile.comfy.cuda.complete index 1311e25..86145d4 100644 --- a/services/comfy/complete/dockerfile.comfy.cuda.complete +++ b/services/comfy/complete/dockerfile.comfy.cuda.complete @@ -1,13 +1,20 @@ FROM core AS complete +# Set shell to bash with pipefail for safety +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + # Copy the extra requirements file COPY --chown=comfy:comfy ./extra-requirements.txt ./ -# Set shell to bash to support 'source' -SHELL ["/bin/bash", "-c"] - # Install additional python requirements # Using the same cache path as the core image RUN --mount=type=cache,mode=0755,uid=1000,gid=1000,target=/app/.cache/pip \ source $VENV_PATH/bin/activate && \ pip install -r extra-requirements.txt + +# Re-apply world-writable permissions after installing extra packages. +# The core stage's chmod doesn't cover files added in this layer — newly +# installed packages and entry point scripts would be root-owned 644/755. +RUN PYTHON_VERSION=$(python3 -c "import sys; print(f'python{sys.version_info.major}.{sys.version_info.minor}')") && \ + chmod -R a+w /app/.venv/lib/${PYTHON_VERSION}/site-packages && \ + chmod a+w /app/.venv/bin diff --git a/services/comfy/core/dockerfile.comfy.core b/services/comfy/core/dockerfile.comfy.core index f760585..7a12d10 100644 --- a/services/comfy/core/dockerfile.comfy.core +++ b/services/comfy/core/dockerfile.comfy.core @@ -33,6 +33,9 @@ RUN --mount=type=cache,target=/app/.cache/pip \ # ================================================================== FROM runtime AS core +# Set shell to bash with pipefail for safety (not inherited across FROM) +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + # Remove default ubuntu user (UID 1000) if it exists to prevent conflict # https://askubuntu.com/questions/1513927/ RUN touch /var/mail/ubuntu && \ @@ -58,6 +61,10 @@ ENV PYTHONPATH="/app/ComfyUI" ENV HOME=/app ENV XDG_CACHE_HOME=/app/.cache +# Suppress .pyc writes — avoids silent permission failures when running +# as an arbitrary UID that can't write to source directories +ENV PYTHONDONTWRITEBYTECODE=1 + # Copy startup scripts to app root COPY --chown=comfy:comfy startup.sh entrypoint.sh /app/ @@ -65,15 +72,23 @@ COPY --chown=comfy:comfy startup.sh entrypoint.sh /app/ RUN chmod +x /app/startup.sh /app/entrypoint.sh # Make directories writable by any user for arbitrary UID support -# (Docker Compose PUID/PGID, K8s securityContext.runAsUser, etc.) +# (Docker Compose PUID/PGID, K8s securityContext.runAsUser, OpenShift, etc.) +# +# Detect the Python version dynamically to avoid hardcoding python3.12 +# Writable targets: # - site-packages: ComfyUI Manager installs custom node deps at runtime +# - venv/bin: pip-installed packages create entry point scripts here # - .cache: uv/pip cache operations -# - /etc/passwd: entrypoint injects passwd entry for arbitrary UIDs -# so getpwuid(), getpass.getuser(), and expanduser("~") work -RUN chmod -R a+w /app/.venv/lib/python3.12/site-packages && \ - chmod a+w /app/.venv/lib/python3.12 && \ +# - ComfyUI data dirs: ComfyUI creates these at startup if missing +# - /etc/passwd, /etc/group: entrypoint injects entries for arbitrary UIDs +# so getpwuid(), getgrgid(), getpass.getuser(), expanduser("~") all work +RUN PYTHON_VERSION=$(python3 -c "import sys; print(f'python{sys.version_info.major}.{sys.version_info.minor}')") && \ + chmod -R a+w /app/.venv/lib/${PYTHON_VERSION}/site-packages && \ + chmod a+w /app/.venv/lib/${PYTHON_VERSION} && \ + chmod a+w /app/.venv/bin && \ mkdir -p /app/.cache && chmod -R a+w /app/.cache && \ - chmod g+w /etc/passwd + chmod a+w /app /app/ComfyUI && \ + chmod a+w /etc/passwd /etc/group # Environment variables ARG CLI_ARGS="" diff --git a/services/comfy/core/entrypoint.sh b/services/comfy/core/entrypoint.sh index 3d0a274..441c194 100755 --- a/services/comfy/core/entrypoint.sh +++ b/services/comfy/core/entrypoint.sh @@ -10,13 +10,16 @@ set -e if [ "$(id -u)" -ne 0 ]; then echo "Starting as non-root UID:GID = $(id -u):$(id -g)" - # Inject a passwd entry for the current UID if one doesn't exist. - # Many tools (Python's getpass.getuser(), PyTorch's cache_dir, - # os.path.expanduser) call getpwuid() which fails with KeyError - # for UIDs not in /etc/passwd. This is standard practice for - # containers supporting arbitrary UIDs (e.g., OpenShift). - if ! whoami &>/dev/null 2>&1; then - echo "comfy:x:$(id -u):$(id -g):ComfyUI User:/app:/bin/bash" >> /etc/passwd + # Inject /etc/passwd and /etc/group entries for the current UID/GID + # if they don't already exist. Many tools depend on these lookups: + # - Python: getpass.getuser(), os.path.expanduser("~"), grp.getgrgid() + # - PyTorch: cache_dir resolution via getpwuid() + # This is standard practice for arbitrary UID containers (OpenShift, etc.) + if ! getent passwd "$(id -u)" &>/dev/null; then + echo "comfyuser:x:$(id -u):$(id -g):ComfyUI User:/app:/bin/bash" >> /etc/passwd + fi + if ! getent group "$(id -g)" &>/dev/null; then + echo "comfygroup:x:$(id -g):" >> /etc/group fi source /app/.venv/bin/activate