issue: Make image arbitrary-UID friendly (OpenShift/Restricted SCC): writable HOME + group-writable app/data dirs #6082

Closed
opened 2025-11-11 16:44:24 -06:00 by GiteaMirror · 1 comment
Owner

Originally created by @SebLz on GitHub (Aug 13, 2025).

Check Existing Issues

  • I have searched the existing issues and discussions.
  • I am using the latest version of Open WebUI.

Installation Method

Other

Open WebUI Version

v0.6.22

Ollama Version (if applicable)

No response

Operating System

RHEL / OpenSHift

Browser (if applicable)

No response

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of both Open WebUI and Ollama.
  • I have included the browser console logs.
  • I have included the Docker container logs.
  • I have provided every relevant configuration, setting, and environment variable used in my setup.
  • I have clearly listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
  • I have documented step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation. My steps:
  • Start with the initial platform/version/OS and dependencies used,
  • Specify exact install/launch/configure commands,
  • List URLs visited, user input (incl. example values/emails/passwords if needed),
  • Describe all options and toggles enabled or changed,
  • Include any files or environmental changes,
  • Identify the expected and actual result at each stage,
  • Ensure any reasonably skilled user can follow and hit the same issue.

Expected Behavior

Summary

Running the official ghcr.io/open-webui/open-webui image on OpenShift with the restricted SCC fails when the app (or Python libs like transformers/tiktoken) try to write outside /app/backend/data (e.g., to $HOME or cache dirs). OpenShift injects a random non-root UID (e.g., 1000790000) and supplemental group 0, so any path not group-writable by GID 0 becomes read-only. The image mostly works because /app/backend/data is writable, but $HOME and other paths under /app default to root:root 755.

This is a standard “arbitrary UID” compatibility issue. A small Dockerfile hardening (least privilege) fixes it without breaking existing usage and improves security by avoiding reliance on a fixed user.

A very similar fix was recently implemented for LiteLLM: https://github.com/BerriAI/litellm/issues/13208


Environment

  • Platform: OpenShift 4.x (restricted SCC)
  • Image: ghcr.io/open-webui/open-webui (e.g., v0.6.18)
  • Storage: PVC mounted at /app/backend/data

Observed inside the running pod

$ id
uid=1000790000(1000790000) gid=0(root) groups=0(root),1000790000

$ pwd
/app/backend

$ ls -alh
total 32K
drwxr-xr-x.  1 root root        142 Jul 19 19:26 .
drwxr-xr-x.  1 root root         21 Jul 19 19:30 ..
drwxrws--x.  5 root 1000790000 4.0K Aug 13 14:30 data   # writable (SGID + g+rw)
drwxr-xr-x. 13 root root       4.0K Jul 19 19:26 open_webui
-rwxr-xr-x.  1 root root       2.7K Jul 19 19:26 start.sh
...
  • The process runs as random UID with supplemental GID 0 (root group).
  • /app/backend/data is correctly group-writable (works).
  • Many other paths (including $HOME=/root) are root:root 755permission denied when libraries write caches there.
    ex:
    ERROR [root] An error occurred: [Errno 13] Permission denied: '/app/backend/open_webui/static/apple-touch-icon.png'

Expected

  • The container should be writable wherever the app and its dependencies expect to write (HOME and cache dirs), without requiring root and without baking in a specific UID. This is the recommended “arbitrary UID” pattern for OpenShift/K8s hardened clusters.

Actual

  • Writes outside /app/backend/data fail with EACCES unless users rebuild or wrap the container.

Root cause

OpenShift injects a random UID and keeps GID 0 as a supplemental group. If the image’s writable paths are group-owned by 0 and g+rwX, it “just works.” Today, the image sets many files to root:root 755, so $HOME and some caches are not writable.


Proposed fix (minimal, backwards-compatible)

Make the image arbitrary-UID friendly:

  • Ensure /app and /root are group 0 and group-writable.
  • Ensure the data dir is group-writable and has SGID so newly created files inherit group 0.
  • Do not set a fixed USER in the final image; let the platform (OpenShift restricted SCC) inject a safe UID at runtime.

Dockerfile patch (unified diff)

diff --git a/Dockerfile b/Dockerfile
index 0000000..1111111 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,6 @@
 # ... existing stages omitted for brevity
 
 FROM python:3.11-slim-bookworm AS base
+ARG OIDATA=/app/backend/data
 # existing ARGs/ENVs...
 
 WORKDIR /app/backend
@@ -120,6 +121,22 @@ COPY --chown=$UID:$GID --from=build /app/CHANGELOG.md /app/CHANGELOG.md
 COPY --chown=$UID:$GID --from=build /app/package.json /app/package.json
 # copy backend files
 COPY --chown=$UID:$GID ./backend .
 
