server/index.js:
- Import http and https modules
- Resolve TLS_ENABLED early (before Helmet) so upgradeInsecureRequests
CSP directive fires when TLS is active directly (not only via proxy)
- loadTlsCredentials() reads TLS_CERT/TLS_KEY (defaulting to bundled
snakeoil) and returns null on failure (graceful HTTP fallback)
- Start https.createServer or http.createServer depending on credentials
- Startup banner now shows protocol, TLS cert path, and snakeoil warning
certs/:
- Add bundled snakeoil self-signed certificate (RSA 2048, 10yr, SAN for
localhost + 127.0.0.1) for out-of-the-box HTTPS without configuration
- .gitignore allows only snakeoil.{crt,key} — real certs must not be
committed
Dockerfile:
- COPY certs/ into image so snakeoil default is always available
- HEALTHCHECK updated to https:// with --no-check-certificate
docker-compose.yaml:
- Port now exposes HTTPS directly by default
- TLS_CERT/TLS_KEY/TLS_ENABLED/TRUST_PROXY documented with Option A/B
- cert volume mount examples added (commented out)
- healthcheck updated to https with --no-check-certificate
.env.sample:
- New TLS/HTTPS section with TLS_ENABLED, TLS_CERT, TLS_KEY
- openssl self-signed cert generation example included
docs/ARCHITECTURE.md:
- Configuration table: TLS_ENABLED, TLS_CERT, TLS_KEY env vars added
- Docker image section: TLS default behaviour documented
- Docker Compose example: Option A (direct TLS) / Option B (proxy) layout
- Security checklist: HTTPS now first item, updated for TLS modes
59 lines
2.4 KiB
Docker
59 lines
2.4 KiB
Docker
# ---------------------------------------------------------------------------
|
|
# Stage 1 — deps: install production dependencies only
|
|
# ---------------------------------------------------------------------------
|
|
FROM node:22-alpine AS deps
|
|
|
|
WORKDIR /app
|
|
|
|
# All dependencies are pure JavaScript — no native addons, no build tools.
|
|
COPY package.json package-lock.json ./
|
|
RUN npm ci --omit=dev
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Stage 2 — runtime image (minimal attack surface)
|
|
# ---------------------------------------------------------------------------
|
|
FROM node:22-alpine AS runtime
|
|
|
|
LABEL org.opencontainers.image.title="sofarr"
|
|
LABEL org.opencontainers.image.description="Personal media download dashboard for *arr services"
|
|
LABEL org.opencontainers.image.authors="Gordon Bolton"
|
|
LABEL org.opencontainers.image.source="https://git.i3omb.com/Gandalf/sofarr"
|
|
LABEL org.opencontainers.image.url="https://docker.i3omb.com"
|
|
LABEL org.opencontainers.image.vendor="Gordon Bolton"
|
|
LABEL org.opencontainers.image.licenses="MIT"
|
|
LABEL custom.hardware.requirement="None - runs on any Docker-supported platform including ARM and x86_64"
|
|
|
|
# Use the built-in non-root 'node' user (UID 1000) from the official image
|
|
# The /app directory is owned by root; data directory is owned by node
|
|
WORKDIR /app
|
|
|
|
# Copy production deps from deps stage
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
|
|
# Copy application source owned by root (read-only at runtime)
|
|
COPY --chown=root:root server/ ./server/
|
|
COPY --chown=root:root public/ ./public/
|
|
COPY --chown=root:root package.json ./
|
|
# Bundled snakeoil certificate for out-of-the-box TLS (self-signed, localhost only).
|
|
# Mount your own cert/key over /app/certs/ or set TLS_CERT/TLS_KEY env vars.
|
|
COPY --chown=root:root certs/ ./certs/
|
|
|
|
# Persistent data directory owned by node user (token store, logs)
|
|
RUN mkdir -p /app/data && chown node:node /app/data
|
|
|
|
ENV NODE_ENV=production
|
|
ENV DATA_DIR=/app/data
|
|
|
|
# Drop to non-root user for all subsequent operations
|
|
USER node
|
|
|
|
EXPOSE 3001
|
|
|
|
# HEALTHCHECK — Docker will restart the container if this fails 3 times
|
|
# --no-check-certificate handles self-signed / snakeoil certs.
|
|
# Remove that flag when using a CA-signed certificate.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD wget -qO- --no-check-certificate https://localhost:3001/health || exit 1
|
|
|
|
CMD ["node", "server/index.js"]
|