diff --git a/.github/workflows/docker-security.yml b/.github/workflows/docker-security.yml new file mode 100644 index 0000000..729cca3 --- /dev/null +++ b/.github/workflows/docker-security.yml @@ -0,0 +1,28 @@ +name: Docker Security Scan + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + scan: + name: Scan Docker Image with Anchore + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build image + run: docker build -t python-samples-fastapi-restful . + + - name: Run Anchore scan + uses: anchore/scan-action@v2 + with: + image-reference: python-samples-fastapi-restful + fail-build: true diff --git a/Dockerfile b/Dockerfile index 19c6f8a..10fd3b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,67 +1,70 @@ -# ------------------------------------------------------------------------------ -# Stage 1: Builder -# This stage builds the application and its dependencies. -# ------------------------------------------------------------------------------ -FROM python:3.13.3-slim-bookworm AS builder -WORKDIR /app - -# Install system build tools for packages with native extensions -RUN apt-get update && \ - apt-get install -y --no-install-recommends build-essential gcc libffi-dev libssl-dev && \ - rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb - -# Pre-build all dependencies into wheels for reproducibility and speed -COPY --chown=root:root --chmod=644 requirements.txt . -RUN pip wheel --no-cache-dir --wheel-dir=/app/wheelhouse -r requirements.txt - -# ------------------------------------------------------------------------------ -# Stage 2: Runtime -# This stage creates the final, minimal image to run the application. -# ------------------------------------------------------------------------------ -FROM python:3.13.3-slim-bookworm AS runtime -WORKDIR /app - -# Metadata labels -LABEL org.opencontainers.image.title="🧪 RESTful API with Python 3 and FastAPI" -LABEL org.opencontainers.image.description="Proof of Concept for a RESTful API made with Python 3 and FastAPI" -LABEL org.opencontainers.image.licenses="MIT" -LABEL org.opencontainers.image.source="https://github.com/nanotaboada/python-samples-fastapi-restful" - -# Copy prebuilt wheels and install dependencies -COPY --chown=root:root --chmod=644 requirements.txt . -COPY --from=builder --chown=root:root --chmod=755 /app/wheelhouse /app/wheelhouse -RUN pip install --no-cache-dir --no-index --find-links /app/wheelhouse -r requirements.txt && \ - rm -rf /app/wheelhouse - -# Copy application code (read-only) -COPY --chown=root:root --chmod=644 main.py ./ -COPY --chown=root:root --chmod=755 database ./database -COPY --chown=root:root --chmod=755 models ./models -COPY --chown=root:root --chmod=755 routes ./routes -COPY --chown=root:root --chmod=755 schemas ./schemas -COPY --chown=root:root --chmod=755 services ./services - -# Copy metadata for GHCR (read-only) -COPY --chown=root:root --chmod=644 README.md ./ -COPY --chown=root:root --chmod=755 assets ./assets - -# Copy entrypoint sctipt and SQLite database -COPY --chown=root:root --chmod=755 scripts/entrypoint.sh ./entrypoint.sh -COPY --chown=root:root --chmod=755 sqlite3-db ./docker-compose - -# Create non-root user and make volume mount point writable -RUN groupadd --system fastapi && \ - adduser --system --ingroup fastapi --disabled-password --gecos '' fastapi && \ - mkdir -p /sqlite3-db && \ - chown fastapi:fastapi /sqlite3-db - -# Drop privileges -USER fastapi - -# Logging output immediately -ENV PYTHONUNBUFFERED=1 - -EXPOSE 9000 - -ENTRYPOINT ["./entrypoint.sh"] -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "9000"] +# ------------------------------------------------------------------------------ +# Stage 1: Builder +# ------------------------------------------------------------------------------ + FROM python:3.13.3-slim-bookworm AS builder + WORKDIR /app + + # Install build dependencies + RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + build-essential \ + gcc \ + libffi-dev \ + libssl-dev && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + + # Copy and pre-build Python dependencies + COPY requirements.txt . + RUN pip install --upgrade pip && \ + pip wheel --no-cache-dir --wheel-dir=/app/wheelhouse -r requirements.txt + + # ------------------------------------------------------------------------------ + # Stage 2: Runtime + # ------------------------------------------------------------------------------ + FROM python:3.13.3-slim-bookworm AS runtime + WORKDIR /app + + # Metadata + LABEL org.opencontainers.image.title="🧪 RESTful API with Python 3 and FastAPI" + LABEL org.opencontainers.image.description="Proof of Concept for a RESTful API made with Python 3 and FastAPI" + LABEL org.opencontainers.image.licenses="MIT" + LABEL org.opencontainers.image.source="https://github.com/nanotaboada/python-samples-fastapi-restful" + + # Install runtime dependencies + COPY requirements.txt . + COPY --from=builder /app/wheelhouse /app/wheelhouse + RUN pip install --no-cache-dir --no-index --find-links=/app/wheelhouse -r requirements.txt && \ + rm -rf /app/wheelhouse + + # Copy app code + COPY main.py . + COPY database/ ./database/ + COPY models/ ./models/ + COPY routes/ ./routes/ + COPY schemas/ ./schemas/ + COPY services/ ./services/ + COPY README.md . + COPY assets/ ./assets/ + + # Copy startup script and SQLite DB seed + COPY scripts/entrypoint.sh ./entrypoint.sh + RUN chmod +x ./entrypoint.sh + COPY sqlite3-db ./docker-compose + + # Create non-root user and make volume writable + RUN groupadd --system fastapi && \ + useradd --system --gid fastapi --create-home fastapi && \ + mkdir -p /sqlite3-db && \ + chown -R fastapi:fastapi /app /sqlite3-db + + # Configure environment + ENV PYTHONUNBUFFERED=1 + EXPOSE 9000 + + ENTRYPOINT ["./entrypoint.sh"] + CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "9000"] + + # 👇Run with not root user + USER fastapi + diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh index 642f010..ea8d4d2 100644 --- a/scripts/entrypoint.sh +++ b/scripts/entrypoint.sh @@ -1,5 +1,7 @@ #!/bin/bash set -e +echo "✔ Fixing permissions..." +chown -R fastapi:fastapi /sqlite3-db IMAGE_DATABASE_FILE_PATH="/app/docker-compose/players-sqlite3.db" VOLUME_DATABASE_FILE_PATH="/sqlite3-db/players-sqlite3.db"