fix: address Docker security and stability issues from PR review

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
This commit is contained in:
Nicolas Pastorello committed 2026-07-08 20:34:34 -03:00
1 parent 48b27bd6f5
commit ef5c7c3f51
4 files changed
+68 -11

No files matched your search

+42
View File
@@ -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
+1 -1
View File
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
FROM node:18-alpine FROM node:22-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2
# Install dependencies # Install dependencies
RUN apk add --no-cache \ RUN apk add --no-cache \
+2 -2
View File
@@ -8,7 +8,6 @@ services:
env_file: env_file:
- .env - .env
environment: environment:
- T3MP3ST_HOST=127.0.0.1
- T3MP3ST_PORT=3333 - T3MP3ST_PORT=3333
volumes: volumes:
- ./reports:/app/reports - ./reports:/app/reports
@@ -16,7 +15,8 @@ services:
ports: ports:
- "127.0.0.1:3333:3333" - "127.0.0.1:3333:3333"
healthcheck: healthcheck:
test: ["CMD", "node", "-e", "process.exit(0)"] test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3333/api/health"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3
start_period: 40s
+23 -8
View File
@@ -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: The default `Dockerfile` is single-stage for simplicity. For production, consider a multi-stage build:
```dockerfile ```dockerfile
FROM node:18-alpine AS builder FROM node:22-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2 AS builder
WORKDIR /app WORKDIR /app
COPY package*.json tsconfig.json ./ COPY package*.json tsconfig.json ./
RUN npm ci RUN npm ci
COPY . . COPY . .
RUN npm run build RUN npm run build
FROM node:18-alpine FROM node:22-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2
WORKDIR /app WORKDIR /app
COPY --from=builder /app/dist ./dist COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules 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 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 ```yaml
ports: ports:
- "8080:3000" # host:container - "127.0.0.1:8080:3333" # host:container
``` ```
## Cleanup ## Cleanup
@@ -178,16 +178,31 @@ docker system prune -a --volumes
## Security Notes ## 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. - 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. - The `.env` file is excluded from the image via `.dockerignore` to prevent secret leakage.
- Network mode is `bridge` by default. Change to `host` if scanning from the container's network context. - Network mode is `bridge` by default (isolated from host network).
## Integration with Tools ## Integration with Tools
External security tools (nuclei, ffuf, sqlmap) can run in separate containers or be installed in a custom Dockerfile: External security tools (nuclei, ffuf, sqlmap) can run in separate containers or be installed in a custom Dockerfile:
```dockerfile ```dockerfile
FROM node:18-alpine FROM node:22-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2
RUN apk add --no-cache nmap nuclei ffuf RUN apk add --no-cache nmap nuclei ffuf
# ... rest of Dockerfile # ... rest of Dockerfile
``` ```