+# --- Make image arbitrary-UID friendly (OpenShift/K8s hardened) ---
+# Any random UID with supplemental GID 0 can write under these paths.
+RUN set -eux; \
+    mkdir -p "$OIDATA"; \
+    # 1) App & home must be group 0 and group-writable
+    chgrp -R 0 /app /root "$OIDATA"; \
+    chmod -R g+rwX /app /root "$OIDATA"; \
+    # 2) Keep group bit on directories so new files inherit group 0
+    find /app -type d -exec chmod g+s {} +; \
+    find "$OIDATA" -type d -exec chmod g+s {} +; \
+    # Helpful no-op if chroma cache path doesn't exist yet:
+    mkdir -p /root/.cache || true
+
 EXPOSE 8080
@@ -140,7 +157,10 @@ HEALTHCHECK CMD curl --silent --fail http://localhost:${PORT:-8080}/health | jq
-USER $UID:$GID
+# IMPORTANT: Do not set a fixed USER.
+# OpenShift restricted SCC injects a safe random UID; setting USER here
+# can conflict with the cluster’s MustRunAsRange policy.
+# USER $UID:$GID
 
 ARG BUILD_HASH
 ENV WEBUI_BUILD_VERSION=${BUILD_HASH}
 ENV DOCKER=true

Notes on compatibility/security

  • This does not grant extra privileges; it simply aligns ownership/perms with the supplemental GID 0 that restricted clusters already provide.
  • No behavioral changes for users running on plain Docker/Kubernetes.
  • Avoiding a fixed USER prevents conflicts with OpenShift’s MustRunAsRange and is the standard way to support arbitrary UIDs.
  • Using g+s keeps new files in group 0, so subsequent restarts/new random UIDs continue to have write access.

(Optional) Nice-to-have: writable HOME on PVC

If you want to be even stricter about not touching /root, you can set:

ENV HOME=/app/backend/data/home
RUN mkdir -p /app/backend/data/home && chgrp -R 0 /app/backend/data/home && chmod -R g+rwX /app/backend/data/home

This keeps all caches under the mounted data volume. (The current image already exposes cache env vars like HF_HOME, SENTENCE_TRANSFORMERS_HOME, TIKTOKEN_CACHE_DIR, etc., so this is mostly a quality-of-life improvement.)


Ask

  1. Merge the Dockerfile changes above (or equivalent) so the image is arbitrary-UID compatible out of the box.

  2. Optionally document an “OpenShift / Restricted SCC” section that mentions:

    • No fixed USER in image
    • Group-0 + g+rwX on /app, /root, and /app/backend/data
    • Optional HOME=/app/backend/data/home

Actual Behavior

cf. above

Steps to Reproduce

cf. above

Logs & Screenshots

cf. above

Additional Information

No response

