Zum Inhalt

SUSE BCI as Container Base Images

Einordnung ins Overtime-Projekt

Die Basis-Images für die Overtime-Backend-Services (Django, Worker-Prozesse) bestimmen einen Großteil der Angriffsfläche jedes Containers. Dieses ADR begründet die Wahl von SUSE BCI als Vertrauensbasis für den Python-Runtime-Stack.


1. Kontext & Problemstellung (Deutsch)

Jedes Container-Image erbt seine fundamentale Angriffsfläche direkt von seinem Basis-Image. Die in der Open-Source-Welt am häufigsten gewählten Optionen (wie python:slim, ubuntu oder alpine) werden größtenteils von Community-Volunteers gepflegt und über Docker Hub ohne kommerzielle Zusicherungen (SLAs) bereitgestellt.

Diese Intransparenz innerhalb der Software-Lieferkette (Software Supply Chain) kann erhebliches Risiko darstellen. Sicherheitsrelevante Kriterien wie die eindeutige Herkunft von Softwarepaketen (Provenance), garantierte Reaktionszeiten bei kritischen Sicherheitslücken (CVEs) sowie die Auditierbarkeit des Upstream-Herstellers müssen nachweisbar sein.

Zudem führt die Verwendung von Standard-Images oft dazu, dass unnötige Systemwerkzeuge (wie Paketmanager, Shells, Curl oder Compiler) im finalen Produktions-Image verbleiben. Diese Werkzeuge erleichtern Angreifern nach einer erfolgreichen Kompromittierung des Backends die laterale Bewegung innerhalb der Infrastruktur (Post-Exploitation). Es wird daher eine gehärtete, abgesicherte und minimalistische Image-Basis benötigt.


2. Architecture Decision Record (English)

ADR-002: SUSE BCI as Container Base Images

Status

Accepted

Context and Problem Statement

Every container image inherits its attack surface from its base. Common choices (python:slim, ubuntu:22.04, alpine) are maintained by community volunteers or Docker Hub with no enterprise SLA. The base image supply chain must be trustworthy, patchable under SLA, auditable (package provenance, CVE response times) and minimal.

Decision Drivers

  • Predictable CVE patch SLAs.
  • Minimal attack surface in the runtime image. Scratch is not possible due to requirement of Python runtime.
  • Free version is available with proper versioning

Considered Options

  • python:3.x-slim (Debian-based, Docker Hub) — Minimal, but lacks an enterprise SLA; Debian security team response time varies.
  • ubuntu:22.04 LTS — Strong community and Canonical CVE SLA, but leaves a larger package footprint than needed.
  • Wiz / Chainguard / Docker Secure Images — Excellent hardening, but mostly restrict real version pinning to paid tiers, only offering volatile @latest tags publicly.
  • SUSE Business Container Images (BCI) — Enterprise SLA, FIPS-capable, full RPM provenance.

Decision Outcome

Chosen: SUSE BCI (registry.suse.com/bci/python).

We adopt a two-tier multi-stage build strategy:

  1. python:3.14-base — Used exclusively during the builder stage (contains pip, package managers, and compilation tools).
  2. python:3.14-micro — Used exclusively during the final runtime stage (stripped to the absolute minimum).

The -micro variant ships without a shell (sh/bash) and without a system package manager (zypper). This eliminates an entire class of post-exploitation lateral movement, as an attacker cannot easily run interactive terminal commands or download malicious diagnostic utilities.

SUSE guarantees commercial CVE patching updates within 72 hours of upstream disclosure. It has SLSA Level 4 and provenance is available. For this project we will just use the free MIT-licensed version though.

Consequences & Trade-offs

Positive Consequences
  • Immense Attack Surface Reduction: The runtime image has no shell, making interactive docker exec sessions impossible for attackers.
  • Verifiable Provenance: SUSE publishes cryptographically signed RPM SBOMs, enabling vulnerability scanners (like Trivy) to resolve package-level CVEs against verified vendor feeds.
  • Registry Independence: When not self-hosting the image, pulling from registry.suse.com bypasses Docker Hub rate limits and ToS changes.
Negative Consequences / Trade-offs
  • Harder Debugging: The lack of a shell or package manager in the -micro tier means runtime debugging is intentionally obstructed. Debugging must be performed in staging using the -base tier.
  • Upstream Delay: SUSE BCI updates trail upstream Python point releases by approximately 1–2 weeks. This latency is monitored automatically via Renovate.

3. Multi-Stage Implementation & Code Snippet (English)

