From f528668e859024798f989f53811e752b1a42593c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Mar 2026 09:18:26 +0000 Subject: [PATCH] feat: optimize setup UX with interactive wizard and host-mounted output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add setup.sh interactive wizard (prereq checks, .env generation, docker pull/up, health wait) with CLI flags for non-interactive use - Replace Docker named volume with host bind mounts (./output/) so reports, screenshots, and session JSONs are accessible on the host filesystem - Restructure .env.example with clear REQUIRED vs OPTIONAL sections - Add `make setup` target to Makefile - Fix inject_verification.py syntax error (multiline f-string incompatible with Python < 3.12) - Update docker/claude-code-entrypoint.sh to show output directory mappings - Update README.md: add setup.sh quick-start, Output Files section, updated project structure - Update DOCKER.md: fix Ollama "required" → "optional" (since v2.1), correct core stack count (9 → 4), fix Compose Services profile column, add output bind mount documentation https://claude.ai/code/session_01JZbXEtmxC7P6HLbTDF2i4j --- .env.example | 56 ++-- .gitignore | 8 +- DOCKER.md | 83 +++-- Makefile | 6 +- README.md | 79 ++++- blhackbox/prompts/inject_verification.py | 11 +- docker-compose.yml | 7 +- docker/claude-code-entrypoint.sh | 5 + setup.sh | 382 +++++++++++++++++++++++ 9 files changed, 571 insertions(+), 66 deletions(-) create mode 100755 setup.sh diff --git a/.env.example b/.env.example index a7f4754..c6c2864 100644 --- a/.env.example +++ b/.env.example @@ -1,25 +1,21 @@ -# ── LLM Provider ──────────────────────────────────────────────────── -# REQUIRED for Claude Code in Docker. Without this key, the Claude Code -# container will not start. Get your key at https://console.anthropic.com -# Uncomment the line below and replace with your actual key: +# ╔══════════════════════════════════════════════════════════════════════╗ +# ║ blhackbox v2.0 — Environment Configuration ║ +# ║ Copy to .env and fill in the REQUIRED section: ║ +# ║ cp .env.example .env ║ +# ║ Or use the setup wizard: ║ +# ║ ./setup.sh ║ +# ╚══════════════════════════════════════════════════════════════════════╝ + +# ══════════════════════════════════════════════════════════════════════ +# ██ REQUIRED — Claude Code in Docker will not start without this ██ +# ══════════════════════════════════════════════════════════════════════ +# Get your key at: https://console.anthropic.com/settings/keys +# Uncomment and set your key: # ANTHROPIC_API_KEY=sk-ant-... -# -# Required for ChatGPT / OpenAI MCP clients (host-based only). -# Get your key at platform.openai.com -# OPENAI_API_KEY=sk-... -# ── Ollama (OPTIONAL — legacy local pipeline) ────────────────────── -# The MCP host (Claude) now handles data aggregation directly. -# These settings are only needed if you enable the Ollama pipeline: -# docker compose --profile ollama up -d -# -# OLLAMA_MODEL=llama3.1:8b -# OLLAMA_TIMEOUT=300 -# OLLAMA_NUM_CTX=8192 -# OLLAMA_KEEP_ALIVE=10m -# OLLAMA_RETRIES=2 -# AGENT_TIMEOUT=1200 -# AGENT_RETRIES=2 +# ══════════════════════════════════════════════════════════════════════ +# OPTIONAL — Defaults work out of the box +# ══════════════════════════════════════════════════════════════════════ # ── Kali MCP Server ────────────────────────────────────────────── # Port exposed on the host for direct SSE connections (http://localhost:9001/sse). @@ -37,7 +33,7 @@ KALI_MAX_TIMEOUT=600 SCREENSHOT_MCP_PORT=9004 # ── Metasploit (integrated in Kali MCP) ─────────────────────────── -# Metasploit is now bundled inside the kali-mcp container. +# Metasploit is bundled inside the kali-mcp container. # Tools use `msfconsole -qx` (CLI) — no msfrpcd daemon needed. # Timeout (seconds) for msfconsole commands. Cold start takes 10-30s. MSF_TIMEOUT=300 @@ -56,3 +52,21 @@ NEO4J_PASSWORD=changeme-min-8-chars # Neo4j Aura alternative (cloud): # NEO4J_URI=neo4j+s://xxxxxxxx.databases.neo4j.io + +# ── Ollama (optional — legacy local pipeline) ────────────────────── +# The MCP host (Claude) now handles data aggregation directly. +# These settings are only needed if you enable the Ollama pipeline: +# docker compose --profile ollama up -d +# +# OLLAMA_MODEL=llama3.1:8b +# OLLAMA_TIMEOUT=300 +# OLLAMA_NUM_CTX=8192 +# OLLAMA_KEEP_ALIVE=10m +# OLLAMA_RETRIES=2 +# AGENT_TIMEOUT=1200 +# AGENT_RETRIES=2 + +# ── OpenAI (optional — for ChatGPT MCP clients on host) ──────────── +# Required for ChatGPT / OpenAI MCP clients (host-based only). +# Get your key at platform.openai.com +# OPENAI_API_KEY=sk-... diff --git a/.gitignore b/.gitignore index 0f5d3b5..d518c70 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,10 @@ env/ # Active verification document (rendered from verification.env — contains engagement details) .claude/verification-active.md -# Results and reports +# Output directory (host-mounted reports, screenshots, sessions) +output/ + +# Legacy results and reports (pre-v2.1) results/*.json results/*.pdf results/*.html @@ -51,6 +54,9 @@ wordlists/*.csv # Docker .docker/ +# Planning docs (temp) +plan.md + # OS .DS_Store Thumbs.db diff --git a/DOCKER.md b/DOCKER.md index 14e1739..c118af0 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -60,7 +60,11 @@ Claude Code ──┬──> Kali MCP (SSE, port 9001) (container) │ 70+ tools: nmap, sqlmap, hydra, msfconsole, msfvenom, etc. ├──> WireMCP (SSE, port 9003) ├──> Screenshot MCP (SSE, port 9004) - └──> Ollama MCP (SSE, port 9000) + │ + │ After collecting raw outputs, Claude structures them directly: + │ get_payload_schema() → parse/dedup/correlate → aggregate_results() + │ + └──> (optional) Ollama MCP (SSE, port 9000) │ ├──► agent-ingestion:8001 ├──► agent-processing:8002 @@ -69,33 +73,45 @@ Claude Code ──┬──> Kali MCP (SSE, port 9001) ▼ Ollama (LLM backend) -Portainer (Docker UI) Neo4j (optional) +output/ Host-mounted directory for reports, screenshots, sessions +Portainer Docker UI (https://localhost:9443) +Neo4j Cross-session memory (optional) ``` **Claude Desktop / ChatGPT (host)** connect via the MCP Gateway: ``` Claude Desktop ──> MCP Gateway (localhost:8080/mcp) ──┬──> Kali MCP -(host app) └──> Ollama MCP +(host app) ├──> WireMCP + └──> Screenshot MCP ``` -> **Ollama is required.** All 3 agent containers call Ollama via the official -> `ollama` Python package. Without it, the aggregation pipeline returns empty results. +> **Ollama is optional since v2.1.** The MCP host (Claude) now handles data +> aggregation directly. The Ollama pipeline is kept as an optional fallback +> for local-only / offline processing. Enable with `--profile ollama`. --- ## Usage -### Core Stack (9 containers) +### Quick Start (Recommended) + +```bash +git clone https://github.com/valITino/blhackbox.git +cd blhackbox +./setup.sh # interactive wizard: prereqs, .env, pull, start, health +``` + +### Manual — Core Stack (4 containers) ```bash git clone https://github.com/valITino/blhackbox.git cd blhackbox cp .env.example .env # REQUIRED: Uncomment and set ANTHROPIC_API_KEY=sk-ant-... in .env -docker compose pull # pull pre-built images (ollama builds locally) -docker compose up -d # start core stack -make ollama-pull # pull the Ollama model (REQUIRED — default: llama3.1:8b) +mkdir -p output/reports output/screenshots output/sessions +docker compose pull +docker compose up -d ``` ### With Claude Code (Recommended) @@ -132,15 +148,15 @@ make health # MCP server health check | `kali-mcp` | `crhacky/blhackbox:kali-mcp` | `9001` | default | Kali Linux security tools + Metasploit (70+) | | `wire-mcp` | `crhacky/blhackbox:wire-mcp` | `9003` | default | Wireshark/tshark (7 tools) | | `screenshot-mcp` | `crhacky/blhackbox:screenshot-mcp` | `9004` | default | Screenshot MCP (headless Chromium, 4 tools) | -| `ollama-mcp` | `crhacky/blhackbox:ollama-mcp` | `9000` | default | Thin MCP orchestrator | -| `agent-ingestion` | `crhacky/blhackbox:agent-ingestion` | `8001` | default | Agent 1: parse raw output | -| `agent-processing` | `crhacky/blhackbox:agent-processing` | `8002` | default | Agent 2: deduplicate, compress | -| `agent-synthesis` | `crhacky/blhackbox:agent-synthesis` | `8003` | default | Agent 3: assemble payload | -| `ollama` | `crhacky/blhackbox:ollama` (built locally) | `11434` | default | LLM inference backend (llama3.1:8b) | | `portainer` | `portainer/portainer-ce:latest` | `9443` | default | Docker management UI (HTTPS) | +| `claude-code` | `crhacky/blhackbox:claude-code` | - | `claude-code` | Claude Code CLI client (Docker) | | `mcp-gateway` | `docker/mcp-gateway:latest` | `8080` | `gateway` | Single MCP entry point (host clients) | | `neo4j` | `neo4j:5` | `7474` `7687` | `neo4j` | Cross-session knowledge graph | -| `claude-code` | `crhacky/blhackbox:claude-code` | - | `claude-code` | Claude Code CLI client (Docker) | +| `ollama-mcp` | `crhacky/blhackbox:ollama-mcp` | `9000` | `ollama` | Thin MCP orchestrator (optional) | +| `agent-ingestion` | `crhacky/blhackbox:agent-ingestion` | `8001` | `ollama` | Agent 1: parse raw output (optional) | +| `agent-processing` | `crhacky/blhackbox:agent-processing` | `8002` | `ollama` | Agent 2: deduplicate, compress (optional) | +| `agent-synthesis` | `crhacky/blhackbox:agent-synthesis` | `8003` | `ollama` | Agent 3: assemble payload (optional) | +| `ollama` | `crhacky/blhackbox:ollama` (built locally) | `11434` | `ollama` | LLM inference backend (optional) | --- @@ -270,13 +286,20 @@ Named volumes for persistent data: | Volume | Service | Purpose | |---|---|---| -| `ollama_models` | ollama | Ollama model storage | -| `neo4j_data` | neo4j | Neo4j graph database | -| `neo4j_logs` | neo4j | Neo4j logs | +| `ollama_models` | ollama | Ollama model storage (optional) | +| `neo4j_data` | neo4j | Neo4j graph database (optional) | +| `neo4j_logs` | neo4j | Neo4j logs (optional) | | `portainer_data` | portainer | Portainer configuration | -| `results` | - | Scan output and reports | | `wordlists` | - | Fuzzing wordlists | +Host bind mounts for output (accessible on your local filesystem): + +| Host Path | Container Path | Purpose | +|---|---|---| +| `./output/reports/` | `/root/reports/` | Generated pentest reports (.md, .pdf) | +| `./output/screenshots/` | `/tmp/screenshots/` | PoC evidence screenshots (.png) | +| `./output/sessions/` | `/root/results/` | Aggregated session JSON files | + --- ## CI/CD Pipeline @@ -300,22 +323,28 @@ Docker Scout vulnerability scanning runs on the ollama-mcp image. ## Useful Commands ```bash +# Interactive setup wizard (recommended for first-time setup) +make setup + # Pull all pre-built images from Docker Hub docker compose pull -# Start core stack (9 containers) +# Start core stack (4 containers) docker compose up -d -# Start with MCP Gateway for Claude Desktop (10 containers) +# Start with MCP Gateway for Claude Desktop (5 containers) make up-gateway -# Start with Neo4j (10 containers) +# Start with Neo4j (5 containers) docker compose --profile neo4j up -d +# Start with Ollama pipeline (9 containers, optional) +docker compose --profile ollama up -d + # Launch Claude Code in Docker make claude-code -# Pull the Ollama model (REQUIRED) +# Pull the Ollama model (only if using --profile ollama) make ollama-pull # Check health of all MCP servers @@ -326,14 +355,10 @@ make status # View service logs make logs-kali -make logs-agent-ingestion -make logs-agent-processing -make logs-agent-synthesis +make logs-wireshark +make logs-screenshot make gateway-logs -# Restart all 3 agent containers -make restart-agents - # Stop and clean up make down make clean # also removes volumes diff --git a/Makefile b/Makefile index 4501a6f..3c3637c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help up up-full up-ollama up-gateway down logs test test-local lint format clean nuke \ +.PHONY: help setup up up-full up-ollama up-gateway down logs test test-local lint format clean nuke \ pull status health portainer gateway-logs ollama-pull ollama-shell \ claude-code \ neo4j-browser logs-ollama-mcp logs-kali \ @@ -11,6 +11,9 @@ COMPOSE := docker compose +setup: ## Interactive setup wizard (prerequisites, .env, pull, start, health check) + @bash setup.sh + help: ## Show this help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-26s\033[0m %s\n", $$1, $$2}' @@ -71,7 +74,6 @@ nuke: ## Full cleanup: containers + volumes + ALL images (frees max disk space) docker volume rm blhackbox_portainer_data 2>/dev/null || true docker volume rm blhackbox_neo4j_data 2>/dev/null || true docker volume rm blhackbox_neo4j_logs 2>/dev/null || true - docker volume rm blhackbox_results 2>/dev/null || true docker volume rm blhackbox_wordlists 2>/dev/null || true find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true rm -rf dist/ build/ *.egg-info diff --git a/README.md b/README.md index f5e1069..1d1badd 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ - [How It Works](#how-it-works) - [Architecture](#architecture) +- [Output Files](#output-files) - [Components](#components) - [Prerequisites](#prerequisites) - [Installation](#installation) @@ -105,6 +106,32 @@ aggregates all servers behind `localhost:8080/mcp`. See --- +## Output Files + +All output files (reports, screenshots, session data) are stored in the `./output/` +directory on your host machine via Docker bind mounts. You can access them directly +from your file system — no need to `docker cp`. + +``` +output/ +├── reports/ ← Pentest reports (.md, .pdf) +├── screenshots/ ← PoC evidence screenshots (.png) +└── sessions/ ← Aggregated session JSON files +``` + +| Container Path | Host Path | What goes there | +|---------------|-----------|-----------------| +| `/root/reports/` | `./output/reports/` | Generated pentest reports (markdown, PDF) | +| `/tmp/screenshots/` | `./output/screenshots/` | Screenshot MCP captures and annotations | +| `/root/results/` | `./output/sessions/` | `AggregatedPayload` session JSONs | + +The `output/` directory is created automatically by `setup.sh`. If you installed +manually, create it with: `mkdir -p output/reports output/screenshots output/sessions` + +> **Note:** The `output/` directory is git-ignored. Back up important reports separately. + +--- + ## Components | Container | What it does | Internal Port | Default Profile | @@ -135,6 +162,40 @@ aggregates all servers behind `localhost:8080/mcp`. See ## Installation +### Quick Start (Recommended) + +```bash +git clone https://github.com/valITino/blhackbox.git +cd blhackbox +./setup.sh +``` + +The setup wizard will: +1. Check prerequisites (Docker, Docker Compose, disk space) +2. Let you choose optional components (Neo4j, MCP Gateway, Ollama) +3. Prompt for your `ANTHROPIC_API_KEY` (required for Claude Code in Docker) +4. Generate `.env` and create the `output/` directory +5. Pull Docker images and start all services +6. Wait for health checks to pass + +You can also pass flags for non-interactive use: + +```bash +./setup.sh --api-key sk-ant-... --minimal # Core stack only +./setup.sh --api-key sk-ant-... --with-neo4j # Core + Neo4j +./setup.sh --help # All options +``` + +Or use the Makefile shortcut: + +```bash +make setup +``` + +### Manual Installation + +If you prefer to set up manually: + ```bash # 1. Clone the repo git clone https://github.com/valITino/blhackbox.git @@ -144,23 +205,25 @@ cd blhackbox cp .env.example .env # REQUIRED: Uncomment and set your Anthropic API key in .env: # ANTHROPIC_API_KEY=sk-ant-... -# Without this, the Claude Code container cannot start. -# 3. Pull all pre-built Docker images +# 3. Create output directories (reports, screenshots, sessions) +mkdir -p output/reports output/screenshots output/sessions + +# 4. Pull all pre-built Docker images docker compose pull -# 4. Start the core stack (4 containers) +# 5. Start the core stack (4 containers) docker compose up -d ``` **Set up authorization (required before running pentests):** ```bash -# 5. Edit verification.env with your engagement details +# 6. Edit verification.env with your engagement details nano verification.env # Set AUTHORIZATION_STATUS=ACTIVE after filling in all fields -# 6. Render the active verification document +# 7. Render the active verification document make inject-verification ``` @@ -616,6 +679,7 @@ blhackbox mcp ## Makefile Shortcuts ```bash +make setup # Interactive setup wizard (prereqs, .env, pull, start, health) make help # Show all available targets make pull # Pull all pre-built images from Docker Hub make up # Start core stack (4 containers) @@ -872,11 +936,16 @@ Then run `make inject-verification` and start your Claude Code session. ``` blhackbox/ +├── setup.sh # Interactive setup wizard ├── .claude/ │ ├── settings.json # Claude Code hooks config │ ├── verification-active.md # Rendered authorization doc (git-ignored) │ └── hooks/ │ └── session-start.sh # auto-setup for web sessions +├── output/ # Host-accessible outputs (git-ignored) +│ ├── reports/ # Generated pentest reports +│ ├── screenshots/ # PoC evidence captures +│ └── sessions/ # Aggregated session JSONs ├── verification.env # Pentest authorization config (edit before testing) ├── .mcp.json # MCP server config (Claude Code Web) ├── docker/ diff --git a/blhackbox/prompts/inject_verification.py b/blhackbox/prompts/inject_verification.py index c74d746..8bd6125 100644 --- a/blhackbox/prompts/inject_verification.py +++ b/blhackbox/prompts/inject_verification.py @@ -155,14 +155,15 @@ def inject( result["status"] = "active" result["output_path"] = str(out_path) + targets = ", ".join( + env.get(f"TARGET_{i}", "") + for i in range(1, 4) + if env.get(f"TARGET_{i}") + ) result["message"] = ( f"Verification document activated → {out_path}\n" f"Engagement: {env.get('ENGAGEMENT_ID', 'N/A')}\n" - f"Targets: {', '.join( - env.get(f'TARGET_{i}', '') - for i in range(1, 4) - if env.get(f'TARGET_{i}') - )}\n" + f"Targets: {targets}\n" f"Window: {env.get('TESTING_START', '?')} — " f"{env.get('TESTING_END', '?')} {env.get('TIMEZONE', 'UTC')}\n" f"Authorized by: {env.get('SIGNATORY_NAME', 'N/A')}" diff --git a/docker-compose.yml b/docker-compose.yml index f83c93e..b93fc6e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,7 +40,6 @@ volumes: neo4j_logs: ollama_models: portainer_data: - results: wordlists: services: @@ -182,7 +181,7 @@ services: ports: - "${SCREENSHOT_MCP_PORT:-9004}:9004" volumes: - - results:/tmp/screenshots + - ./output/screenshots:/tmp/screenshots healthcheck: test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:9004/health')\""] interval: 15s @@ -428,7 +427,9 @@ services: - 8.8.8.8 - 1.1.1.1 volumes: - - results:/tmp/screenshots:ro + - ./output/screenshots:/tmp/screenshots:ro + - ./output/reports:/root/reports + - ./output/sessions:/root/results depends_on: kali-mcp: condition: service_healthy diff --git a/docker/claude-code-entrypoint.sh b/docker/claude-code-entrypoint.sh index c8e2d7e..01e275e 100755 --- a/docker/claude-code-entrypoint.sh +++ b/docker/claude-code-entrypoint.sh @@ -154,6 +154,11 @@ echo -e " ${CYAN}api-security${NC} ${DIM}API security testing (OWASP AP echo -e " ${CYAN}bug-bounty${NC} ${DIM}Bug bounty hunting workflow${NC}" echo -e " ${CYAN}osint-gathering${NC} ${DIM}Passive OSINT intelligence collection${NC}" echo "" +echo -e " ${BOLD}Output files${NC} ${DIM}(mounted to host ./output/)${NC}${BOLD}:${NC}" +echo -e " ${DIM}/root/reports/ → ./output/reports/ pentest reports${NC}" +echo -e " ${DIM}/tmp/screenshots/ → ./output/screenshots/ PoC evidence${NC}" +echo -e " ${DIM}/root/results/ → ./output/sessions/ session JSONs${NC}" +echo "" echo -e " ${BOLD}Quick start:${NC}" echo -e " ${CYAN}/mcp${NC} ${DIM}Check MCP server status${NC}" echo -e " ${CYAN}Use the full-pentest template against example.com${NC}" diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..b0f34f4 --- /dev/null +++ b/setup.sh @@ -0,0 +1,382 @@ +#!/usr/bin/env bash +# ── blhackbox v2.0 — Interactive Setup Wizard ──────────────────────── +# One-command setup: checks prerequisites, generates .env, pulls images, +# starts services, and verifies health. +# +# Usage: +# ./setup.sh # Interactive mode +# ./setup.sh --api-key sk-ant-... # Non-interactive with API key +# ./setup.sh --minimal # Skip optional profiles +# ./setup.sh --help # Show usage +set -euo pipefail + +# ── Colors & Symbols ───────────────────────────────────────────────── +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +BOLD='\033[1m' +DIM='\033[2m' +NC='\033[0m' +CHECK="${GREEN}✔${NC}" +CROSS="${RED}✘${NC}" +WARN="${YELLOW}!${NC}" +ARROW="${CYAN}→${NC}" + +# ── Globals ────────────────────────────────────────────────────────── +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +API_KEY="" +MINIMAL=false +SKIP_PULL=false +NEO4J_PASS="" +PROFILES="" + +# ── Functions ──────────────────────────────────────────────────────── + +print_banner() { + echo "" + echo -e "${CYAN}${BOLD} ╔══════════════════════════════════════════════╗${NC}" + echo -e "${CYAN}${BOLD} ║ blhackbox v2.0 — Setup Wizard ║${NC}" + echo -e "${CYAN}${BOLD} ║ MCP-based Pentesting Framework ║${NC}" + echo -e "${CYAN}${BOLD} ╚══════════════════════════════════════════════╝${NC}" + echo "" +} + +usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --api-key KEY Set ANTHROPIC_API_KEY (skips interactive prompt)" + echo " --minimal Core stack only (no Neo4j, no Ollama)" + echo " --with-neo4j Enable Neo4j knowledge graph" + echo " --with-ollama Enable Ollama local pipeline" + echo " --with-gateway Enable MCP Gateway for Claude Desktop/ChatGPT" + echo " --skip-pull Skip docker compose pull (use cached images)" + echo " --help Show this help" + echo "" + exit 0 +} + +# Check if a command exists +require_cmd() { + if ! command -v "$1" &>/dev/null; then + echo -e " ${CROSS} ${BOLD}$1${NC} is not installed." + echo -e " ${ARROW} $2" + return 1 + fi + return 0 +} + +# Compare semver: returns 0 if $1 >= $2 +version_gte() { + printf '%s\n%s' "$2" "$1" | sort -V -C +} + +check_prerequisites() { + echo -e "${BOLD}Checking prerequisites...${NC}" + echo "" + local ok=true + + # Docker + if require_cmd docker "Install: https://docs.docker.com/get-docker/"; then + local docker_ver + docker_ver=$(docker version --format '{{.Server.Version}}' 2>/dev/null || echo "0.0.0") + if version_gte "$docker_ver" "24.0.0"; then + echo -e " ${CHECK} Docker ${DIM}v${docker_ver}${NC}" + else + echo -e " ${WARN} Docker v${docker_ver} — recommend v24.0+ for BuildKit support" + fi + else + ok=false + fi + + # Docker Compose (v2 plugin) + if docker compose version &>/dev/null; then + local compose_ver + compose_ver=$(docker compose version --short 2>/dev/null | sed 's/^v//') + if version_gte "$compose_ver" "2.20.0"; then + echo -e " ${CHECK} Docker Compose ${DIM}v${compose_ver}${NC}" + else + echo -e " ${WARN} Docker Compose v${compose_ver} — recommend v2.20+" + fi + else + echo -e " ${CROSS} ${BOLD}docker compose${NC} not available." + echo -e " ${ARROW} Install Docker Compose v2: https://docs.docker.com/compose/install/" + ok=false + fi + + # Git + if require_cmd git "Install: https://git-scm.com/downloads"; then + local git_ver + git_ver=$(git --version | awk '{print $3}') + echo -e " ${CHECK} Git ${DIM}v${git_ver}${NC}" + fi + + # Disk space check (need ~5GB for images) + local avail_gb + avail_gb=$(df -BG "$SCRIPT_DIR" | awk 'NR==2 {print $4}' | tr -d 'G') + if [ "$avail_gb" -lt 5 ]; then + echo -e " ${WARN} Only ${avail_gb}GB disk space available (recommend 5GB+)" + else + echo -e " ${CHECK} Disk space ${DIM}${avail_gb}GB available${NC}" + fi + + echo "" + if [ "$ok" = false ]; then + echo -e "${RED}${BOLD}Prerequisites missing. Install the required tools and re-run.${NC}" + exit 1 + fi +} + +configure_env() { + echo -e "${BOLD}Configuring environment...${NC}" + echo "" + + # If .env already exists, ask before overwriting + if [ -f "$SCRIPT_DIR/.env" ]; then + echo -e " ${WARN} ${BOLD}.env${NC} already exists." + read -rp " Overwrite with fresh config? [y/N] " overwrite + if [[ ! "$overwrite" =~ ^[Yy] ]]; then + echo -e " ${ARROW} Keeping existing .env" + echo "" + return + fi + fi + + # Start from template + cp "$SCRIPT_DIR/.env.example" "$SCRIPT_DIR/.env" + + # --- ANTHROPIC_API_KEY --- + if [ -z "$API_KEY" ]; then + echo -e " ${BOLD}ANTHROPIC_API_KEY${NC} ${DIM}(required for Claude Code in Docker)${NC}" + echo -e " ${DIM}Get yours at: https://console.anthropic.com/settings/keys${NC}" + echo -e " ${DIM}Press Enter to skip if you'll only use Claude Code on host.${NC}" + read -rsp " API Key: " API_KEY + echo "" + fi + + if [ -n "$API_KEY" ]; then + # Replace the commented-out line with the actual key + sed -i "s|^# ANTHROPIC_API_KEY=sk-ant-.*|ANTHROPIC_API_KEY=${API_KEY}|" "$SCRIPT_DIR/.env" + echo -e " ${CHECK} ANTHROPIC_API_KEY configured" + else + echo -e " ${WARN} ANTHROPIC_API_KEY skipped — Claude Code Docker won't start without it" + fi + + # --- NEO4J_PASSWORD --- + if [[ "$PROFILES" == *"neo4j"* ]]; then + if [ -z "$NEO4J_PASS" ]; then + echo "" + echo -e " ${BOLD}NEO4J_PASSWORD${NC} ${DIM}(min 8 chars for Neo4j)${NC}" + read -rp " Password [changeme-min-8-chars]: " NEO4J_PASS + NEO4J_PASS="${NEO4J_PASS:-changeme-min-8-chars}" + fi + sed -i "s|^NEO4J_PASSWORD=.*|NEO4J_PASSWORD=${NEO4J_PASS}|" "$SCRIPT_DIR/.env" + echo -e " ${CHECK} NEO4J_PASSWORD configured" + fi + + echo "" +} + +select_profiles() { + if [ "$MINIMAL" = true ]; then + PROFILES="" + echo -e " ${ARROW} Minimal mode: core stack only (4 containers)" + echo "" + return + fi + + echo -e "${BOLD}Select optional components:${NC}" + echo "" + + # Neo4j + if [[ "$PROFILES" != *"neo4j"* ]]; then + read -rp " Enable Neo4j knowledge graph? [y/N] " yn + if [[ "$yn" =~ ^[Yy] ]]; then + PROFILES="${PROFILES:+$PROFILES }--profile neo4j" + fi + fi + + # Gateway + if [[ "$PROFILES" != *"gateway"* ]]; then + read -rp " Enable MCP Gateway (for Claude Desktop/ChatGPT)? [y/N] " yn + if [[ "$yn" =~ ^[Yy] ]]; then + PROFILES="${PROFILES:+$PROFILES }--profile gateway" + fi + fi + + # Ollama + if [[ "$PROFILES" != *"ollama"* ]]; then + read -rp " Enable Ollama local pipeline? [y/N] " yn + if [[ "$yn" =~ ^[Yy] ]]; then + PROFILES="${PROFILES:+$PROFILES }--profile ollama" + fi + fi + + echo "" +} + +create_output_dirs() { + echo -e "${BOLD}Creating output directories...${NC}" + mkdir -p "$SCRIPT_DIR/output/reports" \ + "$SCRIPT_DIR/output/screenshots" \ + "$SCRIPT_DIR/output/sessions" + echo -e " ${CHECK} output/reports/ ${DIM}← pentest reports (.md, .pdf)${NC}" + echo -e " ${CHECK} output/screenshots/ ${DIM}← PoC evidence captures${NC}" + echo -e " ${CHECK} output/sessions/ ${DIM}← aggregated session JSONs${NC}" + echo "" +} + +pull_images() { + if [ "$SKIP_PULL" = true ]; then + echo -e " ${ARROW} Skipping image pull (--skip-pull)" + echo "" + return + fi + + echo -e "${BOLD}Pulling Docker images...${NC}" + echo -e " ${DIM}This may take a few minutes on first run.${NC}" + echo "" + # shellcheck disable=SC2086 + docker compose $PROFILES pull 2>&1 | tail -5 + echo "" + echo -e " ${CHECK} Images pulled" + echo "" +} + +start_services() { + echo -e "${BOLD}Starting services...${NC}" + echo "" + # shellcheck disable=SC2086 + docker compose $PROFILES up -d 2>&1 | tail -10 + echo "" + echo -e " ${CHECK} Services started" + echo "" +} + +wait_for_health() { + echo -e "${BOLD}Waiting for services to become healthy...${NC}" + echo -e " ${DIM}Kali MCP takes 30-60s on first startup (Metasploit DB init).${NC}" + echo "" + + local max_wait=90 + local interval=5 + local elapsed=0 + local all_healthy=false + + while [ $elapsed -lt $max_wait ]; do + local healthy + healthy=$(docker compose ps --format json 2>/dev/null | grep -c '"healthy"' || true) + local total + total=$(docker compose ps --format json 2>/dev/null | grep -c '"running\|healthy\|unhealthy"' || true) + + printf "\r ${DIM}[%02d/%02ds]${NC} %d/%d services healthy..." "$elapsed" "$max_wait" "$healthy" "$total" + + # Check core services + if docker compose ps kali-mcp 2>/dev/null | grep -q "healthy" && \ + docker compose ps screenshot-mcp 2>/dev/null | grep -q "healthy"; then + all_healthy=true + break + fi + + sleep "$interval" + elapsed=$((elapsed + interval)) + done + + echo "" + echo "" + + if [ "$all_healthy" = true ]; then + echo -e " ${CHECK} ${GREEN}${BOLD}Core services are healthy!${NC}" + else + echo -e " ${WARN} Some services may still be starting." + echo -e " ${DIM}Run 'make health' or 'docker compose ps' to check status.${NC}" + fi + echo "" +} + +print_summary() { + echo -e "${DIM}══════════════════════════════════════════════════${NC}" + echo -e "${GREEN}${BOLD} Setup complete!${NC}" + echo -e "${DIM}══════════════════════════════════════════════════${NC}" + echo "" + echo -e " ${BOLD}Quick start:${NC}" + echo -e " ${CYAN}make claude-code${NC} ${DIM}Launch Claude Code in Docker${NC}" + echo -e " ${CYAN}make status${NC} ${DIM}Check service status${NC}" + echo -e " ${CYAN}make health${NC} ${DIM}Run health checks${NC}" + echo -e " ${CYAN}make logs${NC} ${DIM}View service logs${NC}" + echo "" + echo -e " ${BOLD}Output files${NC} (accessible on your host):" + echo -e " ${DIM}./output/reports/ Pentest reports (.md, .pdf)${NC}" + echo -e " ${DIM}./output/screenshots/ PoC evidence screenshots${NC}" + echo -e " ${DIM}./output/sessions/ Aggregated session data${NC}" + echo "" + echo -e " ${BOLD}Portainer UI:${NC}" + echo -e " ${CYAN}https://localhost:9443${NC}" + echo -e " ${WARN} Create admin account within 5 minutes of first start!" + echo "" + if [[ "$PROFILES" == *"neo4j"* ]]; then + echo -e " ${BOLD}Neo4j Browser:${NC} ${CYAN}http://localhost:7474${NC}" + fi + if [[ "$PROFILES" == *"gateway"* ]]; then + echo -e " ${BOLD}MCP Gateway:${NC} ${CYAN}http://localhost:8080${NC}" + fi + echo -e " ${BOLD}For pentesting:${NC}" + echo -e " 1. Edit ${CYAN}verification.env${NC} with your engagement details" + echo -e " 2. Run ${CYAN}make inject-verification${NC}" + echo -e " 3. Use a pentest template: ${DIM}full-pentest, quick-scan, etc.${NC}" + echo "" +} + +# ── Parse Arguments ────────────────────────────────────────────────── + +while [[ $# -gt 0 ]]; do + case "$1" in + --api-key) + API_KEY="$2" + shift 2 + ;; + --minimal) + MINIMAL=true + shift + ;; + --with-neo4j) + PROFILES="${PROFILES:+$PROFILES }--profile neo4j" + shift + ;; + --with-ollama) + PROFILES="${PROFILES:+$PROFILES }--profile ollama" + shift + ;; + --with-gateway) + PROFILES="${PROFILES:+$PROFILES }--profile gateway" + shift + ;; + --skip-pull) + SKIP_PULL=true + shift + ;; + --help|-h) + usage + ;; + *) + echo "Unknown option: $1" + usage + ;; + esac +done + +# ── Main ───────────────────────────────────────────────────────────── + +cd "$SCRIPT_DIR" + +print_banner +check_prerequisites +select_profiles +configure_env +create_output_dirs +pull_images +start_services +wait_for_health +print_summary