Skip to main content

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

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

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

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 CE and plugins

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

Configure Host Docker on Worker Node

Generate TLS Certificates

1

Install easy-rsa

sudo apt install easy-rsa
2

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

Generate client certificate

sudo /usr/share/easy-rsa/easyrsa build-client-full client nopass
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

Configure Docker Daemon with TLS

1

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
2

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
3

Apply configuration

sudo systemctl daemon-reload
sudo systemctl restart docker

Create TLS Test Script

1

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
2

Test TLS connection

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

Configure Docker-in-Docker (dind)

Generate TLS Certificates for dind

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

Create dind Management Script

1

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
2

Start dind container

run-dind
docker ps

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

Network Configuration

Required Ports

PortServiceDescription
2376Host Docker APITLS-secured Docker daemon
3376dind APITLS-secured Docker-in-Docker
9323Host MetricsPrometheus metrics for host Docker
9324dind MetricsPrometheus metrics for dind
28000-30000OOB AttacksDynamic port range for worker containers

Firewall Rules

1

Allow Docker API access from main node

sudo ufw allow from <MAIN_NODE_IP> to any port 2376 proto tcp comment 'Docker host API'
sudo ufw allow from <MAIN_NODE_IP> to any port 3376 proto tcp comment 'Docker dind API'
sudo ufw allow from <MAIN_NODE_IP> to any port 9323 proto tcp comment 'Docker metrics'
sudo ufw allow from <MAIN_NODE_IP> to any port 9324 proto tcp comment 'dind metrics'
2

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

1

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

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_IP>:/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
3

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

1

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
2

Run installer

sudo ./installer
The installer requires root privileges to interact with Docker API. For production, run with sudo. For development, add user to docker group.
3

Configure Docker environment via installer

After installation completes:
  1. Navigate to Tools → Docker Environment
  2. Configure based on connection mode:

Configure PentAGI Docker Environment

Certificate directories will be automatically mounted into the PentAGI container for TLS authentication.

Monitoring Integration

To integrate worker node metrics with PentAGI observability stack:
1

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
2

Restart observability stack

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

Verification

1

Test worker container creation

From PentAGI UI, create a new task that requires a worker container.
2

Verify containers on worker node

# On worker node - check host Docker
docker ps | grep pentagi-terminal

# Check dind containers
docker-dind-tls ps
3

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

Build docs developers (and LLMs) love