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:rocmrequires AMD ROCm-compatible host/device support.- If ROCm devices are unavailable, replace the image with
ollama/ollama:latestand removeAddDevicelines inquadlet/ollama.container. - Run either
pentestagent.serviceorpentestagent-kali.serviceto 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 (
litellmalready exists) - strict
master_keyconfig with missing/blankLITELLM_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:8binmodel_list(already present in this repo config)
- include
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 bothmodel_nameandlitellm_params.modelto the sameollama/<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