mirror of
https://github.com/trycua/computer.git
synced 2026-02-07 06:09:53 -06:00
Merge pull request #622 from synacktraa/feat/qemu-linux-container
Add QEMU Ubuntu 22.04 template with CUA computer-server support
This commit is contained in:
29
.github/workflows/docker-publish-cua-linux.yml
vendored
Normal file
29
.github/workflows/docker-publish-cua-linux.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Build and Publish CUA Linux Container
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- "docker-cua-linux-v*.*.*"
|
||||
paths:
|
||||
- "libs/qemu-docker/linux/**"
|
||||
- ".github/workflows/docker-publish-cua-linux.yml"
|
||||
- ".github/workflows/docker-reusable-publish.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "libs/qemu-docker/linux/**"
|
||||
- ".github/workflows/docker-publish-cua-linux.yml"
|
||||
- ".github/workflows/docker-reusable-publish.yml"
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
uses: ./.github/workflows/docker-reusable-publish.yml
|
||||
with:
|
||||
image_name: cua-linux
|
||||
context_dir: libs/qemu-docker/linux
|
||||
dockerfile_path: Dockerfile
|
||||
tag_prefix: docker-cua-linux-v
|
||||
docker_hub_org: trycua
|
||||
secrets:
|
||||
DOCKER_HUB_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
**/image/setup.iso
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
14
libs/qemu-docker/linux/Dockerfile
Normal file
14
libs/qemu-docker/linux/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM trycua/qemu-local:latest
|
||||
|
||||
COPY src/vm/setup/. /oem/
|
||||
|
||||
COPY --chmod=755 src/entry.sh /entry.sh
|
||||
|
||||
ENV RAM_SIZE="8G"
|
||||
ENV CPU_CORES="8"
|
||||
ENV DISK_SIZE="64G"
|
||||
ENV ARGUMENTS="-qmp tcp:0.0.0.0:7200,server,nowait"
|
||||
|
||||
EXPOSE 5000 8006
|
||||
|
||||
ENTRYPOINT ["/entry.sh"]
|
||||
146
libs/qemu-docker/linux/README.md
Normal file
146
libs/qemu-docker/linux/README.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# CUA Linux Container
|
||||
|
||||
Containerized Ubuntu 22.04 LTS virtual desktop for Computer-Using Agents (CUA). Utilizes QEMU/KVM with Ubuntu Desktop and computer-server pre-installed for remote computer control.
|
||||
|
||||
## Features
|
||||
|
||||
- Ubuntu 22.04 LTS Desktop running in QEMU/KVM
|
||||
- Automated installation via cloud-init autoinstall
|
||||
- Pre-installed CUA computer-server for remote computer control
|
||||
- Support for custom OEM scripts during setup
|
||||
- noVNC access for visual desktop interaction
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Download Ubuntu Server ISO
|
||||
|
||||
**Download Ubuntu 22.04 LTS Server ISO:**
|
||||
|
||||
1. Visit & download the [server ISO](https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso)
|
||||
2. After downloading, rename the file to `setup.iso`
|
||||
3. Copy it to the directory `src/vm/image/`
|
||||
|
||||
This ISO is used for automated Ubuntu installation with cloud-init on first run.
|
||||
|
||||
### 2. Build the Image
|
||||
|
||||
```bash
|
||||
docker build -t cua-linux:dev .
|
||||
```
|
||||
|
||||
### 3. First Run - Create Golden Image
|
||||
|
||||
On first run, the container will install Ubuntu from scratch and create a golden image. This takes 15-30 minutes.
|
||||
|
||||
```bash
|
||||
# Create storage directory
|
||||
mkdir -p ./storage
|
||||
|
||||
# Run with ubuntu.iso to create golden image
|
||||
docker run -it --rm \
|
||||
--device=/dev/kvm \
|
||||
--name cua-linux \
|
||||
--mount type=bind,source=/path/to/ubuntu.iso,target=/custom.iso \
|
||||
--cap-add NET_ADMIN \
|
||||
-v $(pwd)/storage:/storage \
|
||||
-p 8006:8006 \
|
||||
-p 5000:5000 \
|
||||
-e RAM_SIZE=8G \
|
||||
-e CPU_CORES=4 \
|
||||
-e DISK_SIZE=64G \
|
||||
cua-linux:dev
|
||||
```
|
||||
|
||||
**What happens during first run:**
|
||||
|
||||
1. Ubuntu 22.04 Server installs automatically using cloud-init autoinstall
|
||||
2. Minimal desktop environment is installed with auto-login enabled
|
||||
3. OEM setup scripts install Python 3, create venv, and install CUA computer-server
|
||||
4. systemd service created for CUA server (runs automatically on login)
|
||||
5. X11 access configured for GUI automation
|
||||
6. Golden image is saved to `/storage` directory
|
||||
7. Container exits after setup completes
|
||||
|
||||
### 4. Subsequent Runs - Use Golden Image
|
||||
|
||||
After the golden image is created, subsequent runs boot much faster (30 sec - 2 min):
|
||||
|
||||
```bash
|
||||
# Run without ubuntu.iso - uses existing golden image
|
||||
docker run -it --rm \
|
||||
--device=/dev/kvm \
|
||||
--name cua-linux \
|
||||
--cap-add NET_ADMIN \
|
||||
-v $(pwd)/storage:/storage \
|
||||
-p 8006:8006 \
|
||||
-p 5000:5000 \
|
||||
-e RAM_SIZE=8G \
|
||||
-e CPU_CORES=4 \
|
||||
cua-linux:dev
|
||||
```
|
||||
|
||||
**Access points:**
|
||||
|
||||
- **Computer Server API**: `http://localhost:5000`
|
||||
- **noVNC Browser**: `http://localhost:8006`
|
||||
|
||||
## Container Configuration
|
||||
|
||||
### Ports
|
||||
|
||||
- **5000**: CUA computer-server API endpoint
|
||||
- **8006**: noVNC web interface for visual desktop access
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- `RAM_SIZE`: RAM allocated to Ubuntu VM (default: "8G", recommended: "8G" for WSL2)
|
||||
- `CPU_CORES`: CPU cores allocated to VM (default: "8")
|
||||
- `DISK_SIZE`: VM disk size (default: "64G", minimum: "32G")
|
||||
|
||||
### Volumes
|
||||
|
||||
- `/storage`: Persistent VM storage (golden image, disk)
|
||||
- `/custom.iso`: Mount point for ubuntu.iso (only needed for first run)
|
||||
- `/oem`: Optional mount point for custom OEM scripts (built-in scripts included in image)
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Docker Container (Linux host) │
|
||||
│ │
|
||||
│ • Port forwarding: localhost:5000 → EMULATOR_IP:5000 │
|
||||
│ • Exposes: 5000 (API), 8006 (noVNC) │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────┐ │
|
||||
│ │ QEMU VM (Ubuntu 22.04) │ │
|
||||
│ │ │ │
|
||||
│ │ • CUA computer-server listens on 5000 │ │
|
||||
│ │ │ │
|
||||
│ └────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Communication Flow:**
|
||||
|
||||
1. External client → `localhost:5000` (host)
|
||||
2. Docker port mapping → Container's `localhost:5000`
|
||||
3. Container detects VM IP and waits for server to be ready
|
||||
4. CUA computer-server in Ubuntu VM processes request
|
||||
|
||||
## Development
|
||||
|
||||
### Modifying Setup Scripts
|
||||
|
||||
Setup scripts are in `src/vm/setup/`:
|
||||
|
||||
- `install.sh`: Entry point called after cloud-init installation (runs OEM setup)
|
||||
- `setup.sh`: Main setup orchestration (copies scripts to /opt/oem)
|
||||
- `setup-cua-server.sh`: CUA server installation with isolated venv and systemd service
|
||||
|
||||
After modifying, rebuild the image:
|
||||
|
||||
```bash
|
||||
docker build -t cua-linux:dev .
|
||||
```
|
||||
61
libs/qemu-docker/linux/src/entry.sh
Normal file
61
libs/qemu-docker/linux/src/entry.sh
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
cleanup() {
|
||||
echo "Received signal, shutting down gracefully..."
|
||||
if [ -n "$VM_PID" ]; then
|
||||
kill -TERM "$VM_PID" 2>/dev/null
|
||||
wait "$VM_PID" 2>/dev/null
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Install trap for signals
|
||||
trap cleanup SIGTERM SIGINT SIGHUP SIGQUIT
|
||||
|
||||
# Start the VM in the background
|
||||
echo "Starting Ubuntu VM..."
|
||||
/usr/bin/tini -s /run/entry.sh &
|
||||
VM_PID=$!
|
||||
echo "Live stream accessible at localhost:8006"
|
||||
|
||||
echo "Waiting for Ubuntu to boot and CUA computer-server to start..."
|
||||
|
||||
VM_IP=""
|
||||
while true; do
|
||||
# Wait for VM and get the IP
|
||||
if [ -z "$VM_IP" ]; then
|
||||
VM_IP=$(ps aux | grep dnsmasq | grep -oP '(?<=--dhcp-range=)[0-9.]+' | head -1)
|
||||
if [ -n "$VM_IP" ]; then
|
||||
echo "Detected VM IP: $VM_IP"
|
||||
else
|
||||
echo "Waiting for VM to start..."
|
||||
sleep 5
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if server is ready
|
||||
response=$(curl --write-out '%{http_code}' --silent --output /dev/null $VM_IP:5000/status)
|
||||
|
||||
if [ "${response:-0}" -eq 200 ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
echo "Waiting for CUA computer-server to be ready. This might take a while..."
|
||||
sleep 5
|
||||
done
|
||||
|
||||
echo "VM is up and running, and the CUA Computer Server is ready!"
|
||||
|
||||
echo "Computer server accessible at localhost:5000"
|
||||
|
||||
# Detect initial setup by presence of custom ISO
|
||||
CUSTOM_ISO=$(find / -maxdepth 1 -type f -iname "*.iso" -print -quit 2>/dev/null || true)
|
||||
if [ -n "$CUSTOM_ISO" ]; then
|
||||
echo "Preparation complete. Shutting down gracefully..."
|
||||
cleanup
|
||||
fi
|
||||
|
||||
# Keep container alive for golden image boots
|
||||
echo "Container running. Press Ctrl+C to stop."
|
||||
tail -f /dev/null
|
||||
7
libs/qemu-docker/linux/src/vm/image/README.md
Normal file
7
libs/qemu-docker/linux/src/vm/image/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
> Add your Ubuntu 22.04 live server setup.iso to this folder
|
||||
|
||||
**Download Ubuntu 22.04 LTS Server ISO:**
|
||||
|
||||
1. Visit & download the [server ISO](https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso)
|
||||
2. After downloading, rename the file to `setup.iso`
|
||||
3. Copy it to the current directory.
|
||||
26
libs/qemu-docker/linux/src/vm/setup/install.sh
Normal file
26
libs/qemu-docker/linux/src/vm/setup/install.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
# OEM Installation Entry Point for Linux
|
||||
# This script is called by the OEM systemd service on first boot
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="/opt/oem"
|
||||
LOG_FILE="$SCRIPT_DIR/setup.log"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
log "=== Starting OEM Setup ==="
|
||||
|
||||
# Run main setup script
|
||||
if [ -f "$SCRIPT_DIR/setup.sh" ]; then
|
||||
log "Running setup.sh..."
|
||||
bash "$SCRIPT_DIR/setup.sh" 2>&1 | tee -a "$LOG_FILE"
|
||||
log "setup.sh completed with exit code: $?"
|
||||
else
|
||||
log "ERROR: setup.sh not found at $SCRIPT_DIR/setup.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "=== OEM Setup Completed ==="
|
||||
135
libs/qemu-docker/linux/src/vm/setup/setup-cua-server.sh
Normal file
135
libs/qemu-docker/linux/src/vm/setup/setup-cua-server.sh
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/bin/bash
|
||||
# Setup CUA Computer Server on Linux
|
||||
# Creates a system-level systemd service to run computer server in background
|
||||
|
||||
set -e
|
||||
|
||||
USER_NAME="docker"
|
||||
USER_HOME="/home/$USER_NAME"
|
||||
SCRIPT_DIR="/opt/oem"
|
||||
CUA_DIR="/opt/cua-server"
|
||||
VENV_DIR="$CUA_DIR/venv"
|
||||
SERVICE_NAME="cua-computer-server"
|
||||
LOG_FILE="$SCRIPT_DIR/setup.log"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
log "=== Installing CUA Computer Server ==="
|
||||
|
||||
# Install Python 3 and venv
|
||||
log "Installing Python 3 and dependencies..."
|
||||
sudo apt-get install -y python3 python3-venv python3-pip python3-tk python3-dev
|
||||
|
||||
# Create CUA directory
|
||||
log "Creating CUA directory at $CUA_DIR..."
|
||||
sudo mkdir -p "$CUA_DIR"
|
||||
sudo chown "$USER_NAME:$USER_NAME" "$CUA_DIR"
|
||||
|
||||
# Create virtual environment
|
||||
if [ -f "$VENV_DIR/bin/python" ]; then
|
||||
log "Existing venv detected; skipping creation"
|
||||
else
|
||||
log "Creating Python virtual environment at $VENV_DIR..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
log "Virtual environment created successfully"
|
||||
fi
|
||||
|
||||
# Activate and install packages
|
||||
log "Upgrading pip, setuptools, and wheel..."
|
||||
"$VENV_DIR/bin/pip" install --upgrade pip setuptools wheel
|
||||
|
||||
log "Installing cua-computer-server..."
|
||||
"$VENV_DIR/bin/pip" install --upgrade cua-computer-server
|
||||
log "cua-computer-server installed successfully"
|
||||
|
||||
# Open firewall for port 5000 (if ufw is available)
|
||||
if command -v ufw &> /dev/null; then
|
||||
log "Opening firewall for port 5000..."
|
||||
sudo ufw allow 5000/tcp || true
|
||||
log "Firewall rule added"
|
||||
fi
|
||||
|
||||
# Create start script with auto-restart
|
||||
START_SCRIPT="$CUA_DIR/start-server.sh"
|
||||
log "Creating start script at $START_SCRIPT..."
|
||||
|
||||
cat > "$START_SCRIPT" << 'EOF'
|
||||
#!/bin/bash
|
||||
# CUA Computer Server Start Script with auto-restart
|
||||
|
||||
CUA_DIR="/opt/cua-server"
|
||||
VENV_DIR="$CUA_DIR/venv"
|
||||
LOG_FILE="$CUA_DIR/server.log"
|
||||
|
||||
start_server() {
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Updating cua-computer-server..." >> "$LOG_FILE"
|
||||
"$VENV_DIR/bin/pip" install --upgrade cua-computer-server >> "$LOG_FILE" 2>&1
|
||||
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Starting CUA Computer Server on port 5000..." >> "$LOG_FILE"
|
||||
"$VENV_DIR/bin/python" -m computer_server --port 5000 >> "$LOG_FILE" 2>&1
|
||||
return $?
|
||||
}
|
||||
|
||||
while true; do
|
||||
start_server
|
||||
EXIT_CODE=$?
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Server exited with code: $EXIT_CODE. Restarting in 5s..." >> "$LOG_FILE"
|
||||
sleep 5
|
||||
done
|
||||
EOF
|
||||
|
||||
chmod +x "$START_SCRIPT"
|
||||
log "Start script created"
|
||||
|
||||
# Create xhost script for X11 access
|
||||
log "Creating xhost script..."
|
||||
sudo tee /etc/X11/Xsession.d/99xauth > /dev/null << 'EOF'
|
||||
#!/bin/sh
|
||||
# Grant local X11 access for CUA Computer Server
|
||||
export DISPLAY=:0
|
||||
xhost +local: 2>/dev/null || true
|
||||
EOF
|
||||
sudo chmod +x /etc/X11/Xsession.d/99xauth
|
||||
log "X11 access script created"
|
||||
|
||||
# Create system-level systemd service
|
||||
log "Creating systemd system service..."
|
||||
|
||||
sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null << EOF
|
||||
[Unit]
|
||||
Description=CUA Computer Server
|
||||
After=graphical.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=$START_SCRIPT
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
Environment=DISPLAY=:0
|
||||
Environment=XAUTHORITY=$USER_HOME/.Xauthority
|
||||
User=$USER_NAME
|
||||
WorkingDirectory=$CUA_DIR
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical.target
|
||||
EOF
|
||||
|
||||
log "Systemd service created at /etc/systemd/system/$SERVICE_NAME.service"
|
||||
|
||||
# Ensure proper ownership of CUA directory
|
||||
log "Setting ownership of $CUA_DIR to $USER_NAME..."
|
||||
sudo chown -R "$USER_NAME:$USER_NAME" "$CUA_DIR"
|
||||
|
||||
# Enable and start the service
|
||||
log "Enabling systemd service..."
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable "$SERVICE_NAME.service"
|
||||
|
||||
log "Starting CUA Computer Server service..."
|
||||
sudo systemctl start "$SERVICE_NAME.service" || true
|
||||
|
||||
log "=== CUA Computer Server setup completed ==="
|
||||
log "Service status: $(sudo systemctl is-active $SERVICE_NAME.service 2>/dev/null || echo 'unknown')"
|
||||
33
libs/qemu-docker/linux/src/vm/setup/setup.sh
Normal file
33
libs/qemu-docker/linux/src/vm/setup/setup.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
# Main Setup Script for Linux
|
||||
# Installs dependencies and sets up CUA Computer Server
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="/opt/oem"
|
||||
LOG_FILE="$SCRIPT_DIR/setup.log"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
log "=== Running Main Setup ==="
|
||||
|
||||
# Update package lists
|
||||
log "Updating package lists..."
|
||||
sudo apt-get update
|
||||
|
||||
# Install Git
|
||||
log "Installing Git..."
|
||||
sudo apt-get install -y git
|
||||
|
||||
# Setup CUA Computer Server
|
||||
log "Setting up CUA Computer Server..."
|
||||
if [ -f "$SCRIPT_DIR/setup-cua-server.sh" ]; then
|
||||
bash "$SCRIPT_DIR/setup-cua-server.sh" 2>&1 | tee -a "$LOG_FILE"
|
||||
log "CUA Computer Server setup completed."
|
||||
else
|
||||
log "ERROR: setup-cua-server.sh not found at $SCRIPT_DIR/setup-cua-server.sh"
|
||||
fi
|
||||
|
||||
log "=== Main Setup Completed ==="
|
||||
Reference in New Issue
Block a user