Files
claudetools/projects/msp-tools/guru-rmm/agent/src/transport/mod.rs
Mike Swanson 6c316aa701 Add VPN configuration tools and agent documentation
Created comprehensive VPN setup tooling for Peaceful Spirit L2TP/IPsec connection
and enhanced agent documentation framework.

VPN Configuration (PST-NW-VPN):
- Setup-PST-L2TP-VPN.ps1: Automated L2TP/IPsec setup with split-tunnel and DNS
- Connect-PST-VPN.ps1: Connection helper with PPP adapter detection, DNS (192.168.0.2), and route config (192.168.0.0/24)
- Connect-PST-VPN-Standalone.ps1: Self-contained connection script for remote deployment
- Fix-PST-VPN-Auth.ps1: Authentication troubleshooting for CHAP/MSChapv2
- Diagnose-VPN-Interface.ps1: Comprehensive VPN interface and routing diagnostic
- Quick-Test-VPN.ps1: Fast connectivity verification (DNS/router/routes)
- Add-PST-VPN-Route-Manual.ps1: Manual route configuration helper
- vpn-connect.bat, vpn-disconnect.bat: Simple batch file shortcuts
- OpenVPN config files (Windows-compatible, abandoned for L2TP)

Key VPN Implementation Details:
- L2TP creates PPP adapter with connection name as interface description
- UniFi auto-configures DNS (192.168.0.2) but requires manual route to 192.168.0.0/24
- Split-tunnel enabled (only remote traffic through VPN)
- All-user connection for pre-login auto-connect via scheduled task
- Authentication: CHAP + MSChapv2 for UniFi compatibility

Agent Documentation:
- AGENT_QUICK_REFERENCE.md: Quick reference for all specialized agents
- documentation-squire.md: Documentation and task management specialist agent
- Updated all agent markdown files with standardized formatting

Project Organization:
- Moved conversation logs to dedicated directories (guru-connect-conversation-logs, guru-rmm-conversation-logs)
- Cleaned up old session JSONL files from projects/msp-tools/
- Added guru-connect infrastructure (agent, dashboard, proto, scripts, .gitea workflows)
- Added guru-rmm server components and deployment configs

Technical Notes:
- VPN IP pool: 192.168.4.x (client gets 192.168.4.6)
- Remote network: 192.168.0.0/24 (router at 192.168.0.10)
- PSK: rrClvnmUeXEFo90Ol+z7tfsAZHeSK6w7
- Credentials: pst-admin / 24Hearts$

Files: 15 VPN scripts, 2 agent docs, conversation log reorganization,
guru-connect/guru-rmm infrastructure additions

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 11:51:47 -07:00

300 lines
7.1 KiB
Rust