The following multi-stage Dockerfile template illustrates the practical application of the two-tier SUSE BCI architecture for the Django application:

ARG BUILDER_IMAGE=registry.suse.com/bci/python:3.14-base@sha256:cfe34a1d6a49d7da8c06e8173afb38fc77f23092cadfe088db17ef5a01154936
ARG RUNTIME_IMAGE=registry.suse.com/bci/python:3.14-micro@sha256:3338e229873f5d4659efa0ce04616725367c2972e03ae2cb7140af5775d939e4

# ── build ─────────────────────────────────────────────────────────────────────
FROM ${BUILDER_IMAGE} AS builder

USER root

COPY --from=ghcr.io/astral-sh/uv:0.11.21@sha256:ff07b86af50d4d9391d9daf4ff89ce427bc544f9aae87057e69a1cc0aa369946 /uv /usr/local/bin/uv

WORKDIR /app

COPY pyproject.toml uv.lock ./

# Install deps from the lock file into a fixed venv path
ENV UV_PROJECT_ENVIRONMENT=/opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN uv sync --frozen --no-dev

COPY . .
RUN mkdir -p /app/staticfiles && \
    DJANGO_SETTINGS_MODULE=config.settings.base python manage.py collectstatic --noinput

RUN mkdir -p /app/prometheus_multiproc

# ── runtime ───────────────────────────────────────────────────────────────────
FROM ${RUNTIME_IMAGE} AS runtime

COPY --chown=65532:65532 --from=builder /opt/venv /opt/venv
COPY --chown=65532:65532 --from=builder /app /app

ENV HOME=/app
ENV PATH="/opt/venv/bin:$PATH"
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PROMETHEUS_MULTIPROC_DIR=/app/prometheus_multiproc

USER 65532:65532

WORKDIR /app
EXPOSE 8000

ENTRYPOINT []
CMD ["/opt/venv/bin/gunicorn", "config.wsgi:application", "-c", "gunicorn.conf.py"]

4. Compliance Einordnung (Deutsch)

BSI IT-Grundschutz Compliance

Standard Kriterium / Inhalt Wie im Projekt adressiert
BSI IT-Grundschutz SYS.1.6.A6 (B): Verwendung sicherer Images Es werden ausschließlich verifizierte Basis-Images aus der offiziellen, herstellerbetriebenen SUSE-Registry genutzt, deren Ersteller eindeutig identifizierbar ist.
BSI IT-Grundschutz SYS.1.6.A12 (S): Verteilung sicherer Images Dieses Architektur-Dokument begründet dediziert, warum registry.suse.com als vertrauenswürdige Quelle klassifiziert wurde und wie die Integrität gewahrt bleibt.
BSI IT-Grundschutz SYS.1.6.A17 (S): Ausführung von Containern ohne Privilegien Durch den Einsatz der micro-Variante enthält das Produktions-Image keinerlei administrative Systemwerkzeuge, was das Risiko einer Privilegieneskalation minimiert. Der User wird auf 65532 gesetzt und hat damit keine privilegierten Rechte.
BSI IT-Grundschutz SYS.1.6.A11 (S): Nur ein Dienst pro Container Das Image ist strikt als Applikations-Container konzipiert. Da jegliche Shells fehlen, kann der Container technisch nur die definierte Django-Anwendung ausführen.

CIS Docker & Kubernetes Benchmark Compliance

Standard Kriterium / Inhalt Wie im Projekt adressiert
CIS Docker Benchmark Sektion 4.1: Ensure that a user for the container has been created Das finale Runtime-Image wechselt über die Direktive USER 65532:65532 explizit auf einen unprivilegierten Standard-User, statt als Root zu laufen.
CIS Docker Benchmark Sektion 4.2: Ensure that containers use only trusted base images Die Nutzung von SUSE BCI garantiert eine geschlossene Lieferkette mit kommerziellen Patches innerhalb von 72 Stunden nach Upstream-Bekanntgabe.
CIS Docker Benchmark Sektion 4.3: Ensure that unnecessary packages are not installed in the container Die finale micro-Stufe eliminiert sämtliche für den Betrieb redundanten Pakete, Compiler, Shells (bash) und Paketmanager (zypper).
CIS Kubernetes Benchmark Sektion 5.2.7: Minimize the admission of root containers Da das Dockerfile den Non-Root-User 65532 fest vorschreibt, erfüllt der resultierende Pod nativ die Restricted-Vorgaben der K8s Pod Security Standards.