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:
1 parent
48b27bd6f5
commit
ef5c7c3f51
4 files changed
+68
-11
No files matched your search
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in new issue
Block a user