build: download nltk data somewhere a non-root UID can read

`nltk.download` targets the first entry of `nltk.data.path` that already exists
and is writable. None of the system-wide candidates exist in this image, so it
falls back to `$HOME/nltk_data` -- `/root/nltk_data`, inside a directory that is
mode 0700. The corpus itself is world-readable; the parent is what blocks it.

So the baked `punkt_tab` is unreachable for any run that is not root, including
`docker run --user`, `runAsNonRoot` and OpenShift's restricted SCC:

    $ docker run --rm --user 1002720000:0 open-webui:v0.11.0 \
        python -c "import nltk; nltk.data.find('tokenizers/punkt_tab')"
    LookupError: Resource punkt_tab not found.

`/usr/local/share/nltk_data` is already on `nltk.data.path`, so pointing the
download there needs no NLTK_DATA and no change at the read side. Verified as
`--user 1002720000:0`, where `nltk.data.find` now resolves it.

Scope, to be straight about it: I could not find anything that currently loads
this corpus -- `unstructured` 0.22.31 uses spaCy, and `retrieval.py` instantiates
only the Markdown, RecursiveCharacter and Token splitters. So this makes 15 MB
that ships either way actually usable, rather than fixing a reported failure.
Happy to send a patch that drops the download instead if you prefer the smaller
image. (`USE_PERMISSION_HARDENING=true` already fixes this for that build; no
published tag sets it.)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sebastian Danielsson 2026-08-20 12:49:42 +02:00
parent 8a42aa53e8
commit dd8786f414
No known key found for this signature in database

View file

@ -149,7 +149,9 @@ RUN set -e; \
python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ.get('AUXILIARY_EMBEDDING_MODEL', 'TaylorAI/bge-micro-v2'), device='cpu')"; \
python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])"; \
python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \
python -c "import nltk; nltk.download('punkt_tab')"; \
# Without an explicit dir this lands in /root/nltk_data, which is mode 0700
# and so unreadable when the container does not run as root.
python -c "import nltk; nltk.download('punkt_tab', download_dir='/usr/local/share/nltk_data')"; \
else \
pip3 install 'torch<=2.9.1' torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir; \
uv pip install --system -r requirements.txt --no-cache-dir; \
@ -158,7 +160,9 @@ RUN set -e; \
python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ.get('AUXILIARY_EMBEDDING_MODEL', 'TaylorAI/bge-micro-v2'), device='cpu')"; \
python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])"; \
python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \
python -c "import nltk; nltk.download('punkt_tab')"; \
# Without an explicit dir this lands in /root/nltk_data, which is mode 0700
# and so unreadable when the container does not run as root.
python -c "import nltk; nltk.download('punkt_tab', download_dir='/usr/local/share/nltk_data')"; \
fi; \
fi; \
mkdir -p /app/backend/data; chown -R $UID:$GID /app/backend/data/; \