Originally created by @SebLz on GitHub (Aug 13, 2025). ### Check Existing Issues - [x] I have searched the existing issues and discussions. - [x] I am using the latest version of Open WebUI. ### Installation Method Other ### Open WebUI Version v0.6.22 ### Ollama Version (if applicable) _No response_ ### Operating System RHEL / OpenSHift ### Browser (if applicable) _No response_ ### Confirmation - [x] I have read and followed all instructions in `README.md`. - [x] I am using the latest version of **both** Open WebUI and Ollama. - [x] I have included the browser console logs. - [x] I have included the Docker container logs. - [x] I have **provided every relevant configuration, setting, and environment variable used in my setup.** - [x] I have clearly **listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup** (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc). - [x] I have documented **step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation**. My steps: - Start with the initial platform/version/OS and dependencies used, - Specify exact install/launch/configure commands, - List URLs visited, user input (incl. example values/emails/passwords if needed), - Describe all options and toggles enabled or changed, - Include any files or environmental changes, - Identify the expected and actual result at each stage, - Ensure any reasonably skilled user can follow and hit the same issue. ### Expected Behavior **Summary** Running the official `ghcr.io/open-webui/open-webui` image on OpenShift with the **restricted** SCC fails when the app (or Python libs like transformers/tiktoken) try to write outside `/app/backend/data` (e.g., to `$HOME` or cache dirs). OpenShift injects a **random non-root UID** (e.g., `1000790000`) and **supplemental group 0**, so any path not group-writable by GID 0 becomes read-only. The image mostly works because `/app/backend/data` is writable, but `$HOME` and other paths under `/app` default to `root:root 755`. This is a standard “arbitrary UID” compatibility issue. A small Dockerfile hardening (least privilege) fixes it without breaking existing usage and **improves security** by avoiding reliance on a fixed user. A very similar fix was recently implemented for LiteLLM: [https://github.com/BerriAI/litellm/issues/13208](https://github.com/BerriAI/litellm/issues/13208) --- **Environment** * Platform: OpenShift 4.x (restricted SCC) * Image: `ghcr.io/open-webui/open-webui` (e.g., v0.6.18) * Storage: PVC mounted at `/app/backend/data` **Observed inside the running pod** ```bash $ id uid=1000790000(1000790000) gid=0(root) groups=0(root),1000790000 $ pwd /app/backend $ ls -alh total 32K drwxr-xr-x. 1 root root 142 Jul 19 19:26 . drwxr-xr-x. 1 root root 21 Jul 19 19:30 .. drwxrws--x. 5 root 1000790000 4.0K Aug 13 14:30 data # writable (SGID + g+rw) drwxr-xr-x. 13 root root 4.0K Jul 19 19:26 open_webui -rwxr-xr-x. 1 root root 2.7K Jul 19 19:26 start.sh ... ``` * The process runs as **random UID** with **supplemental GID 0** (root group). * `/app/backend/data` is correctly group-writable (works). * Many other paths (including `$HOME=/root`) are `root:root 755` → **permission denied** when libraries write caches there. ex: `ERROR [root] An error occurred: [Errno 13] Permission denied: '/app/backend/open_webui/static/apple-touch-icon.png'` **Expected** * The container should be writable wherever the app and its dependencies expect to write (HOME and cache dirs), **without requiring root** and **without baking in a specific UID**. This is the recommended “arbitrary UID” pattern for OpenShift/K8s hardened clusters. **Actual** * Writes outside `/app/backend/data` fail with EACCES unless users rebuild or wrap the container. --- ## Root cause OpenShift injects a random UID and keeps **GID 0** as a supplemental group. If the image’s writable paths are **group-owned by 0 and g+rwX**, it “just works.” Today, the image sets many files to `root:root 755`, so `$HOME` and some caches are not writable. --- ## Proposed fix (minimal, backwards-compatible) Make the image **arbitrary-UID friendly**: * Ensure `/app` and `/root` are **group 0** and **group-writable**. * Ensure the **data dir** is group-writable and has **SGID** so newly created files inherit group 0. * **Do not set a fixed `USER`** in the final image; let the platform (OpenShift restricted SCC) inject a safe UID at runtime. ### Dockerfile patch (unified diff) ```diff diff --git a/Dockerfile b/Dockerfile index 0000000..1111111 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,6 @@ # ... existing stages omitted for brevity FROM python:3.11-slim-bookworm AS base +ARG OIDATA=/app/backend/data # existing ARGs/ENVs... WORKDIR /app/backend @@ -120,6 +121,22 @@ COPY --chown=$UID:$GID --from=build /app/CHANGELOG.md /app/CHANGELOG.md COPY --chown=$UID:$GID --from=build /app/package.json /app/package.json # copy backend files COPY --chown=$UID:$GID ./backend . +# --- Make image arbitrary-UID friendly (OpenShift/K8s hardened) --- +# Any random UID with supplemental GID 0 can write under these paths. +RUN set -eux; \ + mkdir -p "$OIDATA"; \ + # 1) App & home must be group 0 and group-writable + chgrp -R 0 /app /root "$OIDATA"; \ + chmod -R g+rwX /app /root "$OIDATA"; \ + # 2) Keep group bit on directories so new files inherit group 0 + find /app -type d -exec chmod g+s {} +; \ + find "$OIDATA" -type d -exec chmod g+s {} +; \ + # Helpful no-op if chroma cache path doesn't exist yet: + mkdir -p /root/.cache || true + EXPOSE 8080 @@ -140,7 +157,10 @@ HEALTHCHECK CMD curl --silent --fail http://localhost:${PORT:-8080}/health | jq -USER $UID:$GID +# IMPORTANT: Do not set a fixed USER. +# OpenShift restricted SCC injects a safe random UID; setting USER here +# can conflict with the cluster’s MustRunAsRange policy. +# USER $UID:$GID ARG BUILD_HASH ENV WEBUI_BUILD_VERSION=${BUILD_HASH} ENV DOCKER=true ``` **Notes on compatibility/security** * This does **not** grant extra privileges; it simply aligns ownership/perms with the **supplemental GID 0** that restricted clusters already provide. * No behavioral changes for users running on plain Docker/Kubernetes. * Avoiding a fixed `USER` prevents conflicts with OpenShift’s `MustRunAsRange` and is the standard way to support arbitrary UIDs. * Using `g+s` keeps new files in group 0, so subsequent restarts/new random UIDs continue to have write access. --- ## (Optional) Nice-to-have: writable HOME on PVC If you want to be even stricter about not touching `/root`, you can set: ```dockerfile ENV HOME=/app/backend/data/home RUN mkdir -p /app/backend/data/home && chgrp -R 0 /app/backend/data/home && chmod -R g+rwX /app/backend/data/home ``` This keeps all caches under the mounted data volume. (The current image already exposes cache env vars like `HF_HOME`, `SENTENCE_TRANSFORMERS_HOME`, `TIKTOKEN_CACHE_DIR`, etc., so this is mostly a quality-of-life improvement.) --- **Ask** 1. Merge the Dockerfile changes above (or equivalent) so the image is **arbitrary-UID compatible** out of the box. 2. Optionally document an “OpenShift / Restricted SCC” section that mentions: * No fixed `USER` in image * Group-0 + `g+rwX` on `/app`, `/root`, and `/app/backend/data` * Optional `HOME=/app/backend/data/home` ### Actual Behavior cf. above ### Steps to Reproduce cf. above ### Logs & Screenshots cf. above ### Additional Information _No response_
GiteaMirror added the bug label 2025-11-11 16:44:24 -06:00
Author
Owner

@tjbck commented on GitHub (Aug 13, 2025):

PR welcome

@tjbck commented on GitHub (Aug 13, 2025): PR welcome
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#6082