Overview
For production deployments and security-sensitive environments, PentAGI supports a distributed two-node architecture where worker operations are isolated on a separate server. This prevents untrusted code execution and network access issues on your main system.
Architecture
The two-node setup separates concerns between a main control node and a worker execution node:
Benefits
Isolated Execution Worker containers run on dedicated hardware, preventing compromise of the main system.
Network Isolation Separate network boundaries for penetration testing activities.
Security Boundaries Docker-in-Docker with TLS authentication prevents unauthorized access.
OOB Attack Support Dedicated port ranges for out-of-band attack techniques.
Prerequisites
Before starting, prepare:
Two Linux servers (Ubuntu 20.04+ recommended)
Docker and Docker Compose on both nodes
Network connectivity between nodes
Minimum 2 vCPU and 4GB RAM on each node
Private network IP address for the worker node
Set worker node IP address
Export the private IP that will be used throughout setup: export PRIVATE_IP = 192.168.10.10 # Replace with your worker node IP
Part 1: Install Docker (Both Nodes)
Execute these commands on both the main node and worker node .
Add Docker's official GPG key
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
Add Docker repository
echo \
"deb [arch=$( dpkg --print-architecture ) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$( . /etc/os-release && echo "${ UBUNTU_CODENAME :- $VERSION_CODENAME }") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
Install Docker packages
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
The following steps are performed only on the worker node .
Host Docker Configuration
Install certificate management tools
sudo apt install easy-rsa
Create PKI infrastructure for host Docker
sudo mkdir -p /etc/easy-rsa/docker-host
cd /etc/easy-rsa/docker-host
sudo /usr/share/easy-rsa/easyrsa init-pki
sudo /usr/share/easy-rsa/easyrsa build-ca nopass
Generate server certificate with SAN
export EASYRSA_EXTRA_EXTS = "subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = docker
DNS.3 = docker-host
IP.1 = 127.0.0.1
IP.2 = 0.0.0.0
IP.3 = ${ PRIVATE_IP }"
sudo /usr/share/easy-rsa/easyrsa build-server-full server nopass
unset EASYRSA_EXTRA_EXTS
Confirm with ‘yes’ when prompted.
Generate client certificate
sudo /usr/share/easy-rsa/easyrsa build-client-full client nopass
Confirm with ‘yes’ when prompted.
Copy certificates to Docker directory
# Server certificates
sudo mkdir -p /etc/docker/certs/server
sudo cp pki/ca.crt /etc/docker/certs/server/ca.pem
sudo cp pki/issued/server.crt /etc/docker/certs/server/cert.pem
sudo cp pki/private/server.key /etc/docker/certs/server/key.pem
# Client certificates
sudo mkdir -p /etc/docker/certs/client
sudo cp pki/ca.crt /etc/docker/certs/client/ca.pem
sudo cp pki/issued/client.crt /etc/docker/certs/client/cert.pem
sudo cp pki/private/client.key /etc/docker/certs/client/key.pem
Configure Docker daemon with TLS
sudo cat > /etc/docker/daemon.json << EOF
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "2",
"compress": "true"
},
"dns-opts": [
"ndots:1"
],
"metrics-addr": "${ PRIVATE_IP }:9323",
"tls": true,
"tlscacert": "/etc/docker/certs/server/ca.pem",
"tlscert": "/etc/docker/certs/server/cert.pem",
"tlskey": "/etc/docker/certs/server/key.pem",
"tlsverify": true
}
EOF
Enable TCP listening
sudo sed -i "s|ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock|ExecStart=/usr/bin/dockerd -H fd:// -H tcp://${ PRIVATE_IP }:2376 --containerd=/run/containerd/containerd.sock|" /lib/systemd/system/docker.service
Restart Docker service
sudo systemctl daemon-reload
sudo systemctl restart docker
Create TLS test script
sudo cat > /usr/local/bin/docker-host-tls << 'EOF'
#!/bin/bash
export DOCKER_HOST=tcp://${PRIVATE_IP}:2376
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH=/etc/docker/certs/client
if [ $# -eq 0 ]; then
echo "Docker API connection configured:"
echo " Host: ${PRIVATE_IP}:2376"
echo " TLS: enabled"
echo " Certificates: /etc/docker/certs/client/"
echo ""
echo "Usage: docker-host-tls [docker-commands]"
exit 0
fi
exec docker "$@"
EOF
sudo chmod +x /usr/local/bin/docker-host-tls
Test TLS connection
docker-host-tls ps
docker-host-tls info
Docker-in-Docker (dind) Configuration
Create PKI infrastructure for dind
sudo mkdir -p /etc/easy-rsa/docker-dind
cd /etc/easy-rsa/docker-dind
sudo /usr/share/easy-rsa/easyrsa init-pki
sudo /usr/share/easy-rsa/easyrsa build-ca nopass
Generate dind server certificate
export EASYRSA_EXTRA_EXTS = "subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = docker
DNS.3 = docker-dind
IP.1 = 127.0.0.1
IP.2 = 0.0.0.0
IP.3 = ${ PRIVATE_IP }"
sudo /usr/share/easy-rsa/easyrsa build-server-full server nopass
unset EASYRSA_EXTRA_EXTS
Generate dind client certificate
sudo /usr/share/easy-rsa/easyrsa build-client-full client nopass
Copy dind certificates
sudo mkdir -p /etc/docker/certs/dind/{ca,client,server}
# CA certificates
sudo cp pki/ca.crt /etc/docker/certs/dind/ca/cert.pem
sudo cp pki/private/ca.key /etc/docker/certs/dind/ca/key.pem
# Server certificates
sudo cp pki/ca.crt /etc/docker/certs/dind/server/ca.pem
sudo cp pki/issued/server.crt /etc/docker/certs/dind/server/cert.pem
sudo cp pki/private/server.key /etc/docker/certs/dind/server/key.pem
# Client certificates
sudo cp pki/ca.crt /etc/docker/certs/dind/client/ca.pem
sudo cp pki/issued/client.crt /etc/docker/certs/dind/client/cert.pem
sudo cp pki/private/client.key /etc/docker/certs/dind/client/key.pem
Create dind management script
sudo cat > /usr/local/bin/run-dind << EOF
#!/bin/bash
if docker ps -a --format '{{.Names}}' | grep -q "^docker-dind$"; then
if ! docker ps --format '{{.Names}}' | grep -q "^docker-dind$"; then
echo "Starting existing docker-dind container..."
docker start docker-dind
else
echo "docker-dind container is already running."
fi
else
echo "Creating new docker-dind container..."
docker run -d \
--privileged \
-v /etc/docker/certs/dind/server:/certs/server:ro \
-v /var/lib/docker-dind:/var/lib/docker \
-v /var/run/docker-dind:/var/run/dind \
-p ${ PRIVATE_IP }:3376:2376 \
-p ${ PRIVATE_IP }:9324:9324 \
--cpus 2 --memory 2G \
--name docker-dind \
--restart always \
--log-opt max-size=50m \
--log-opt max-file=7 \
docker:dind \
--host=unix:///var/run/dind/docker.sock \
--host=tcp://0.0.0.0:2376 \
--tls=true \
--tlscert=/certs/server/cert.pem \
--tlskey=/certs/server/key.pem \
--tlscacert=/certs/server/ca.pem \
--tlsverify=true \
--metrics-addr=0.0.0.0:9324
echo "docker-dind container created and started."
fi
EOF
sudo chmod +x /usr/local/bin/run-dind
Create dind test scripts
TLS access: sudo cat > /usr/local/bin/docker-dind-tls << 'EOF'
#!/bin/bash
export DOCKER_HOST=tcp://${PRIVATE_IP}:3376
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH=/etc/docker/certs/dind/client
if [ $# -eq 0 ]; then
echo "Docker dind API connection configured:"
echo " Host: ${PRIVATE_IP}:3376"
echo " TLS: enabled"
exit 0
fi
exec docker "$@"
EOF
sudo chmod +x /usr/local/bin/docker-dind-tls
Socket access: sudo cat > /usr/local/bin/docker-dind-sock << 'EOF'
#!/bin/bash
export DOCKER_HOST=unix:///var/run/docker-dind/docker.sock
export DOCKER_TLS_VERIFY=
export DOCKER_CERT_PATH=
if [ $# -eq 0 ]; then
echo "Docker dind socket connection configured:"
echo " Host: unix:///var/run/docker-dind/docker.sock"
exit 0
fi
exec docker "$@"
EOF
sudo chmod +x /usr/local/bin/docker-dind-sock
Test dind connections
docker-dind-tls ps
docker-dind-sock ps
Part 3: Network and Security
Required Ports
Configure your firewall to allow the following connections from the main node to the worker node:
Port Service Description 2376 Host Docker API TLS-secured Docker daemon for worker management 3376 dind API TLS-secured Docker-in-Docker daemon 9323 Host Docker Metrics Prometheus metrics endpoint 9324 dind Metrics Prometheus metrics endpoint 28000-30000 OOB Attack Ports Out-of-band attack technique ports
Firewall Configuration
# Allow Docker API access from main node
sudo ufw allow from < MAIN_NODE_I P > to any port 2376 proto tcp
sudo ufw allow from < MAIN_NODE_I P > to any port 3376 proto tcp
sudo ufw allow from < MAIN_NODE_I P > to any port 9323 proto tcp
sudo ufw allow from < MAIN_NODE_I P > to any port 9324 proto tcp
# Allow OOB ports from target networks
sudo ufw allow 28000:30000/tcp
The OOB port range (28000-30000) should be accessible from networks you’re testing against. Configure your perimeter firewall accordingly.
Part 4: Transfer Certificates to Main Node
Execute these commands to transfer certificates from the worker node to the main node.
Create certificate archives on worker node
# Host Docker certificates
sudo tar czf docker-host-ssl.tar.gz -C /etc/docker/certs client/
# dind certificates
sudo tar czf docker-dind-ssl.tar.gz -C /etc/docker/certs/dind client/
Transfer to main node
Replace <MAIN_NODE_IP> with your main node’s IP address: scp docker-host-ssl.tar.gz root@ < MAIN_NODE_I P > :/opt/pentagi/
scp docker-dind-ssl.tar.gz root@ < MAIN_NODE_I P > :/opt/pentagi/
Extract certificates on main node
# On the main node
cd /opt/pentagi
# Extract host Docker certificates
tar xzf docker-host-ssl.tar.gz
mv client docker-host-ssl
rm docker-host-ssl.tar.gz
# Extract dind certificates
tar xzf docker-dind-ssl.tar.gz
mv client docker-dind-ssl
rm docker-dind-ssl.tar.gz
Verify certificate structure
ls -la /opt/pentagi/docker-host-ssl/
ls -la /opt/pentagi/docker-dind-ssl/
Each directory should contain:
ca.pem (Certificate Authority)
cert.pem (Client certificate)
key.pem (Client private key)
Part 5: Install PentAGI on Main Node
Download and run installer
mkdir -p /opt/pentagi && cd /opt/pentagi
wget -O installer.zip https://pentagi.com/downloads/linux/amd64/installer-latest.zip
unzip installer.zip
sudo ./installer
Complete basic installation
Follow the installer prompts to configure:
LLM providers
Search engines
Security settings
Optional services
Configure Docker environment
After installation, access the installer again: Navigate to: Tools → Docker Environment
Enter Docker configuration
Configure for host Docker with dind socket mapping:
Docker Access : true
Network Admin : true
Docker Socket : /var/run/docker-dind/docker.sock
Docker Network : pentagi-network
Public IP Address : ${PRIVATE_IP} (worker node IP)
Work Directory : Leave empty
Default Image : debian:latest or leave empty
Pentesting Image : vxcontrol/kali-linux or leave empty
Docker Host : tcp://${PRIVATE_IP}:2376
TLS Verification : 1
TLS Certificates : /opt/pentagi/docker-host-ssl
Configure for direct dind access:
Docker Access : true
Network Admin : true
Docker Socket : Leave empty
Docker Network : pentagi-network
Public IP Address : ${PRIVATE_IP} (worker node IP)
Work Directory : Leave empty
Default Image : debian:latest or leave empty
Pentesting Image : vxcontrol/kali-linux or leave empty
Docker Host : tcp://${PRIVATE_IP}:3376
TLS Verification : 1
TLS Certificates : /opt/pentagi/docker-dind-ssl
Save and restart
Save the configuration and restart PentAGI services for changes to take effect.
Verification
Test the distributed setup:
Verify worker node connectivity
From the main node, test Docker connection: DOCKER_HOST = tcp:// ${ PRIVATE_IP } :2376 \
DOCKER_TLS_VERIFY=1 \
DOCKER_CERT_PATH=/opt/pentagi/docker-host-ssl \
docker ps
Create a test flow in PentAGI
Login to PentAGI web interface
Create a new penetration test flow
Observe worker containers being created on the worker node
Monitor worker node
On the worker node: # Watch containers being created
watch docker ps
# Check dind containers
docker-dind-sock ps
Troubleshooting
Verify:
Certificates are readable by PentAGI container
CA, cert, and key files are present
Certificate paths in configuration are correct
Test manually: openssl s_client -connect ${ PRIVATE_IP } :2376 \
-cert /opt/pentagi/docker-host-ssl/cert.pem \
-key /opt/pentagi/docker-host-ssl/key.pem \
-CAfile /opt/pentagi/docker-host-ssl/ca.pem
Worker containers not starting
Check:
Worker node Docker is running: docker ps
dind container is running: docker ps | grep docker-dind
Network connectivity: ping ${PRIVATE_IP}
Firewall rules allow connections
PentAGI logs: docker compose logs pentagi
Verify:
Public IP address is set correctly
Port range 28000-30000 is accessible from target network
Firewall rules on worker node allow inbound connections
Target systems can reach the worker node IP
Monitoring
Integrate worker node metrics with PentAGI observability:
Configure Prometheus scraping
Add to observability/otel/config.yml: scrape_configs :
- job_name : 'docker-host'
static_configs :
- targets : [ '${PRIVATE_IP}:9323' ]
- job_name : 'docker-dind'
static_configs :
- targets : [ '${PRIVATE_IP}:9324' ]
Restart observability stack
docker compose -f docker-compose-observability.yml restart
View metrics in Grafana
Access Grafana at http://localhost:3000 and create dashboards for:
Worker node Docker metrics
Container resource usage
Network throughput
dind performance
Next Steps
First Pentest Run your first distributed pentest
Best Practices Security guidelines for distributed testing
Advanced Techniques Leverage worker isolation for advanced testing
Custom Assistants Configure assistants for distributed mode