From 3b2765c6034def795995125682306ff951e2ac4b Mon Sep 17 00:00:00 2001 From: Marco Franssen Date: Tue, 19 May 2026 18:01:14 +0200 Subject: [PATCH 1/2] Ensure Docker container runs as non root user Signed-off-by: Marco Franssen --- Dockerfile | 55 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4eb8a3c..bfdc744 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,33 +3,34 @@ FROM node:20-slim WORKDIR /app RUN apt-get update && apt-get install -y \ - git \ - python3 \ - make \ - g++ \ - sqlite3 \ - libsqlite3-dev \ - curl \ - ca-certificates \ - chromium \ - chromium-sandbox \ - fonts-freefont-ttf \ - fonts-ipafont-gothic \ - fonts-kacst \ - fonts-liberation \ - fonts-noto-color-emoji \ - fonts-thai-tlwg \ - libx11-xcb1 \ - libxcb-dri3-0 \ - libxcomposite1 \ - libxdamage1 \ - libxi6 \ - libxrandr2 \ - libxshmfence1 \ - libxtst6 \ - && apt-get clean \ - && ln -s /usr/bin/chromium /usr/bin/chromium-browser || true + git \ + python3 \ + make \ + g++ \ + sqlite3 \ + libsqlite3-dev \ + curl \ + ca-certificates \ + chromium \ + chromium-sandbox \ + fonts-freefont-ttf \ + fonts-ipafont-gothic \ + fonts-kacst \ + fonts-liberation \ + fonts-noto-color-emoji \ + fonts-thai-tlwg \ + libx11-xcb1 \ + libxcb-dri3-0 \ + libxcomposite1 \ + libxdamage1 \ + libxi6 \ + libxrandr2 \ + libxshmfence1 \ + libxtst6 \ + && apt-get clean \ + && ln -s /usr/bin/chromium /usr/bin/chromium-browser || true +USER 14000 COPY package*.json ./ RUN npm install --ignore-scripts # --ignore-scripts skips node-gyp rebuild for native modules @@ -42,4 +43,4 @@ RUN npm rebuild better-sqlite3 RUN npx puppeteer browsers install chrome || true COPY . . -RUN npm run build \ No newline at end of file +RUN npm run build From 1d1075d283e637b541f6f1c2b89a9037b52afbf4 Mon Sep 17 00:00:00 2001 From: Marco Franssen Date: Mon, 22 Jun 2026 11:12:41 +0200 Subject: [PATCH 2/2] Split build and runtime to have leaner image Signed-off-by: Marco Franssen --- Dockerfile | 71 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/Dockerfile b/Dockerfile index bfdc744..24becb0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,12 @@ -FROM node:20-slim +FROM node:20-slim AS base WORKDIR /app -RUN apt-get update && apt-get install -y \ +ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium + +RUN apt-get update && apt-get install -y --no-install-recommends \ git \ - python3 \ - make \ - g++ \ sqlite3 \ - libsqlite3-dev \ curl \ ca-certificates \ chromium \ @@ -27,20 +25,51 @@ RUN apt-get update && apt-get install -y \ libxrandr2 \ libxshmfence1 \ libxtst6 \ - && apt-get clean \ - && ln -s /usr/bin/chromium /usr/bin/chromium-browser || true - -USER 14000 -COPY package*.json ./ -RUN npm install --ignore-scripts -# --ignore-scripts skips node-gyp rebuild for native modules -# (security best-practice, blocks malicious postinstall scripts). -# Explicitly rebuild better-sqlite3 so its arm64/amd64 .node binding -# is compiled — otherwise the runtime fails with "Could not locate -# the bindings file" on architectures lacking a prebuild. -RUN npm rebuild better-sqlite3 -# Install Chrome via Puppeteer as fallback (system Chromium will be used first) -RUN npx puppeteer browsers install chrome || true -COPY . . + && rm -rf /var/lib/apt/lists/* \ + && ln -sf /usr/bin/chromium /usr/bin/chromium-browser + +RUN groupadd --gid 14000 doc2vec \ + && useradd --uid 14000 --gid doc2vec --create-home --shell /bin/bash doc2vec \ + && chown -R doc2vec:doc2vec /app + +FROM base AS builder + +USER doc2vec + +COPY --chown=doc2vec:doc2vec package*.json ./ +RUN npm ci --ignore-scripts + +COPY --chown=doc2vec:doc2vec . . RUN npm run build + +FROM base AS prod-deps + +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 \ + make \ + g++ \ + libsqlite3-dev \ + && rm -rf /var/lib/apt/lists/* + +USER doc2vec + +COPY --chown=doc2vec:doc2vec package*.json ./ +RUN node -e "const fs = require('fs'); const pkg = require('./package.json'); delete pkg.devDependencies; if (pkg.scripts) { delete pkg.scripts.prepare; delete pkg.scripts.prepublishOnly; } fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');" \ + && npm install --omit=dev --omit=peer --ignore-scripts \ + && npm rebuild better-sqlite3 \ + && npm cache clean --force + +FROM base AS runtime + +ENV NODE_ENV=production + +COPY --from=prod-deps --chown=doc2vec:doc2vec /app/package*.json ./ +COPY --from=prod-deps --chown=doc2vec:doc2vec /app/node_modules ./node_modules +COPY --from=builder --chown=doc2vec:doc2vec /app/dist ./dist +COPY --from=builder --chown=doc2vec:doc2vec /app/README.md /app/LICENSE /app/config.yaml ./ + +USER doc2vec + +# Install Chrome via Puppeteer as fallback. System Chromium is used first. +RUN npx puppeteer browsers install chrome || true