Architecture Overview
The distributed two-node architecture isolates worker operations on a separate server, preventing untrusted code execution and network access issues on your main system.
Connection Modes
Standard Mode
Direct Mode
PentAGI → Host Docker → Workers use dind via socket mapping
PentAGI connects to worker node’s host Docker via TLS
Host Docker creates worker containers
Workers mount dind socket for nested Docker operations
Best for most production deployments
PentAGI → dind (creates workers directly)
PentAGI connects directly to dind via TLS
dind creates and manages all worker containers
No socket mapping needed
Simpler setup, fewer moving parts
Benefits
Isolated Execution Worker containers run on dedicated hardware away from main node
Network Isolation Separate network boundaries for penetration testing
Security Boundaries Docker-in-Docker with TLS authentication prevents unauthorized access
OOB Attack Support Dedicated port ranges for out-of-band exploitation techniques
Prerequisites
Set the worker node private IP address:
export PRIVATE_IP = 192.168.10.10 # Replace with your worker node IP
Docker must be installed on both the worker node and main node. Follow the installation steps on each server separately.
Install Docker on Both Nodes
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 CE and plugins
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Generate TLS Certificates
Install easy-rsa
sudo apt install easy-rsa
Create PKI infrastructure
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
Generate client certificate
sudo /usr/share/easy-rsa/easyrsa build-client-full client nopass
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
Create daemon configuration
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
Apply configuration
sudo systemctl daemon-reload
sudo systemctl restart docker
Create TLS Test Script
Create test wrapper script
sudo cat > /usr/local/bin/docker-host-tls << 'EOF'
#!/bin/bash
# Docker API client wrapper for TLS connections
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 ""
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
Generate TLS Certificates for dind
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
Create dind startup 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
fi
EOF
sudo chmod +x /usr/local/bin/run-dind
Create dind Test Scripts
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
docker-dind-tls ps
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"
exit 0
fi
exec docker "$@"
EOF
sudo chmod +x /usr/local/bin/docker-dind-sock
docker-dind-sock ps
Network Configuration
Required Ports
Port Service Description 2376 Host Docker API TLS-secured Docker daemon 3376 dind API TLS-secured Docker-in-Docker 9323 Host Metrics Prometheus metrics for host Docker 9324 dind Metrics Prometheus metrics for dind 28000-30000 OOB Attacks Dynamic port range for worker containers
Firewall Rules
Allow Docker API access from main node
sudo ufw allow from < MAIN_NODE_I P > to any port 2376 proto tcp comment 'Docker host API'
sudo ufw allow from < MAIN_NODE_I P > to any port 3376 proto tcp comment 'Docker dind API'
sudo ufw allow from < MAIN_NODE_I P > to any port 9323 proto tcp comment 'Docker metrics'
sudo ufw allow from < MAIN_NODE_I P > to any port 9324 proto tcp comment 'dind metrics'
Allow OOB attack ports from target networks
sudo ufw allow 28000:30000/tcp comment 'OOB attack range'
sudo ufw allow 28000:30000/udp comment 'OOB attack range'
Each worker container (pentagi-terminal-N) allocates 2 ports from range 28000-30000 for Out-of-Band attack techniques. Ensure perimeter firewall permits OOB traffic from target networks.
Transfer Certificates to Main Node
Copy host Docker certificates
# On worker node
sudo tar czf docker-host-ssl.tar.gz -C /etc/docker/certs client/
scp docker-host-ssl.tar.gz root@ < MAIN_NODE_I P > :/opt/pentagi/
# On main node
cd /opt/pentagi
tar xzf docker-host-ssl.tar.gz
mv client docker-host-ssl
rm docker-host-ssl.tar.gz
Copy dind certificates
# On worker node
sudo tar czf docker-dind-ssl.tar.gz -C /etc/docker/certs/dind client/
scp docker-dind-ssl.tar.gz root@ < MAIN_NODE_I P > :/opt/pentagi/
# On main node
cd /opt/pentagi
tar xzf docker-dind-ssl.tar.gz
mv client docker-dind-ssl
rm docker-dind-ssl.tar.gz
Verify certificate structure
# Expected files in each directory
ls -la /opt/pentagi/docker-host-ssl/
ls -la /opt/pentagi/docker-dind-ssl/
# Should contain:
# ca.pem (Certificate Authority)
# cert.pem (Client certificate)
# key.pem (Client private key)
Install PentAGI on Main Node
Download installer
mkdir -p /opt/pentagi && cd /opt/pentagi
wget -O installer.zip https://pentagi.com/downloads/linux/amd64/installer-latest.zip
unzip installer.zip
Run installer
The installer requires root privileges to interact with Docker API. For production, run with sudo. For development, add user to docker group.
Configure Docker environment via installer
After installation completes:
Navigate to Tools → Docker Environment
Configure based on connection mode:
PentAGI connects to host Docker, workers use dind via socket mapping Field Value Docker Access trueNetwork Admin trueDocker Socket /var/run/docker-dind/docker.sockDocker Network pentagi-networkPublic IP Address ${PRIVATE_IP}Work Directory Leave empty Default Image debian:latestPentesting Image vxcontrol/kali-linuxDocker Host tcp://${PRIVATE_IP}:2376TLS Verification 1TLS Certificates /opt/pentagi/docker-host-ssl
PentAGI connects directly to dind Field Value Docker Access trueNetwork Admin trueDocker Socket Leave empty Docker Network pentagi-networkPublic IP Address ${PRIVATE_IP}Work Directory Leave empty Default Image debian:latestPentesting Image vxcontrol/kali-linuxDocker Host tcp://${PRIVATE_IP}:3376TLS Verification 1TLS Certificates /opt/pentagi/docker-dind-ssl
Certificate directories will be automatically mounted into the PentAGI container for TLS authentication.
Monitoring Integration
To integrate worker node metrics with PentAGI observability stack:
Edit OpenTelemetry configuration
On main node, edit observability/otel/config.yml: receivers :
prometheus :
config :
scrape_configs :
- job_name : 'docker-engine-worker'
scrape_interval : 15s
static_configs :
- targets :
- '${PRIVATE_IP}:9323' # Host Docker
- '${PRIVATE_IP}:9324' # dind
Restart observability stack
docker compose -f docker-compose.yml -f docker-compose-observability.yml restart otel
Verification
Test worker container creation
From PentAGI UI, create a new task that requires a worker container.
Verify containers on worker node
# On worker node - check host Docker
docker ps | grep pentagi-terminal
# Check dind containers
docker-dind-tls ps
Test OOB port allocation
Worker containers should have 2 ports allocated from range 28000-30000: docker ps --format '{{.Names}}\t{{.Ports}}' | grep pentagi-terminal
Next Steps
Production Hardening Security best practices and optimization
Troubleshooting Common worker node issues
Docker Configuration Advanced Docker settings
Network Configuration Network isolation and firewall rules