From ef5c7c3f516dc3c5b0dac56350ee42ff72e10c2a Mon Sep 17 00:00:00 2001 From: Nicolas Pastorello Date: Wed, 8 Jul 2026 20:34:34 -0300 Subject: [PATCH] fix: address Docker security and stability issues from PR review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes all issues raised in PR #47 review: 1. Add .dockerignore to prevent: - Real .env secrets baked into image layers - Host node_modules (glibc) overwriting alpine (musl) → ABI mismatch - .git, reports/, evidence/ bloating the image 2. Fix compose host-binding: - Remove T3MP3ST_HOST=127.0.0.1 override (was breaking port forwarding) - Server now binds to 0.0.0.0 in-container (DOCKER=true from Dockerfile) - Host mapping stays 127.0.0.1:3333:3333 for security 3. Add critical security warning in DOCKER.md: - Explain exposure risk of 0.0.0.0 binding + disabled Host-header guard - Document safe vs unsafe deployment patterns - Warn against network_mode: host and -p 3333:3333 without 127.0.0.1 4. Update base image: - node:18-alpine → node:22-alpine (Node 18 EOL) - Pin by digest @sha256:16e22a550... (prevent floating tag drift) 5. Fix healthcheck in compose: - Was always returning success (process.exit(0)) - Now actually tests /api/health endpoint - Add start_period: 40s to align with Dockerfile 6. Fix port typos in DOCKER.md: - 3000 → 3333 in troubleshooting examples - Update all example snippets to node:22-alpine with digest --- .dockerignore | 42 ++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 2 +- docker-compose.yml | 4 ++-- docs/DOCKER.md | 31 +++++++++++++++++++++++-------- 4 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ee03553 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,42 @@ +# Environment files (may contain real secrets) +.env +.env.* +!.env.example + +# Git +.git +.gitignore + +# Node modules (host might be glibc, container is musl) +node_modules + +# Reports and evidence (mounted as volumes) +reports +evidence + +# Build artifacts +dist +*.log +npm-debug.log* + +# IDE +.vscode +.idea +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Docker +docker-compose.override.yml +Dockerfile* +.dockerignore + +# CI/CD +.github + +# Documentation (built at runtime if needed) +docs/.vitepress/cache +docs/.vitepress/dist diff --git a/Dockerfile b/Dockerfile index abf9194..05bcb29 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -FROM node:18-alpine +FROM node:22-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2 # Install dependencies RUN apk add --no-cache \ diff --git a/docker-compose.yml b/docker-compose.yml index 48ff696..60bd12c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,6 @@ services: env_file: - .env environment: - - T3MP3ST_HOST=127.0.0.1 - T3MP3ST_PORT=3333 volumes: - ./reports:/app/reports @@ -16,7 +15,8 @@ services: ports: - "127.0.0.1:3333:3333" healthcheck: - test: ["CMD", "node", "-e", "process.exit(0)"] + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3333/api/health"] interval: 30s timeout: 10s retries: 3 + start_period: 40s diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 069461f..6fdca4e 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -110,14 +110,14 @@ docker compose -f docker-compose.yml -f docker-compose.dev.yml up The default `Dockerfile` is single-stage for simplicity. For production, consider a multi-stage build: ```dockerfile -FROM node:18-alpine AS builder +FROM node:22-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2 AS builder WORKDIR /app COPY package*.json tsconfig.json ./ RUN npm ci COPY . . RUN npm run build -FROM node:18-alpine +FROM node:22-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2 WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules @@ -151,12 +151,12 @@ On Linux, match container UID/GID if needed: sudo chown -R $(id -u):$(id -g) reports evidence ``` -### Port 3000 already in use +### Port 3333 already in use -Change the host port in `docker-compose.yml`: +Change the host port in `docker-compose.yml` (keep the `127.0.0.1:` prefix for security): ```yaml ports: - - "8080:3000" # host:container + - "127.0.0.1:8080:3333" # host:container ``` ## Cleanup @@ -178,16 +178,31 @@ docker system prune -a --volumes ## Security Notes +**⚠️ CRITICAL: External Exposure Risk** + +The Docker setup binds the server to `0.0.0.0` inside the container to allow port forwarding. This disables the Host-header/DNS-rebinding guard in `server.ts`. + +**SAFE:** The default `docker-compose.yml` publishes the port as `127.0.0.1:3333:3333`, which restricts access to localhost only. + +**UNSAFE:** Running `docker run -p 3333:3333` (without the `127.0.0.1:` prefix) or `network_mode: host` exposes the **unauthenticated command-executing API** on all network interfaces (0.0.0.0), allowing anyone on your network (or the internet if port-forwarded) to execute arbitrary system commands. + +**Deployment Rules:** +- ✅ Always bind to `127.0.0.1` on the host: `-p 127.0.0.1:3333:3333` +- ✅ Use a reverse proxy (nginx, Caddy, Coolify) with authentication for external access +- ❌ Never use `network_mode: host` in untrusted networks +- ❌ Never publish to `0.0.0.0` without authentication middleware + +**Additional Security:** - Container runs as root by default. For production, add a non-root user in the Dockerfile. -- `.env` is mounted read-only in the compose file to prevent accidental modification. -- Network mode is `bridge` by default. Change to `host` if scanning from the container's network context. +- The `.env` file is excluded from the image via `.dockerignore` to prevent secret leakage. +- Network mode is `bridge` by default (isolated from host network). ## Integration with Tools External security tools (nuclei, ffuf, sqlmap) can run in separate containers or be installed in a custom Dockerfile: ```dockerfile -FROM node:18-alpine +FROM node:22-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2 RUN apk add --no-cache nmap nuclei ffuf # ... rest of Dockerfile ```