Skip to main content

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.
This is an advanced setup. For standard deployments, use the Interactive Installer or Manual Setup.

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
1

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.
1

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
2

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
3

Install Docker packages

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Part 2: Configure Worker Node

The following steps are performed only on the worker node.

Host Docker Configuration

1

Install certificate management tools

sudo apt install easy-rsa
2

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
3

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.
4

Generate client certificate

sudo /usr/share/easy-rsa/easyrsa build-client-full client nopass
Confirm with ‘yes’ when prompted.
5

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
6

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
7

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
8

Restart Docker service

sudo systemctl daemon-reload
sudo systemctl restart docker
9

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
10

Test TLS connection

docker-host-tls ps
docker-host-tls info

Docker-in-Docker (dind) Configuration

1

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
2

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
3

Generate dind client certificate

sudo /usr/share/easy-rsa/easyrsa build-client-full client nopass
4

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
5

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
6

Start dind container

run-dind
docker ps
7

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
8

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:
PortServiceDescription
2376Host Docker APITLS-secured Docker daemon for worker management
3376dind APITLS-secured Docker-in-Docker daemon
9323Host Docker MetricsPrometheus metrics endpoint
9324dind MetricsPrometheus metrics endpoint
28000-30000OOB Attack PortsOut-of-band attack technique ports

Firewall Configuration

# Allow Docker API access from main node
sudo ufw allow from <MAIN_NODE_IP> to any port 2376 proto tcp
sudo ufw allow from <MAIN_NODE_IP> to any port 3376 proto tcp
sudo ufw allow from <MAIN_NODE_IP> to any port 9323 proto tcp
sudo ufw allow from <MAIN_NODE_IP> 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.
1

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/
2

Transfer to main node

Replace <MAIN_NODE_IP> with your main node’s IP address:
scp docker-host-ssl.tar.gz root@<MAIN_NODE_IP>:/opt/pentagi/
scp docker-dind-ssl.tar.gz root@<MAIN_NODE_IP>:/opt/pentagi/
3

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
4

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

1

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
2

Complete basic installation

Follow the installer prompts to configure:
  • LLM providers
  • Search engines
  • Security settings
  • Optional services
3

Configure Docker environment

After installation, access the installer again:
./installer
Navigate to: Tools → Docker Environment
4

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
5

Save and restart

Save the configuration and restart PentAGI services for changes to take effect.

Verification

Test the distributed setup:
1

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
2

Create a test flow in PentAGI

  1. Login to PentAGI web interface
  2. Create a new penetration test flow
  3. Observe worker containers being created on the worker node
3

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
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
Optimize:
  • Increase dind container resources (CPU, memory)
  • Use dedicated storage for /var/lib/docker-dind
  • Monitor with metrics endpoints (9323, 9324)
  • Check network latency between nodes

Monitoring

Integrate worker node metrics with PentAGI observability:
1

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']
2

Restart observability stack

docker compose -f docker-compose-observability.yml restart
3

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

Build docs developers (and LLMs) love