docker: improve safety, UX, and Metasploit MCP defaults

Why:
- Persist artifacts to /app/loot so container outputs remain available when mounted.
- Avoid mandatory host chown; make chown opt-in via CHOWN_ON_START to prevent accidental ownership changes.
- Bind msfrpcd to 127.0.0.1 by default and add EXPOSE_MSF_RPC opt-in to avoid exposing RPC to host network.
- Replace crashing assertion on missing default model with a friendly CLI/TUI error path.
- Add .dockerignore to reduce build context and avoid copying unnecessary files.
This commit is contained in:
giveen
2026-01-15 08:56:49 -07:00
parent f136ef6f9d
commit 36350775f9
7 changed files with 162 additions and 16 deletions

17
.dockerignore Normal file
View File

@@ -0,0 +1,17 @@
venv
.venv
__pycache__
*.pyc
.pytest_cache
.mypy_cache
.git
loot
dist
build
*.egg-info
third_party/MetasploitMCP
venv/
venv/*
.env
runtime/
/.pytest_cache

81
PR_BODY.md Normal file
View File

@@ -0,0 +1,81 @@
# MCP: Add Metasploit integration, HexStrike parity, auto-start flags and SSETransport improvements
## Summary
This PR adds a vendored Metasploit MCP integration and brings it to parity with the existing HexStrike MCP integration. It also improves the MCP transport layer (SSE) to reliably handle HTTP/SSE MCP servers that return 202/async responses.
## Key changes
- Add Metasploit MCP support via a `MetasploitAdapter` that can start/stop the vendored `MetasploitMCP` server and optionally start `msfrpcd` for local testing.
- Make MCP tool registration automatic: MCP tools are discovered and registered at startup so they appear in the TUI `/tools` view.
- Add environment flags to control vendored servers and subtree updates:
- `LAUNCH_HEXTRIKE` / `LAUNCH_HEXSTRIKE` — control HexStrike vendored server auto-start behavior
- `LAUNCH_METASPLOIT_MCP` — control Metasploit vendored server auto-start behavior
- `FORCE_SUBTREE_PULL` — helper for scripts that add/update vendored `third_party` subtrees
- Improve HTTP/SSE transport (`SSETransport`) to:
- Discover the server's POST endpoint announced over `/sse`
- Maintain a persistent SSE listener instead of transient GETs
- Correlate pending requests with SSE-delivered responses (supporting 202 Accepted flows)
- Wait for endpoint discovery on connect to avoid writer races
- Add/update helper scripts (`scripts/add_metasploit_subtree.sh`, `scripts/setup.sh`) to vendor/update the Metasploit subtree and provide optional msfrpcd auto-start during setup.
## Files touched (high level)
- `pentestagent/mcp/metasploit_adapter.py` — new adapter to manage vendored Metasploit MCP and optional msfrpcd.
- `pentestagent/mcp/transport.py` — SSETransport enhancements and robustness fixes.
- `pentestagent/mcp/manager.py` — LAUNCH env handling, auto-start wiring and connection logic (fixes applied so LAUNCH_* works as expected).
- `pentestagent/mcp/mcp_servers.json` — added/updated `metasploit-local` entry in HexStrike-style (`--server http://...`).
- `.env.example` — grouped MCP settings; documents `FORCE_SUBTREE_PULL` and LAUNCH flags.
- `scripts/add_metasploit_subtree.sh`, `scripts/setup.sh` — vendoring helpers and optional msfrpcd startup.
(See the full commit set on this branch for exact diffs and additional smaller edits.)
## Behavior / Usage
- Default: vendored MCP entries are present in `pentestagent/mcp/mcp_servers.json` but not started unless configured.
- To allow PentestAgent to auto-start vendored MCPs at runtime, set the corresponding `LAUNCH_*` environment variable to a truthy value (e.g. `true`, `1`, `yes`):
```bash
export LAUNCH_METASPLOIT_MCP=true
export LAUNCH_HEXTRIKE=true
pentestagent
```
When `LAUNCH_METASPLOIT_MCP` is truthy and the manager is started, the manager will attempt to auto-start the vendored Metasploit adapter and connect to it so its tools are registered automatically.
If you prefer to run the vendored MCP server manually (recommended for debugging), start it separately and then run PentestAgent/TUI. Example:
```bash
python third_party/MetasploitMCP/MetasploitMCP.py --server http://127.0.0.1:7777 \
> loot/artifacts/metasploit_mcp.log 2>&1 & disown
pentestagent mcp test metasploit-local
```
## How to test
1. Pull this branch and install dependencies (see `scripts/setup.sh`).
2. Manual test:
- Start msfrpcd (or configure `MSF_*` envs to point to an existing Metasploit RPC)
- Start the vendored MCP server:
```bash
python third_party/MetasploitMCP/MetasploitMCP.py --server http://127.0.0.1:7777
```
- Run the manager test:
```bash
pentestagent mcp test metasploit-local
```
- Expected: `+ Connected successfully!` and a list of available Metasploit tools.
3. Auto-start test:
- Export `LAUNCH_METASPLOIT_MCP=true` and run `pentestagent` (or the TUI). The manager should auto-start the adapter and register tools so they appear in `/tools`.
## Security / Notes
- Do not commit real passwords or API keys. Use a local `.env` (never committed) to provide secrets like `MSF_PASSWORD`.
- The setup helper that may start `msfrpcd` will never invoke `sudo` — it only starts a local msfrpcd process if credentials are present and auto-start is enabled.
## Follow-ups / Optional improvements
- Add `scripts/start_metasploit.sh` to start the vendored MCP detached and capture logs (I can add this in a follow-up PR if desired).
- Add a short README section documenting the MCP vendoring workflow and recommended `.env` values for local testing.
---
If you'd like any edits to wording or additional testing instructions, tell me what to change and I will update the PR body.

View File

@@ -21,7 +21,7 @@ services:
context: .
dockerfile: Dockerfile.kali
container_name: pentestagent-kali
privileged: true # Required for VPN and some tools
privileged: true # Required for VPN and some tools. NOTE: this is risky on shared hosts; prefer running inside a disposable VM.
cap_add:
- NET_ADMIN
- SYS_ADMIN
@@ -31,6 +31,9 @@ services:
- PENTESTAGENT_MODEL=${PENTESTAGENT_MODEL}
- ENABLE_TOR=${ENABLE_TOR:-false}
- INIT_METASPLOIT=${INIT_METASPLOIT:-false}
# By default msfrpcd binds to loopback; to intentionally expose Metasploit RPC to the host
# set EXPOSE_MSF_RPC=true in your environment. This is NOT recommended on shared machines.
- EXPOSE_MSF_RPC=${EXPOSE_MSF_RPC:-false}
volumes:
- ./loot:/app/loot
networks:

View File

@@ -11,12 +11,12 @@ NC='\033[0m'
echo -e "${GREEN}🔧 PentestAgent Container Starting...${NC}"
# Start VPN if config provided
if [ -f "/vpn/config.ovpn" ]; then
# Start VPN if config provided and openvpn is available
if [ -f "/vpn/config.ovpn" ] && command -v openvpn >/dev/null 2>&1; then
echo -e "${YELLOW}📡 Starting VPN connection...${NC}"
openvpn --config /vpn/config.ovpn --daemon
openvpn --config /vpn/config.ovpn --daemon || echo "openvpn failed to start"
sleep 5
# Check VPN connection
if ip a show tun0 &>/dev/null; then
echo -e "${GREEN}✅ VPN connected${NC}"
@@ -25,22 +25,35 @@ if [ -f "/vpn/config.ovpn" ]; then
fi
fi
# Start Tor if enabled
if [ "$ENABLE_TOR" = "true" ]; then
# Start Tor if enabled and if a service command is available
if [ "$ENABLE_TOR" = "true" ] && command -v service >/dev/null 2>&1; then
echo -e "${YELLOW}🧅 Starting Tor...${NC}"
service tor start
service tor start || echo "tor service not available"
sleep 3
fi
# Initialize any databases
if [ "$INIT_METASPLOIT" = "true" ]; then
# Initialize any databases (guarded)
if [ "$INIT_METASPLOIT" = "true" ] && command -v msfdb >/dev/null 2>&1; then
echo -e "${YELLOW}🗄️ Initializing Metasploit database...${NC}"
msfdb init 2>/dev/null || true
msfdb init 2>/dev/null || echo "msfdb init failed"
fi
# Create output directory with timestamp
OUTPUT_DIR="/output/$(date +%Y%m%d_%H%M%S)"
# Ensure persistent output directory lives under /app/loot (mounted by compose)
OUTPUT_DIR="/app/loot/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTPUT_DIR"
# Optionally chown mounted volume on startup (only when running as root and explicitly enabled)
if [ "$(id -u)" = "0" ] && [ "${CHOWN_ON_START,,}" = "true" ]; then
# If PUID/PGID supplied use them, otherwise keep default permissions
if [ -n "${PUID:-}" ] && [ -n "${PGID:-}" ]; then
groupadd -g ${PGID} pentestagent 2>/dev/null || true
useradd -u ${PUID} -g ${PGID} -m pentestagent 2>/dev/null || true
chown -R ${PUID}:${PGID} /app/loot || true
else
chown -R pentestagent:pentestagent /app/loot 2>/dev/null || true
fi
fi
export PENTESTAGENT_OUTPUT_DIR="$OUTPUT_DIR"
echo -e "${GREEN}📁 Output directory: $OUTPUT_DIR${NC}"

View File

@@ -389,6 +389,15 @@ def main():
# If no command provided, default to TUI
if args.command is None:
# Ensure a default model is configured; provide a friendly error if not
if not DEFAULT_MODEL:
print("Error: No default model configured (PENTESTAGENT_MODEL).")
print("Set PENTESTAGENT_MODEL in .env file or pass --model on the command line.")
print(
"Example: PENTESTAGENT_MODEL=gpt-5 or PENTESTAGENT_MODEL=claude-sonnet-4-20250514"
)
return
run_tui(target=None, model=DEFAULT_MODEL, use_docker=False)
return

View File

@@ -1139,9 +1139,21 @@ class PentestAgentTUI(App):
await self.runtime.start()
# LLM
# Ensure types for static analysis: runtime and model are set
assert self.model is not None
assert self.runtime is not None
# Validate model/runtime presence and provide a user-friendly error
if not self.model:
self._add_system(
"[!] No model configured. Set PENTESTAGENT_MODEL environment variable or create a .env file (see .env.example)."
)
self._set_status("error")
self._is_initializing = False
return
if not self.runtime:
self._add_system("[!] Runtime failed to initialize.")
self._set_status("error")
self._is_initializing = False
return
llm = LLM(
model=self.model,
config=ModelConfig(temperature=0.7),

View File

@@ -154,6 +154,17 @@ if [ "${LAUNCH_METASPLOIT_MCP,,}" = "true" ] && [ -n "${MSF_PASSWORD:-}" ]; then
LOG_DIR="loot/artifacts"
mkdir -p "$LOG_DIR"
MSF_LOG="$LOG_DIR/metasploit_msfrpcd.log"
# For safety, bind msfrpcd to loopback by default. To intentionally expose RPC to the host
# set EXPOSE_MSF_RPC=true in your environment (not recommended on shared hosts).
if [ "${EXPOSE_MSF_RPC,,}" != "true" ]; then
if [ "$MSF_SERVER" != "127.0.0.1" ] && [ "$MSF_SERVER" != "localhost" ]; then
echo "Warning: MSF_SERVER is set to '$MSF_SERVER' but EXPOSE_MSF_RPC is not true. Overriding to 127.0.0.1 for safety."
fi
MSF_SERVER=127.0.0.1
else
echo "EXPOSE_MSF_RPC=true: msfrpcd will bind to $MSF_SERVER and may be reachable from the host network. Ensure you know the risks."
fi
if [ "${MSF_SSL,,}" = "true" ] || [ "${MSF_SSL}" = "1" ]; then
"$msfrpcd_cmd" -U "$MSF_USER" -P "$MSF_PASSWORD" -a "$MSF_SERVER" -p "$MSF_PORT" -S >"$MSF_LOG" 2>&1 &
else