# ── Stage 1 : nothing to build, just copy static assets ────────────────────── # Use nginx:alpine — ~8 MB, actively maintained, CVE-scanned by Docker Hub FROM nginx:stable-alpine AS runtime # Drop all Linux capabilities the HTTP server doesn't need. # Only CAP_NET_BIND_SERVICE is kept when binding to port 80; since we expose # 8080 (>1024) and run as non-root, we can drop everything. # These are set at runtime via docker-compose; the label is informational. LABEL org.opencontainers.image.title="it_portfolio" \ org.opencontainers.image.description="Static portfolio — nginx/alpine" \ org.opencontainers.image.authors="Gauvain Boiché" # Replace the default nginx config with our hardened one COPY nginx.conf /etc/nginx/nginx.conf # Copy static assets COPY index.html /usr/share/nginx/html/index.html COPY content/ /usr/share/nginx/html/content/ COPY resources/ /usr/share/nginx/html/resources/ # Give the non-root nginx user ownership of what it needs at runtime. # Temp files go to /tmp (1777 tmpfs), so no pre-creation needed. RUN chown -R nginx:nginx \ /var/log/nginx \ /etc/nginx/nginx.conf \ /usr/share/nginx/html \ && chmod -R 755 /usr/share/nginx/html # Switch to the built-in unprivileged nginx user USER nginx # Expose unprivileged port (no CAP_NET_BIND_SERVICE required) EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ CMD wget -qO- http://localhost:8080/ || exit 1