IIIF-Studio / Dockerfile
Claude
fix(security): Sprint 1 β€” path traversal, SSRF, Docker hardening, error exposure
b986b08 unverified
# IIIF Studio β€” image de production (multi-stage)
# Ce fichier est utilisΓ© par HuggingFace Spaces (SDK docker, dΓ©tection automatique).
# Source unique β€” le fichier infra/Dockerfile a Γ©tΓ© supprimΓ© pour Γ©viter la divergence.
#
# Build depuis la racine du dΓ©pΓ΄t :
# docker build -t iiif-studio .
# ── Stage 1 : build du frontend React ────────────────────────────────────────
FROM node:20-slim AS frontend-builder
WORKDIR /frontend
# Installer les dΓ©pendances (cache layer sΓ©parΓ©)
COPY frontend/package.json ./
RUN npm install
# Copier les sources et builder
COPY frontend/ ./
RUN npm run build
# ── Stage 2 : image Python finale ────────────────────────────────────────────
FROM python:3.11-slim
WORKDIR /app
# ── DΓ©pendances Python ─────────────────────────────────────────────────────
# On copie uniquement pyproject.toml pour exploiter le cache de layers Docker.
# Un stub app/__init__.py satisfait setuptools (discover packages) sans avoir
# besoin de copier tout le code source Γ  ce stade.
COPY backend/pyproject.toml /tmp/build/
RUN mkdir -p /tmp/build/app \
&& touch /tmp/build/app/__init__.py \
&& pip install --no-cache-dir --upgrade /tmp/build/ \
&& rm -rf /tmp/build
# ── Layer dΓ©diΓ© mistralai β€” garantit v1.x mΓͺme si le cache pip rΓ©sout autrement ──
# Layer sΓ©parΓ© pour s'assurer que mistralai>=1.0 est bien installΓ©.
# Sans cette ligne, une rΓ©solution de dΓ©pendances conflictuelle peut installer v0.x.
RUN pip install --no-cache-dir 'mistralai>=1.0,<2.0'
# ── Code source backend ────────────────────────────────────────────────────
COPY backend/app ./backend/app
COPY profiles/ ./profiles/
COPY prompts/ ./prompts/
# ── Frontend buildΓ© ────────────────────────────────────────────────────────
COPY --from=frontend-builder /frontend/dist ./static
# ── RΓ©pertoire des artefacts (vide dans l'image ; montΓ© en volume) ─────────
RUN mkdir -p /app/data
# ── Secrets : JAMAIS dans l'image (R06) ────────────────────────────────────
# Passer au runtime via les Secrets HuggingFace Spaces :
# GOOGLE_AI_STUDIO_API_KEY, MISTRAL_API_KEY, VERTEX_SERVICE_ACCOUNT_JSON
# ── Utilisateur non-root (sΓ©curitΓ©) ───────────────────────────────────────
RUN adduser --disabled-password --gecos '' appuser && chown -R appuser:appuser /app
USER appuser
# PYTHONPATH permet l'import `app.main:app` depuis /app/backend/app/
ENV PYTHONPATH=/app/backend
ENV PROFILES_DIR=/app/profiles
ENV PROMPTS_DIR=/app/prompts
ENV DATA_DIR=/app/data
EXPOSE 7860
# 1 worker au MVP β€” pas de Gunicorn, pas de multiprocessing
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"]