DEV Community

Muhammad Ikhsan Nur Rafid
Muhammad Ikhsan Nur Rafid

Posted on Edited on

HydraSec: Running Six Pentest Tools From One Web App, Built With Docker

The problem
Small businesses and MSMEs are digitizing through websites, but they rarely have the budget for professional penetration testing. Meanwhile, the industry-standard tools (Nmap, Nuclei, xray, XSStrike, sqlmap, ffuf) are free but command-line heavy, each produces reports in a different format, and running them against your own site without proper isolation is risky. The predictable outcome: website owners skip security testing entirely.

HydraSec is my answer: a self-hosted web application that consolidates all six tools into one interface. You point it at a website you own, and it runs the scan inside an isolated ephemeral container, streams the live CLI output straight to your browser through xterm.js, normalizes every finding into a single database schema, and adds your scan to a public leaderboard. Total cost: Rp0, running entirely on your own machine.

Architecture

arch

The stack is nine containers: an Nginx gateway, a React frontend, a Node.js API, PostgreSQL, Redis (BullMQ), a background worker, MailPit for OTP email delivery, plus Prometheus and cAdvisor for telemetry. The design principle is strict separation: a dedicated internal network for app services and a sandbox network for the scanning tools, so a scanner can never reach the database. Tool containers spawn on demand as non-root users with all capabilities dropped, memory caps, PID limits, and a per-tool timeout, then destroy themselves when the scan ends.

The Docker details that mattered
The frontend uses a multi-stage build, which is the cheapest win in the whole project:

Dockerfile
FROM node:20-alpine AS build
COPY package.json package-lock.json* ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html

Only the final stage ships, so the image is 74 MB instead of 400+ MB of Node.js and node_modules. Every base image is pinned to a specific version (no latest), and the tool sources are pinned to exact commit hashes.

The orchestration rule is equally simple: the API never spawns containers. Jobs go into a Redis queue (BullMQ) and the worker runs at most two tools at a time:
docker-compose.yml
background-worker:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- tooldata:/data
environment:
WORKER_CONCURRENCY: 2
networks: [internal, sandbox]

Ten simultaneous scans queue safely instead of OOM-ing the host. Security was treated as a chain: parameterized SQL everywhere, OTP codes stored as SHA-256 hashes (there are no passwords at all), an SSRF guard that resolves DNS and rejects private ranges, and strict input whitelists before anything reaches a CLI.

Run it locally
bash
git clone https://github.com/Ikhsaaan334/hydrasec && cd hydrasec
bash setup.sh

Sign in with an email and a one-time code, pick a unique username, choose a tool, and watch the scan stream live into your terminal. When it finishes, every finding lands in one normalized table you can export as JSON.

1
2
3
4
5
6
7
8
9

What I learned

  1. Multi-stage builds are the cheapest performance win in containerization: one extra FROM cut my frontend image by 6x.
  2. cgroups make ephemeral sandboxes practical: memory caps and PID limits per tool container are what let two aggressive scanners run side by side safely.
  3. Test before you ship, always. A 2024 binary against 2026 template formats crashed instantly; the bug only surfaced when I actually ran the scan instead of trusting the build.
  4. Security is a chain: one latest tag or one unpinned dependency weakens an otherwise hardened setup.
  5. AI-assisted development needs a human in the loop: I used an AI coding assistant heavily for scaffolding, but every fix, test, and architectural call went through my own hands (full declaration in the repo README).

Links
Repository: https://github.com/Ikhsaaan334/hydrasec/
Demo video: https://youtu.be/---Ny7KHdEk

Top comments (0)