12a9d963f5
Trivy flagged nginx 1.28.3-r1 in the frontend image (alerts #371-374): - CVE-2026-42055 (HIGH) HTTP/2 heap overflow - CVE-2026-49975 (HIGH) HTTP/2 DoS - CVE-2026-9256 (HIGH) rewrite_module code exec / DoS - CVE-2026-48142 (MED) charset_module memory disclosure All fixed in nginx 1.28.3-r4. The Dockerfile already ran 'apk upgrade --no-cache', but the pushed image predated the fixed package and the layer was cached on r1. Add an explicit nginx upgrade to force the layer to rebuild against the current Alpine repos (which now carry r4).
88 lines
2.9 KiB
Docker
88 lines
2.9 KiB
Docker
# Build stage — node:22-alpine drops npm-bundled CVEs in older Node 20
|
|
# (picomatch, ip-address, brace-expansion, @sigstore/core, tar) since the
|
|
# bundled npm version is newer in 22. Matches the backend Dockerfile base.
|
|
FROM node:22-alpine AS builder
|
|
|
|
# Add build arguments
|
|
ARG CACHEBUST=1
|
|
ARG BUILD_DATE
|
|
ARG VCS_REF
|
|
ARG VERSION
|
|
|
|
# Add labels for GitHub Container Registry
|
|
LABEL org.opencontainers.image.source="https://github.com/PicPeak/picpeak"
|
|
LABEL org.opencontainers.image.description="PicPeak Frontend Application"
|
|
LABEL org.opencontainers.image.licenses="MIT"
|
|
|
|
# Set working directory
|
|
WORKDIR /app
|
|
|
|
# Copy package files
|
|
COPY package*.json ./
|
|
|
|
# Install dependencies
|
|
RUN npm ci --legacy-peer-deps
|
|
|
|
# Copy source files
|
|
COPY . .
|
|
|
|
# Build the application
|
|
RUN npm run build
|
|
|
|
# Production stage (Alpine 3.23 with OpenSSL 3.5.5, patched libexpat)
|
|
FROM nginx:1.28-alpine
|
|
|
|
# Upgrade all Alpine packages for security fixes. The explicit nginx upgrade
|
|
# closes the HTTP/2 + rewrite/charset CVEs (CVE-2026-42055 / -49975 / -9256 /
|
|
# -48142, fixed in nginx 1.28.3-r4) and busts any cached layer still carrying
|
|
# the vulnerable r1 build.
|
|
RUN apk upgrade --no-cache && apk add --no-cache --upgrade nginx
|
|
|
|
# Install runtime dependencies. `gettext` provides envsubst, used by
|
|
# docker-entrypoint.sh for the BRAND_TITLE / BRAND_DESCRIPTION runtime
|
|
# substitution into index.html (#521 — runtime fix for self-hosters
|
|
# on the pre-built GHCR image who can't override at build time).
|
|
RUN apk add --no-cache curl gettext
|
|
|
|
# Remove default nginx config
|
|
RUN rm -rf /etc/nginx/conf.d/*
|
|
|
|
# Copy custom nginx config
|
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
|
|
# Copy built application from builder stage
|
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
|
|
|
# Snapshot index.html as a template so the entrypoint always renders
|
|
# from a known-good source — not from its own previous substitution.
|
|
# Container restarts can change BRAND_TITLE freely; the rendered file
|
|
# is recomputed from the .tpl each time.
|
|
RUN mv /usr/share/nginx/html/index.html /usr/share/nginx/html/index.html.tpl
|
|
|
|
# Runtime entrypoint that envsubsts the template and execs nginx
|
|
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
|
|
|
# Set permissions (nginx user already exists in nginx:alpine)
|
|
RUN chown -R nginx:nginx /usr/share/nginx/html && \
|
|
chown -R nginx:nginx /var/cache/nginx && \
|
|
chown -R nginx:nginx /var/log/nginx && \
|
|
touch /var/run/nginx.pid && \
|
|
chown -R nginx:nginx /var/run/nginx.pid
|
|
|
|
# Expose port
|
|
EXPOSE 80
|
|
|
|
# Health check
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
CMD curl -f http://localhost/health || exit 1
|
|
|
|
# Switch to non-root user
|
|
USER nginx
|
|
|
|
# Start nginx via the entrypoint so each container start re-renders
|
|
# index.html from the template against the current BRAND_TITLE /
|
|
# BRAND_DESCRIPTION env vars (defaults applied when unset).
|
|
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
|
CMD ["nginx", "-g", "daemon off;"]
|