//! Transport layer for agent-server communication
//!
//! Handles WebSocket connection to the GuruRMM server with:
//! - Auto-reconnection on disconnect
//! - Authentication via API key
//! - Sending metrics and receiving commands
//! - Heartbeat to maintain connection
mod websocket;
pub use websocket::WebSocketClient;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
/// Messages sent from agent to server
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "payload")]
#[serde(rename_all = "snake_case")]
pub enum AgentMessage {
/// Authentication message (sent on connect)
Auth(AuthPayload),
/// Metrics report
Metrics(crate::metrics::SystemMetrics),
/// Network state update (sent on connect and when interfaces change)
NetworkState(crate::metrics::NetworkState),
/// Command execution result
CommandResult(CommandResultPayload),
/// Watchdog event (service stopped, restarted, etc.)
WatchdogEvent(WatchdogEventPayload),
/// Update result (success, failure, rollback)
UpdateResult(UpdateResultPayload),
/// Heartbeat to keep connection alive
Heartbeat,
}
/// Authentication payload
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthPayload {
/// API key for this agent (or site)
pub api_key: String,
/// Unique device identifier (hardware-derived)
pub device_id: String,
/// Hostname of this machine
pub hostname: String,
/// Operating system type
pub os_type: String,
/// Operating system version
pub os_version: String,
/// Agent version
pub agent_version: String,
/// Architecture (amd64, arm64, etc.)
#[serde(default = "default_arch")]
pub architecture: String,
/// Previous version if reconnecting after update
#[serde(skip_serializing_if = "Option::is_none")]
pub previous_version: Option<String>,
/// Update ID if reconnecting after update
#[serde(skip_serializing_if = "Option::is_none")]
pub pending_update_id: Option<Uuid>,
}
fn default_arch() -> String {
#[cfg(target_arch = "x86_64")]
{ "amd64".to_string() }
#[cfg(target_arch = "aarch64")]
{ "arm64".to_string() }
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{ "unknown".to_string() }
}
/// Command execution result payload
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommandResultPayload {
/// Command ID (from the server)
pub command_id: Uuid,
/// Exit code (0 = success)
pub exit_code: i32,
/// Standard output
pub stdout: String,
/// Standard error
pub stderr: String,
/// Execution duration in milliseconds
pub duration_ms: u64,
}
/// Watchdog event payload
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WatchdogEventPayload {
/// Service or process name
pub name: String,
/// Event type
pub event: WatchdogEvent,
/// Additional details
pub details: Option<String>,
}
/// Types of watchdog events
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WatchdogEvent {
/// Service/process was found stopped
Stopped,
/// Service/process was restarted by the agent
Restarted,
/// Restart attempt failed
RestartFailed,
/// Max restart attempts reached
MaxRestartsReached,
/// Service/process recovered on its own
Recovered,
}
/// Messages sent from server to agent
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "payload")]
#[serde(rename_all = "snake_case")]
pub enum ServerMessage {
/// Authentication acknowledgment
AuthAck(AuthAckPayload),
/// Command to execute
Command(CommandPayload),
/// Configuration update
ConfigUpdate(ConfigUpdatePayload),
/// Agent update command
Update(UpdatePayload),
/// Acknowledgment of received message
Ack { message_id: Option<String> },
/// Error message
Error { code: String, message: String },
}
/// Authentication acknowledgment payload
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthAckPayload {
/// Whether authentication was successful
pub success: bool,
/// Agent ID assigned by server
pub agent_id: Option<Uuid>,
/// Error message if authentication failed
pub error: Option<String>,
}
/// Command payload from server
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommandPayload {
/// Unique command ID
pub id: Uuid,
/// Type of command
pub command_type: CommandType,
/// Command text to execute
pub command: String,
/// Optional timeout in seconds
pub timeout_seconds: Option<u64>,
/// Whether to run as elevated/admin
pub elevated: bool,
}
/// Types of commands
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CommandType {
/// Shell command (cmd on Windows, bash on Unix)
Shell,
/// PowerShell command (Windows)
PowerShell,
/// Python script
Python,
/// Raw script (requires interpreter path)
Script { interpreter: String },
}
/// Configuration update payload
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigUpdatePayload {
/// New metrics interval (if changed)
pub metrics_interval_seconds: Option<u64>,
/// Updated watchdog config
pub watchdog: Option<WatchdogConfigUpdate>,
}
/// Watchdog configuration update
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WatchdogConfigUpdate {
/// Enable/disable watchdog
pub enabled: Option<bool>,
/// Check interval
pub check_interval_seconds: Option<u64>,
// Services and processes would be included here for remote config updates
}
/// Update command payload from server
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdatePayload {
/// Unique update ID for tracking
pub update_id: Uuid,
/// Target version to update to
pub target_version: String,
/// Download URL for the new binary
pub download_url: String,
/// SHA256 checksum of the binary
pub checksum_sha256: String,
/// Whether to force update (skip version check)
#[serde(default)]
pub force: bool,
}
/// Update result payload sent back to server
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateResultPayload {
/// Update ID (from the server)
pub update_id: Uuid,
/// Update status
pub status: UpdateStatus,
/// Old version before update
pub old_version: String,
/// New version after update (if successful)
pub new_version: Option<String>,
/// Error message if failed
pub error: Option<String>,
}
/// Update status codes
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UpdateStatus {
/// Update starting
Starting,
/// Downloading new binary
Downloading,
/// Download complete, verifying
Verifying,
/// Installing (replacing binary)
Installing,
/// Restarting service
Restarting,
/// Update completed successfully
Completed,
/// Update failed
Failed,
/// Rolled back to previous version
RolledBack,
}