> This documentation is organized by AI with reference to actual code. AI can make mistakes — please verify against the source code when in doubt. # Web Wireshark Feature - Business Process Documentation ## Overview The **Web Wireshark** feature enables users to run Wireshark packet capture analysis directly in a web browser without requiring a desktop environment or VNC connection. This is achieved through an **xpra** (persistent remote applications) HTML5 client running inside a Docker container. ## Feature Summary - **Core Capability**: Web-based packet capture visualization using Wireshark - **Technology Stack**: Docker container + xpra + HTML5 WebSocket proxy - **Target Users**: Network engineers and students who need to analyze network traffic in GNS3 topologies - **Key Benefit**: Zero-install, browser-based packet capture analysis --- ## Installation Before using Web Wireshark, install the GNS3 server and set up the Docker image: ```bash # Development install pip install -e . && gns3server-web-wireshark-setup # Production install pip install gns3-server && gns3server-web-wireshark-setup ``` This command will: 1. Install the gns3-server package 2. Pull the `gns3/web-wireshark:latest` image from Docker Hub 3. If pull fails, build the image locally using the included Dockerfile The `wireshark` command shows the raw output from `docker pull` or `docker build`, allowing you to see the full progress. --- ## Architecture Overview ```mermaid graph TD subgraph GNS3Server["GNS3 Server"] WebUI["Web UI
(Browser)"] Controller["GNS3 Controller"] LinkCtrl["Link Controller
(start_capture / stop_capture)"] Manager["WebWiresharkManager
(called directly, not via CLI)"] DockerClient["DockerHTTPClient
(HTTP via Unix Socket)"] end subgraph DockerDaemon["Docker Daemon"] Container["gns3-wireshark-{project_id}"] Xvfb["Xvfb (virtual framebuffer)"] Xpra["xpra server"] Wireshark["Wireshark"] end ClientBrowser["Client Browser
(HTML5 Client)"] ClientBrowser -->|REST API| WebUI WebUI -->|capture/start| Controller Controller --> LinkCtrl LinkCtrl -->|start/stop/restart session| Manager Manager -->|Docker API
/var/run/docker.sock| DockerClient DockerClient -->|container lifecycle| Container Container --- Xvfb Container --- Xpra Container --- Wireshark ClientBrowser -.->|WebSocket proxy| Xpra Xpra -.->|xpra HTML5| ClientBrowser style GNS3Server fill:#e8f4fd,stroke:#2196f3 style DockerDaemon fill:#fff3e0,stroke:#ff9800 style ClientBrowser fill:#e8f5e9,stroke:#4caf50 ``` --- ## Component Description ### 1. Web UI (Client Browser) - User interface for starting/stopping packet capture - Receives WebSocket URL for connecting to xpra HTML5 client - No plugins required - pure HTML5/JavaScript ### 2. GNS3 Controller - Orchestrates the capture workflow - Validates user permissions (RBAC) - Manages link capture state ### 3. manage_wireshark.py (Management CLI) - Command-line interface for container and session management - **For manual debugging and testing only** — the server calls `WebWiresharkManager` directly at runtime - Handles Docker container lifecycle - Manages xpra sessions per link ### 4. WebWiresharkManager - Core business logic for Web Wireshark - Handles container creation, session startup/shutdown - Deterministic port allocation based on link_id ### 5. DockerHTTPClient - Async HTTP client for Docker API - Communicates via Unix socket (/var/run/docker.sock) - Manages container lifecycle ### 6. Docker Container (gns3-wireshark-{project_id}) - Runs xpra server with HTML5 support - Contains Xvfb (virtual framebuffer) for headless Wireshark - Streams display to browser via WebSocket --- ## Business Processes ### Process 1: Start Packet Capture with Web Wireshark ```mermaid sequenceDiagram actor User participant WebUI as Web UI participant Controller as Controller participant LinkCtrl as Link Controller participant Manager as WebWiresharkManager User->>WebUI: 1. Start Capture WebUI->>Controller: 2. POST /capture/start Controller->>LinkCtrl: 3. _start_web_wireshark(jwt_token) LinkCtrl->>Manager: 4. start_wireshark_session() Note right of Manager: 5. Ensure network exists Note right of Manager: 6. Get/create container Note right of Manager: 7. Start xpra session
(display + port) Note right of Manager: 8. Start Wireshark
(curl | wireshark -i - -k) Manager-->>LinkCtrl: ws_url LinkCtrl-->>Controller: ws_url Controller-->>WebUI: 10. {capturing: true, ws_url: "ws://..."} WebUI-->>User: 9. ws_url Note over User,Manager: 11-12. Browser connects via WebSocket
Server proxies to container xpra ``` **API Endpoint**: `POST /v3/projects/{project_id}/links/{link_id}/capture/start` **Request Body**: ```json { "wireshark": true, "data_link_type": "DLT_EN10MB", "capture_file_name": "capture.pcap" } ``` **Response**: ```json { "id": "link-uuid", "capturing": true, "ws_url": "ws://192.168.1.100:14500" } ``` ### Process 2: Container Lifecycle (Per Project) ```mermaid flowchart TD A["Project Open"] --> B["First Link Capture"] B --> C["Container Created
(one per project)"] C --> D["Container Running"] subgraph SessionArchitecture["Container Session Architecture"] direction TB D --> E["gns3-wireshark-{project_id}"] subgraph Link1Session["Link 1 Session"] L1Xpra["xpra :14503"] L1Xvfb["Xvfb :14503
(1920x1080x24)"] L1WS["Wireshark
(curl | wireshark -i -)"] L1Bind["bind-ws=0.0.0.0:14503"] L1Xpra --- L1Xvfb L1Xpra --- L1WS L1Xpra --- L1Bind end subgraph Link2Session["Link 2 Session"] L2Xpra["xpra :11024"] L2Xvfb["Xvfb :11024
(1920x1080x24)"] L2WS["Wireshark
(curl | wireshark -i -)"] L2Bind["bind-ws=0.0.0.0:11024"] L2Xpra --- L2Xvfb L2Xpra --- L2WS L2Xpra --- L2Bind end E --> Link1Session E --> Link2Session Note1["display = port = 10000 + hash(link_id) % 10000
Each xpra creates its own Xvfb (NOT shared)"] end D --> F["Project Close"] F --> G["Stop Container
(all sessions terminate)"] G --> H["Container Stopped
(preserved for reuse)"] H -->|Project Reopened| D I["Project Delete"] --> J["Delete Container"] J --> K["Container Removed"] style SessionArchitecture fill:#fff8e1,stroke:#ffa000 style Link1Session fill:#e3f2fd,stroke:#1976d2 style Link2Session fill:#e8f5e9,stroke:#388e3c ``` ### Process 3: WebSocket Connection Flow ```mermaid sequenceDiagram participant Browser as Client Browser participant Server as GNS3 Server (API Route) participant Container as Docker Container (xpra) Browser->>Server: 1. WSS Connect (ws://.../web-wireshark?token=jwt) Note right of Server: 2. Validate JWT
(RBAC: Link.Capture) Note right of Server: 3. Get container IP
from Docker API Server-->>Browser: 4. Accept connection Note right of Server: 5. Start WebSocket proxy Server->>Container: 6. Connect to xpra
ws://container:port Container-->>Server: 7. xpra validates subprotocol Note over Browser,Container: 8-9. Bidirectional WebSocket proxy
Browser ⟺ Server ⟺ Container Note left of Browser: 10. HTML5 client renders
Wireshark window ``` **WebSocket Endpoint**: `ws://host/v3/projects/{project_id}/links/{link_id}/capture/web-wireshark?token=` ### Process 4: Capture Data Flow ```mermaid flowchart LR subgraph GNS3Node["GNS3 Node"] Router["Router / Switch"] end subgraph ComputeNode["Compute Node"] Capture["Link Capture Buffer"] end Router -->|"TAP (raw packets)"| Capture subgraph GNS3Server["GNS3 Server"] APIRoute["GET /capture/stream
Proxies pcap stream"] end Capture -->|"pcap stream"| APIRoute subgraph DockerContainer["Docker Container"] direction TB Curl["curl -N -H 'Authorization: Bearer {jwt}'
'{server}/capture/stream'"] Wireshark["wireshark -i - -k"] Xvfb["Xvfb :display
(1920x1080x24)"] Xpra["xpra :display
ws://0.0.0.0:{port}"] Curl -->|"stdin pipe"| Wireshark Wireshark -->|"renders to"| Xvfb Xvfb -->|"X11 display"| Xpra end APIRoute -.->|"pcap stream
(curl fetches from container)"| Curl subgraph Browser["Client Browser"] HTML5Client["HTML5/xpra Client"] WSUI["Wireshark Web Interface"] HTML5Client --> WSUI end Xpra <-->|"WebSocket"| HTML5Client style GNS3Node fill:#f3e5f5,stroke:#7b1fa2 style ComputeNode fill:#e8eaf6,stroke:#283593 style GNS3Server fill:#e8f4fd,stroke:#2196f3 style DockerContainer fill:#fff3e0,stroke:#ff9800 style Browser fill:#e8f5e9,stroke:#4caf50 ``` --- ## Port Allocation Strategy ### Deterministic Port Mapping ```mermaid flowchart LR LinkID["link_id (UUID)"] --> Hash["MD5 Hash"] --> Modulo["hash % 10000"] Modulo --> Offset["+ 10000"] Offset --> Result["display = port
Range: 10000 - 19999"] style LinkID fill:#e3f2fd,stroke:#1976d2 style Result fill:#e8f5e9,stroke:#388e3c ``` | link_id | port/display | |---------|-------------| | `f233f27f-7432-49c3-9aa2-50e326a10eec` | 14503 | | `a1b2c3d4-1234-5678-90ab-cdef12345678` | 11024 | | `12345678-90ab-cdef-1234-567890abcdef` | 17892 | **Benefits**: - Same link always gets same port (deterministic) - Display number = port number (same hash) - No port conflicts between sessions - Easy to predict and debug --- ## Network Architecture ```mermaid graph TD subgraph HostMachine["Host Machine"] subgraph GNS3ServerHost["GNS3 Server
192.168.1.100:3080"] WSProxy["WebSocket Proxy"] end subgraph DockerNetwork["Docker Bridge: gns3-wireshark
Subnet: 172.31.0.0/22"] Gateway["Bridge Gateway
172.31.0.1"] Container["gns3-wireshark-{project_id}
172.31.0.x"] Gateway --- Container end end Client["Client Browser"] -->|"ws://192.168.1.100:3080
/capture/web-wireshark"| WSProxy WSProxy -->|"via gateway 172.31.0.1
ws://172.31.0.x:{port}"| Container style HostMachine fill:#fafafa,stroke:#9e9e9e style DockerNetwork fill:#e3f2fd,stroke:#1976d2 style GNS3ServerHost fill:#e8f5e9,stroke:#388e3c ``` --- ## Session Management Commands > **Note**: These commands are for manual debugging and testing only. The GNS3 server calls `WebWiresharkManager` directly at runtime. ### start Starts Web Wireshark session for a specific link. | Argument | Required | Default | Description | |----------|----------|---------|-------------| | `--project-id` | Yes | - | Project UUID | | `--link-id` | Yes | - | Link UUID | | `--jwt-token` | Yes | - | JWT authentication token | | `--capture-url` | No | auto-detected | PCAP stream URL | | `--image` | No | `gns3/web-wireshark:latest` | Docker image | | `--memory` | No | `2g` | Memory limit | | `--cpus` | No | `1.0` | CPU cores | | `--pids-limit` | No | `1000` | Process limit | ```bash python manage_wireshark.py start \ --project-id "5af0fe00-..." \ --link-id "f233f27f-..." \ --jwt-token "eyJhbG..." ``` ### Other Commands | Command | Description | Key Arguments | |---------|-------------|---------------| | `stop` | Stop session for a specific link | `--project-id`, `--link-id` | | `restart` | Restart session (reopens Wireshark window) | `--project-id`, `--link-id`, `--jwt-token` | | `stop-all` | Stop all sessions for a project | `--project-id` | | `delete` | Delete container (alias for delete-container) | `--project-id` | | `stop-container` | Stop container without deleting | `--project-id` | | `delete-container` | Delete the container | `--project-id` | --- ## Project Close/Delete Workflow ```mermaid flowchart TD A["Project Close"] --> B["_stop_web_wireshark_container()"] B --> C["WebWiresharkManager.stop_container(project_id)"] C --> D["docker stop
(all xpra/Xvfb/Wireshark terminate)"] D --> E["Container Stopped
(preserved for reuse)"] E -->|Project Reopened| F["First Capture Start"] F --> G["docker start
(container already exists, fast)"] G --> H["New xpra sessions created"] I["Project Delete"] --> J["_cleanup_web_wireshark_container()"] J --> K["WebWiresharkManager.delete_container(project_id)"] K --> L["docker rm
(container removed)"] style A fill:#e3f2fd,stroke:#1976d2 style I fill:#ffebee,stroke:#c62828 style E fill:#fff8e1,stroke:#ffa000 style L fill:#ffebee,stroke:#c62828 ``` --- ## API Endpoints Summary | Method | Endpoint | Description | Privilege | |--------|----------|-------------|-----------| | POST | `/v3/projects/{id}/links/{id}/capture/start` | Start capture with Web Wireshark | Link.Capture | | POST | `/v3/projects/{id}/links/{id}/capture/stop` | Stop capture | Link.Capture | | POST | `/v3/projects/{id}/links/{id}/capture/wireshark/restart` | Restart Wireshark window | Link.Capture | | GET | `/v3/projects/{id}/links/{id}/capture/stream` | Stream PCAP data | Link.Capture | | GET | `/v3/projects/{id}/links/{id}/capture/file` | Download PCAP file | Link.Capture | | WS | `/v3/projects/{id}/links/{id}/capture/web-wireshark` | WebSocket proxy for xpra | Link.Capture | --- ## Security Considerations 1. **RBAC Authentication**: All endpoints require `Link.Capture` privilege 2. **JWT Token Validation**: WebSocket connections validate JWT token 3. **WebSocket Subprotocol Negotiation**: Proper xpra subprotocol handling 4. **Container Isolation**: Each project gets its own container with isolated resources 5. **Network Segmentation**: Container runs on isolated Docker network (not host network) --- ## Performance Characteristics ### Startup & Shutdown Performance | Step | Before Optimization | After Optimization | Improvement | |------|---------------------|-------------------|-------------| | **Health Check** | ~1.0s | ~0s | Docker native status | | **Gateway Detection** | 0.85s | ~0.001s | Docker API vs exec | | **Process Cleanup** | ~850ms | ~40ms | Host perspective recursive tree walk | | **Xpra Startup** | 6.4s | ~3s | HTML5 client disabled | | **Wireshark Launch** | ~1s | ~1s | No change | | **Total Startup** | **~15s** | **~5-6s** | **67% faster** | | Step | Before Optimization | After Optimization | Improvement | |------|---------------------|-------------------|-------------| | **Process Termination** | ~8s | ~20-40ms | Recursive tree walk, no orphans | | **File Cleanup** | ~850ms | ~850ms | Docker exec (safety) | | **Total Shutdown** | **~9s** | **~2s** | **78% faster** | #### Startup Breakdown: First vs Subsequent | Phase | First Startup (Container stopped) | Subsequent Startup (Container running) | |-------|-----------------------------------|----------------------------------------| | Container startup | ~1-2s | ~0s (already running) | | Container health check | ~1s (unhealthy→healthy) | ~0s (already healthy) | | Gateway detection | ~0.001s | ~0.001s | | Process cleanup | ~40ms | ~40ms | | Xpra startup | ~3s | ~3s | | Wireshark launch | ~1s | ~1s | | **Total** | **~6s** | **~5s** | #### Measured Performance Data **Startup (from production logs):** ``` 13:46:53 → 13:46:59 = 6s (first startup with container start) 13:47:38 → 13:47:43 = 5s (subsequent startup, container running) ``` **Shutdown (from production logs):** ``` 13:48:15 → 13:48:17 = 2s (complete cleanup, no orphan processes) 13:48:50 → 13:48:52 = 2s (complete cleanup, no orphan processes) ``` #### Key Optimizations - **Complete Process Cleanup**: Recursive process tree traversal eliminates orphaned processes (Xvfb, pulseaudio, ibus-daemon) - **Fast Gateway Detection**: Docker API query instead of container exec - **Smart Health Check**: Trust Docker built-in status, no manual ping - **Xpra Optimization**: Disabled unnecessary HTML5 client (`--html=off`) - **Reduced Docker Exec Calls**: Combined X lock and xpra socket cleanup into single exec call ### Docker Exec Performance Limitations **Important**: Docker daemon has internal queuing for concurrent exec requests to the same container. #### Test Results (Same Container) | Test Scenario | Execution Time | Avg Per Exec | |--------------|----------------|--------------| | Single docker exec | 0.854s | 0.854s | | Serial 7 docker exec | 7.225s | 1.032s | | Parallel 7 docker exec | 10.253s | 1.465s | **Key Finding**: Parallel execution is **42% slower** than serial execution. ``` Serial: 7.225s (7 requests processed sequentially) Parallel: 10.253s (Docker daemon still processes sequentially + context switch overhead) ``` #### Impact on Web Wireshark When stopping multiple capture sessions quickly: - Each stop requires 1 docker exec (cleanup files) - Docker daemon processes exec requests sequentially - 7 sessions × ~1s each = ~7-10 seconds total - User requests appear to "queue" even though they're concurrent #### Optimization Strategy Since Docker exec cannot be parallelized effectively: 1. **Minimize exec calls** - Already implemented: 2 calls → 1 call 2. **Accept serial processing** - No benefit to parallel execution 3. **Focus on fast exec content** - Use simple `rm -f` commands #### Future Optimization Options - Use Docker API instead of exec (requires container filesystem access) - Delay cleanup to next startup (increases startup complexity) - Batch multiple stops into single operation (requires API changes) ### Resource Usage (Per Wireshark Instance) | Resource | Typical Usage | |----------|---------------| | Memory | 150-250 MB | | CPU | 0.5-2% (idle to active) | | Threads | ~30 threads | | Disk I/O | Minimal | ### Container Configuration Configured via `WebWiresharkSettings` in `gns3server/schemas/config.py`: | Parameter | Default | Recommended | Description | |-----------|---------|-------------|-------------| | enabled | true | - | Enable/disable Web Wireshark feature | | image | gns3/web-wireshark:latest | - | Docker image name | | network_subnet | 172.31.0.0/22 | - | Docker bridge network subnet | | Memory | 2GB | 2-4GB | Container memory limit | | CPUs | 1.0 | 1.0-2.0 | Container CPU limit | | PIDs Limit | 1000 | 1000 | Container process limit | ### Scaling Guidelines | Instances | Memory | Use Case | |-----------|--------|----------| | 1-3 | 450-750 MB | Light projects | | 4-6 | 600-1.5 GB | Medium projects | | 7-10 | 1-2.5 GB | Large projects | | 10+ | >2.5 GB | Increase memory | --- ## Docker API Compatibility ### Version Negotiation Mechanism The Web Wireshark feature implements automatic Docker API version negotiation to ensure compatibility across different Docker versions: 1. **Primary**: Try API version 1.44 first - Supports Docker 29.3+ (API 1.54+), which requires minimum API 1.44 2. **Fallback**: If server rejects 1.44 with 400 error, downgrade to API 1.40 - Supports Docker 20.10 (API 1.41) ### Tested Configurations | Docker Version | API Version | API 1.40 | API 1.44 | Solution | |----------------|-------------|----------|----------|----------| | 20.10 | 1.41 | ✓ | ✗ | Fallback to 1.40 | | 29.3+ | 1.54+ | ✗ | ✓ | Use 1.44 | ### Container IP Retrieval The `get_container_ip()` method implements a dual-strategy approach for retrieving container IP addresses: 1. **Primary Method**: Query Docker Container API - Uses `docker inspect` via HTTP API - Safe access to `NetworkSettings.Networks` field using `.get()` - Handles missing fields gracefully (fixes KeyError bug) 2. **Fallback Method**: Execute `hostname -I` command inside container - Works when Docker API response lacks network information - Compatible with containers without `ip` command - Returns first IP address from `hostname -I` output This dual approach ensures compatibility across different Docker API versions that may have varying response formats. --- ## Known Limitations 1. **JWT Token Visibility**: Token passed via command-line arguments (visible in `/proc//cmdline`) 2. **Single Container**: All Wireshark instances run in a single container per project 3. **Docker Dependency**: Requires Docker daemon running on the server 4. **Browser Support**: Requires modern browser with WebSocket support 5. **Port Range**: Limited to 10,000 unique ports (10000-19999) --- ## File Structure ``` gns3server/ ├── controller/ │ ├── link.py # Link capture lifecycle │ └── project.py # Project cleanup hooks ├── api/routes/controller/ │ └── links.py # REST/WebSocket API endpoints └── agent/web_wireshark/ ├── setup_wireshark_image.py # Docker image setup tool ├── manage_wireshark.py # CLI management tool (manual/debug use only) ├── manager.py # Session management logic (called by server) ├── docker_client.py # Docker API client ├── stats.py # Container statistics collection ├── docker/ │ └── Dockerfile # Container image definition └── WEB_WIRESHARK.md # Technical documentation ```