PentestAgent Podman Quadlet Stack

This workspace provides a rootless Podman Quadlet setup with:

  • Shared bridge network: pentestagent-net
  • Pod: pentestagent
  • Containers in same pod/network namespace:
    • ollama (ollama/ollama:rocm)
    • litellm (ghcr.io/berriai/litellm:main-stable)
    • pentestagent (ghcr.io/gh05tcrew/pentestagent:latest)
    • pentestagent-kali (ghcr.io/gh05tcrew/pentestagent:kali) (optional)
    • zap (zaproxy/zap-stable) (optional, for OWASP ZAP)
    • mcp-zap-server (ghcr.io/dtkmn/mcp-zap-server:latest) (optional, MCP ZAP Server)

1) Install files

Run on the Linux host where Podman + systemd user services run:

mkdir -p ~/.config/containers/systemd
mkdir -p ~/.config/pentestagent
mkdir -p ~/.local/share/pentestagent/{ollama,loot,workspace,zap}

cp quadlet/*.network ~/.config/containers/systemd/
cp quadlet/*.pod ~/.config/containers/systemd/
cp quadlet/ollama.container ~/.config/containers/systemd/
cp quadlet/litellm.container ~/.config/containers/systemd/
cp quadlet/pentestagent.container ~/.config/containers/systemd/
cp quadlet/pentestagent-kali.container ~/.config/containers/systemd/
cp quadlet/zap.container ~/.config/containers/systemd/
cp quadlet/mcp-zap-server.container ~/.config/containers/systemd/
cp config/litellm-config.yaml ~/.config/pentestagent/litellm-config.yaml
cp config/pentestagent.env.example ~/.config/pentestagent/pentestagent.env

1b) Proxmox LXC / root login (no user bus)

If you are in an unprivileged LXC and operating as root, systemctl --user often fails with:

$DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined

Use system scope Quadlet units instead:

mkdir -p /etc/containers/systemd
mkdir -p /etc/pentestagent
mkdir -p /var/lib/pentestagent/{ollama,loot,workspace,zap}

cp quadlet/*.network /etc/containers/systemd/
cp quadlet/*.pod /etc/containers/systemd/
cp quadlet/ollama.container /etc/containers/systemd/
cp quadlet/litellm.container /etc/containers/systemd/
cp quadlet/pentestagent.container /etc/containers/systemd/
cp quadlet/pentestagent-kali.container /etc/containers/systemd/
cp quadlet/zap.container /etc/containers/systemd/
cp quadlet/mcp-zap-server.container /etc/containers/systemd/
cp config/litellm-config.yaml /etc/pentestagent/litellm-config.yaml
cp config/pentestagent.env.example /etc/pentestagent/pentestagent.env

Rewrite all copied *.container files for system scope:

chmod +x scripts/prepare-system-quadlet.sh
./scripts/prepare-system-quadlet.sh

This script applies these replacements to all /etc/containers/systemd/*.container files:

  • %h/.config/pentestagent/pentestagent.env/etc/pentestagent/pentestagent.env
  • %h/.config/pentestagent/litellm-config.yaml/etc/pentestagent/litellm-config.yaml
  • %h/.local/share/pentestagent/.../var/lib/pentestagent/...

Start units with systemd system scope:

systemctl daemon-reload
systemctl start pentestagent-network.service
systemctl start pentestagent-pod.service
systemctl start ollama.service
systemctl start litellm.service
systemctl start zap.service
systemctl start mcp-zap-server.service

# Choose one runtime container (standard OR kali)
systemctl start pentestagent.service
# systemctl start pentestagent-kali.service

Note: Quadlet-generated .service units are transient/generated, so systemctl enable may fail. Auto-start behavior comes from the Quadlet file [Install] section during generation.

2) Edit environment

Edit ~/.config/pentestagent/pentestagent.env and set at least:

  • PENTESTAGENT_MODEL=ollama/llama3.1 (or another local model)
  • Optional API keys for non-local providers

If you set LITELLM_MASTER_KEY, clients calling LiteLLM must include it.

3) Start with systemd user units

systemctl --user daemon-reload
systemctl --user start pentestagent-network.service
systemctl --user start pentestagent-pod.service
systemctl --user start ollama.service
systemctl --user start litellm.service
systemctl --user start zap.service
systemctl --user start mcp-zap-server.service

# Choose one runtime container (standard OR kali)
systemctl --user start pentestagent.service
# systemctl --user start pentestagent-kali.service

Check status/logs:

systemctl --user status ollama litellm pentestagent
journalctl --user -u ollama -u litellm -u pentestagent -f

4) Use services

  • LiteLLM endpoint: http://127.0.0.1:4000
  • Ollama endpoint: http://127.0.0.1:11434
  • MCP ZAP Server (HTTP MCP): http://127.0.0.1:7456/mcp

4b) Wire MCP ZAP into PentestAgent

GH05TCREW/pentestagent supports MCP server config via mcp_servers.json and supports type: "sse" with optional bearer auth.

Use this example as a starting point:

cp config/mcp_servers.zap.example.json mcp_servers.json

Then set the bearer token to the same value as MCP_API_KEY from your pentestagent.env:

{
  "mcpServers": {
    "zap-mcp-server": {
      "type": "sse",
      "description": "OWASP ZAP MCP server via streamable HTTP",
      "url": "http://127.0.0.1:7456/mcp",
      "bearer": "<MCP_API_KEY>"
    }
  }
}

Validate from inside your PentestAgent environment:

pentestagent mcp list
pentestagent mcp test zap-mcp-server

If mcp_servers.json is not in the current working directory, PentestAgent also checks:

  • ./mcp.json
  • ~/.pentestagent/mcp_servers.json

Optional: no-auth lab profile (development only)

If you explicitly set the MCP ZAP server to no-auth mode in your env:

# in ~/.config/pentestagent/pentestagent.env (or /etc/pentestagent/pentestagent.env)
MCP_SECURITY_MODE=none

you can use this client config without a bearer token:

cp config/mcp_servers.zap.noauth.example.json mcp_servers.json

This mode is for trusted/local lab setups only.

Helper: switch MCP client profile automatically

Use the helper script to generate mcp_servers.json for PentestAgent:

chmod +x scripts/switch-zap-mcp-profile.sh

# Auth profile (auto-reads MCP_API_KEY from env file)
./scripts/switch-zap-mcp-profile.sh auth

# Auth profile with explicit token/path
./scripts/switch-zap-mcp-profile.sh auth --bearer "<MCP_API_KEY>" --output /workspace/mcp_servers.json

# System scope env lookup (/etc/pentestagent/pentestagent.env)
./scripts/switch-zap-mcp-profile.sh auth --scope system

# No-auth lab profile
./scripts/switch-zap-mcp-profile.sh noauth

Before first use, pull your selected Ollama model (required):

chmod +x scripts/pull-ollama-model.sh

# system scope (LXC/root default)
./scripts/pull-ollama-model.sh --model qwen3.5:35b --scope system

# user scope
./scripts/pull-ollama-model.sh --model qwen3.5:35b --scope user

Launch the TUI inside the running standard container:

podman exec -it pentestagent pentestagent

Launch the TUI inside the Kali container (if enabled):

podman exec -it pentestagent-kali python3 -m pentestagent

Or run with explicit model routing through LiteLLM:

podman exec -it pentestagent env PENTESTAGENT_MODEL=ollama/qwen3.5:35b pentestagent

Non-interactive run syntax (task is positional, not --task):

podman exec -it pentestagent-kali env PENTESTAGENT_DEBUG=true PENTESTAGENT_MODEL=ollama/qwen3.5:35b \
  python3 -m pentestagent run -t shad-base.com "Use terminal tool once to run: echo TOOL_OK, then summarize."

Notes

  • ollama/ollama:rocm requires AMD ROCm-compatible host/device support.
  • If ROCm devices are unavailable, replace the image with ollama/ollama:latest and remove AddDevice lines in quadlet/ollama.container.
  • Run either pentestagent.service or pentestagent-kali.service to avoid duplicate idle runtime containers.

ROCm in LXC troubleshooting

If you see Error: no devices found in /dev/dri: invalid argument, it usually means a Quadlet tried to map the /dev/dri directory instead of specific device nodes.

The ROCm unit in this repo uses explicit nodes (/dev/kfd and /dev/dri/renderD128|renderD129) with optional mapping (AddDevice=-...) so missing nodes do not hard-fail.

For system-scope LXC/root installs, re-copy and restart:

cp quadlet/ollama.container /etc/containers/systemd/ollama.container
./scripts/prepare-system-quadlet.sh
systemctl daemon-reload
systemctl restart ollama.service

If your render node differs, edit /etc/containers/systemd/ollama.container and set the correct AddDevice=-/dev/dri/renderD* entries.

If you previously copied quadlet/*.container, remove legacy conflicting CPU unit once:

rm -f /etc/containers/systemd/ollama-cpu.container
systemctl stop ollama-cpu.service 2>/dev/null || true
systemctl reset-failed ollama-cpu.service 2>/dev/null || true
systemctl daemon-reload

If you see container name "ollama" is already in use, remove stale leftovers and restart:

podman rm -f ollama 2>/dev/null || true
systemctl restart ollama.service

If you see Dependency failed for litellm.service, it usually means ollama.service failed first. This stack now uses a soft dependency (Wants=), so LiteLLM can still start while Ollama recovers.

If litellm.service exits with status 1/FAILURE, check these common causes:

  • stale container name collision (litellm already exists)
  • strict master_key config with missing/blank LITELLM_MASTER_KEY

If you see OllamaException - {"error":"model '...' not found"}, the model has not been pulled into Ollama yet.

./scripts/pull-ollama-model.sh --model qwen3.5:35b --scope system
podman exec ollama ollama list

If you see Agent returned empty response and debug output includes functions_unsupported_model, the selected model is not returning tool calls reliably for this workflow.

Switch to a tool-calling fallback model:

./scripts/pull-ollama-model.sh --model llama3.1:8b --scope system

Set model consistently in both files:

  • /etc/pentestagent/pentestagent.env:
    • PENTESTAGENT_MODEL=ollama/llama3.1:8b
  • /etc/pentestagent/litellm-config.yaml:
    • include ollama/llama3.1:8b in model_list (already present in this repo config)

Apply changes:

cp config/litellm-config.yaml /etc/pentestagent/litellm-config.yaml
systemctl restart litellm.service pentestagent-kali.service

If that model name is unavailable on your host, switch to an installed model and keep naming consistent:

  • in /etc/pentestagent/pentestagent.env: PENTESTAGENT_MODEL=ollama/<installed_model>:<tag>
  • in /etc/pentestagent/litellm-config.yaml: set both model_name and litellm_params.model to the same ollama/<installed_model>:<tag>

Then reload:

systemctl daemon-reload
systemctl restart litellm.service pentestagent.service

Recovery (system scope / LXC root):

cp quadlet/litellm.container /etc/containers/systemd/litellm.container
cp config/litellm-config.yaml /etc/pentestagent/litellm-config.yaml
./scripts/prepare-system-quadlet.sh
systemctl daemon-reload
podman rm -f litellm 2>/dev/null || true
systemctl restart litellm.service

Debug logs:

journalctl -u litellm -n 200 --no-pager
podman logs litellm 2>/dev/null || true

If you see FileNotFoundError: ... 'loot/reports', the process is running with an invalid current working directory. This stack now sets WorkingDir=/workspace for runtime containers.

Immediate workaround on existing running container:

podman exec -it -w /workspace pentestagent-kali sh -lc 'mkdir -p /workspace/loot/reports /workspace/loot/artifacts/screenshots && python3 -m pentestagent run -t shad-base.com "Use terminal tool once to run: echo TOOL_OK, then summarize."'

Persistent fix (system scope / LXC root):

cp quadlet/pentestagent.container /etc/containers/systemd/pentestagent.container
cp quadlet/pentestagent-kali.container /etc/containers/systemd/pentestagent-kali.container
./scripts/prepare-system-quadlet.sh
systemctl daemon-reload
systemctl restart pentestagent.service pentestagent-kali.service

Ollama profile switch (ROCm ↔ CPU fallback)

This repo includes both:

  • quadlet/ollama.container (ROCm)
  • quadlet/ollama-cpu.container.example (CPU fallback profile template)

Use the helper script:

chmod +x scripts/switch-ollama-profile.sh

# user scope (default)
./scripts/switch-ollama-profile.sh cpu
./scripts/switch-ollama-profile.sh rocm

# system scope (LXC/root)
./scripts/switch-ollama-profile.sh cpu --scope system
./scripts/switch-ollama-profile.sh rocm --scope system

Manual fallback (if you prefer not to run scripts):

# Switch to CPU fallback (user scope)
cp quadlet/ollama-cpu.container.example ~/.config/containers/systemd/ollama.container
rm -f ~/.config/containers/systemd/ollama-cpu.container
systemctl --user stop ollama-cpu.service 2>/dev/null || true
systemctl --user daemon-reload
systemctl --user restart ollama.service

# Switch to CPU fallback (system scope / LXC root)
cp quadlet/ollama-cpu.container.example /etc/containers/systemd/ollama.container
rm -f /etc/containers/systemd/ollama-cpu.container
systemctl stop ollama-cpu.service 2>/dev/null || true
sed -i 's|%h/.local/share/pentestagent|/var/lib/pentestagent|g' /etc/containers/systemd/ollama.container
systemctl daemon-reload
systemctl restart ollama.service

# user scope
cp quadlet/ollama.container ~/.config/containers/systemd/ollama.container
systemctl --user daemon-reload
systemctl --user restart ollama.service

# system scope
cp quadlet/ollama.container /etc/containers/systemd/ollama.container
sed -i 's|%h/.local/share/pentestagent|/var/lib/pentestagent|g' /etc/containers/systemd/ollama.container
systemctl daemon-reload
systemctl restart ollama.service

Uninstall

Use the helper script:

chmod +x scripts/uninstall-pentestagent-quadlet.sh

# user scope
./scripts/uninstall-pentestagent-quadlet.sh

# system scope (LXC/root)
./scripts/uninstall-pentestagent-quadlet.sh --scope system

# also remove config/data
./scripts/uninstall-pentestagent-quadlet.sh --scope system --remove-data

Manual uninstall (if you prefer not to run scripts):

User scope uninstall:

systemctl --user disable --now pentestagent.service pentestagent-kali.service litellm.service ollama.service pentestagent-pod.service pentestagent-network.service 2>/dev/null || true
systemctl --user stop ollama-cpu.service 2>/dev/null || true
systemctl --user daemon-reload

rm -f ~/.config/containers/systemd/pentestagent.network
rm -f ~/.config/containers/systemd/pentestagent.pod
rm -f ~/.config/containers/systemd/ollama.container
rm -f ~/.config/containers/systemd/ollama-cpu.container
rm -f ~/.config/containers/systemd/litellm.container
rm -f ~/.config/containers/systemd/pentestagent.container
rm -f ~/.config/containers/systemd/pentestagent-kali.container

# Optional: remove config/data
rm -rf ~/.config/pentestagent
rm -rf ~/.local/share/pentestagent

System scope uninstall (LXC/root):

systemctl disable --now pentestagent.service pentestagent-kali.service litellm.service ollama.service pentestagent-pod.service pentestagent-network.service 2>/dev/null || true
systemctl stop ollama-cpu.service 2>/dev/null || true
systemctl daemon-reload

rm -f /etc/containers/systemd/pentestagent.network
rm -f /etc/containers/systemd/pentestagent.pod
rm -f /etc/containers/systemd/ollama.container
rm -f /etc/containers/systemd/ollama-cpu.container
rm -f /etc/containers/systemd/litellm.container
rm -f /etc/containers/systemd/pentestagent.container
rm -f /etc/containers/systemd/pentestagent-kali.container

# Optional: remove config/data
rm -rf /etc/pentestagent
rm -rf /var/lib/pentestagent
S
Description
No description provided
Readme 58 KiB
Languages
Shell 100%