Server Up beheerde de Docker-daemon als root zonder enige vorm van authenticatie: elke /api/*-route was gelijk aan root-toegang op de host. - Authenticatie toegevoegd (core/auth.py): lokale accounts met scrypt-hash, sessiecookie (HttpOnly, SameSite=Strict) en optionele trusted-proxy-header SSO die alleen vanaf geconfigureerde proxy-IP's wordt vertrouwd. - before_request-guard schermt alle API-routes af; loginscherm en eerste-account-setup in de UI. - CSRF-token verplicht op elke mutatie; GET-varianten van state-wijzigende routes verwijderd (o.a. /api/docker/restart was via <img> te triggeren). - Path traversal in /api/store/install gedicht; gedeelde safe_name()-validatie voor stack-, instantie- en repo-namen. - Git-tokens worden niet meer teruggegeven via /api/repos en /api/settings (has_token-vlag); settings-PUT wist een bestaand token niet meer en kan AUTH niet overschrijven. - Git-URL's beperkt tot http(s)/ssh/scp-syntax; ext::-transport (voert een shell-commando uit) en file:// worden geweigerd. - Boilerplate-templates renderen in een SandboxedEnvironment (SSTI). - Automatisch syncen van repo's bij boot standaard uit (AUTO_SYNC_ON_BOOT), optionele commit-pinning per repo. - Waitress in plaats van de Flask-ontwikkelserver, MAX_CONTENT_LENGTH, ProxyFix, en CSP/X-Frame-Options/nosniff/Referrer-Policy headers. - Front-end libraries (Tailwind, Alpine, htmx) lokaal meegeleverd i.p.v. CDN; Google Fonts verwijderd. Werkt nu ook offline. - SSH host-key-verificatie aan (accept-new + /data/known_hosts). - Lichte /healthz voor de healthcheck i.p.v. `docker info`. - config.json en secret.key met 0600-rechten. - Poort standaard op 127.0.0.1 gebonden. - Audit-log gebruikt één gedeelde SQLite-verbinding (fd-lek per job verholpen); joblogs afgekapt op 2000 regels. - VERSION-bestand is de enige bron voor het versienummer. - pytest-suite toegevoegd (74 tests) en als stap in beide deploy-workflows. - fix-config.sh verwijderd (hardgecodeerd intern IP, overschreef config). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
74 lines
3.9 KiB
Docker
74 lines
3.9 KiB
Docker
FROM python:3.12-slim AS build
|
|
WORKDIR /app
|
|
COPY server-up/requirements.txt .
|
|
RUN pip install --no-cache-dir --prefix=/inst -r requirements.txt
|
|
|
|
FROM python:3.12-slim
|
|
# Build-arg: zet bij build naar de git-tag, zodat de image z'n eigen versie kent.
|
|
# docker build --build-arg SU_VERSION=$(git describe --tags --always) ...
|
|
# Forgejo Actions doet dit automatisch (zie server-up-deploy/README.md).
|
|
ARG SU_VERSION=dev
|
|
LABEL org.opencontainers.image.title="Server Up" \
|
|
org.opencontainers.image.version="${SU_VERSION}" \
|
|
org.opencontainers.image.source="https://git.example.com/bes-r/server-up"
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
git openssh-client curl tar gzip ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Install Docker CLI + docker-compose inside container
|
|
RUN DPKG_ARCH=$(dpkg --print-architecture) \
|
|
&& case "$DPKG_ARCH" in \
|
|
amd64) DOCKER_ARCH=x86_64; COMPOSE_ARCH=x86_64 ;; \
|
|
arm64) DOCKER_ARCH=aarch64; COMPOSE_ARCH=aarch64 ;; \
|
|
armhf) DOCKER_ARCH=armhf; COMPOSE_ARCH=armv7 ;; \
|
|
*) DOCKER_ARCH=$DPKG_ARCH; COMPOSE_ARCH=$DPKG_ARCH ;; \
|
|
esac \
|
|
&& curl -fsSL "https://download.docker.com/linux/static/stable/${DOCKER_ARCH}/docker-27.5.1.tgz" \
|
|
| tar xz --strip-components=1 -C /usr/local/bin docker/docker \
|
|
&& chmod +x /usr/local/bin/docker \
|
|
&& curl -fsSL "https://github.com/docker/compose/releases/download/v2.32.4/docker-compose-linux-${COMPOSE_ARCH}" \
|
|
-o /usr/local/bin/docker-compose \
|
|
&& chmod +x /usr/local/bin/docker-compose \
|
|
&& mkdir -p /usr/local/lib/docker/cli-plugins /usr/libexec/docker/cli-plugins \
|
|
&& ln -sf /usr/local/bin/docker-compose /usr/local/lib/docker/cli-plugins/docker-compose \
|
|
&& ln -sf /usr/local/bin/docker-compose /usr/libexec/docker/cli-plugins/docker-compose
|
|
|
|
COPY --from=build /inst /usr/local
|
|
WORKDIR /app
|
|
COPY server-up/ ./
|
|
COPY modules/ ./modules-bundled/
|
|
COPY VERSION ./VERSION
|
|
RUN mkdir -p static/fonts \
|
|
&& curl -fsSL "https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css" -o static/fonts/mdi.min.css \
|
|
&& curl -fsSL "https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/fonts/materialdesignicons-webfont.woff2" -o static/fonts/materialdesignicons-webfont.woff2 \
|
|
&& sed -i "s|https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/fonts/||g" static/fonts/mdi.min.css
|
|
|
|
# Front-end libraries meeleveren in plaats van vanaf een CDN laden. Zo kan de
|
|
# Content-Security-Policy op 'self' blijven, is er geen supply-chain-risico van
|
|
# unpkg/jsdelivr op runtime, en werkt de UI ook zonder internetverbinding.
|
|
RUN mkdir -p static/vendor \
|
|
&& curl -fsSL "https://cdn.tailwindcss.com/3.4.16?plugins=forms,typography" \
|
|
-o static/vendor/tailwind.js \
|
|
&& curl -fsSL "https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js" \
|
|
-o static/vendor/alpine.min.js \
|
|
&& curl -fsSL "https://cdn.jsdelivr.net/npm/htmx.org@2.0.3/dist/htmx.min.js" \
|
|
-o static/vendor/htmx.min.js \
|
|
&& for f in static/vendor/tailwind.js static/vendor/alpine.min.js static/vendor/htmx.min.js; do \
|
|
test -s "$f" || { echo "LEEG: $f"; exit 1; }; \
|
|
done
|
|
|
|
# Directories + git safe.directory (fix dubious ownership)
|
|
RUN mkdir -p /data/stacks /data/appdata /data/backups /data/git modules /root \
|
|
&& printf '[safe]\n\tdirectory = *\n' > /root/.gitconfig
|
|
|
|
# GIT_SSH_COMMAND: StrictHostKeyChecking=accept-new vertrouwt een host bij de
|
|
# eerste verbinding en bewaart de sleutel in /data/known_hosts. Wijzigt die
|
|
# sleutel later, dan faalt de verbinding — met `no` was elke MITM onzichtbaar.
|
|
ENV PYTHONUNBUFFERED=1 PORT=5000 HOME=/root \
|
|
SU_VERSION=${SU_VERSION} \
|
|
SU_CONFIG=/data/config.json SU_AUDIT=/data/audit.db SU_GIT_CACHE=/data/git \
|
|
SU_SECRET=/data/secret.key \
|
|
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/data/known_hosts"
|
|
EXPOSE 5000
|
|
HEALTHCHECK --interval=30s --timeout=8s --start-period=15s CMD curl -fs http://localhost:5000/healthz || exit 1
|
|
CMD ["python","app.py"]
|