Complete bidirectional tunnel communication between server and agents, enabling persistent secure channels for future command execution and file operations. Agents transition from heartbeat mode to tunnel mode on-demand while maintaining WebSocket connection. Server Implementation: - Database layer (db/tunnel.rs): Session CRUD, ownership validation, cleanup on disconnect (prevents orphaned sessions) - API endpoints (api/tunnel.rs): POST /open, POST /close, GET /status with JWT auth, UUID validation, proper HTTP status codes - Protocol extension (ws/mod.rs): TunnelOpen/Close/Data messages, agent response handlers (TunnelReady/Data/Error) - Migration (006_tunnel_sessions.sql): tech_sessions table with partial unique constraint, foreign keys with CASCADE, audit table Agent Implementation: - State machine (tunnel/mod.rs): AgentMode (Heartbeat ↔ Tunnel), channel multiplexing, concurrent session prevention - WebSocket handlers (transport/websocket.rs): Open/close tunnel, mode switching without dropping connection, cleanup on disconnect - Protocol extension (transport/mod.rs): TunnelReady/Data/Error messages matching server definitions - Unit tests: Lifecycle and channel management coverage Key Features: - Security: JWT auth, session ownership verification, SQL injection prevention, constraint-based duplicate session blocking - Cleanup: Automatic session closure on agent disconnect (both sides), channel cleanup, graceful state transitions - Error handling: Proper HTTP status codes (400/403/404/409/500), comprehensive Result types, detailed logging - Extensibility: Channel types ready (Terminal/File/Registry/Service), TunnelDataPayload enum for Phase 2+ expansion Phase 1 Scope (Implemented): - Tunnel session lifecycle management - Mode switching (heartbeat ↔ tunnel) - Protocol message routing - Database session tracking Phase 2 Next Steps: - Terminal command execution (tokio::process::Command) - Client WebSocket connections for output streaming - Command audit logging - File transfer operations Verification: - Server compiles successfully (0 errors) - Agent unit tests pass (tunnel lifecycle, channel management) - Code review approved (protocol alignment verified) - Database constraints enforce referential integrity - Cleanup tested (session closure on disconnect) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
320 lines
8.8 KiB
Markdown
320 lines
8.8 KiB
Markdown
# GuruRMM Tunnel - Phase 1 Agent Implementation
|
|
|
|
**Date:** 2026-04-14
|
|
**Status:** COMPLETED
|
|
**Component:** Agent (Rust)
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
Successfully implemented Phase 1 of the GuruRMM real-time tunnel feature on the agent side. The agent now supports mode switching between Heartbeat and Tunnel modes, handles tunnel lifecycle messages, and is ready for Phase 2 terminal command execution.
|
|
|
|
---
|
|
|
|
## Implementation Details
|
|
|
|
### 1. Protocol Extensions
|
|
|
|
**File:** `agent/src/transport/mod.rs`
|
|
|
|
Added new message types to `AgentMessage` enum:
|
|
- `TunnelReady { session_id: String }` - Confirmation that tunnel is ready
|
|
- `TunnelData { channel_id: String, data: TunnelDataPayload }` - Bidirectional tunnel data
|
|
- `TunnelError { channel_id: String, error: String }` - Error reporting
|
|
|
|
Added new message types to `ServerMessage` enum:
|
|
- `TunnelOpen { session_id: String, tech_id: Uuid }` - Server request to open tunnel
|
|
- `TunnelClose { session_id: String }` - Server request to close tunnel
|
|
- `TunnelData { channel_id: String, data: TunnelDataPayload }` - Bidirectional tunnel data
|
|
|
|
Added `TunnelDataPayload` enum (Phase 1: Terminal only):
|
|
- `Terminal { command: String }` - Terminal command request
|
|
- `TerminalOutput { stdout: String, stderr: String, exit_code: Option<i32> }` - Terminal response
|
|
|
|
### 2. Tunnel Manager Module
|
|
|
|
**File:** `agent/src/tunnel/mod.rs` (NEW)
|
|
|
|
Created comprehensive tunnel state management:
|
|
|
|
**AgentMode enum:**
|
|
```rust
|
|
pub enum AgentMode {
|
|
Heartbeat, // Default: 30s heartbeats, metrics, network monitoring
|
|
Tunnel {
|
|
session_id: String,
|
|
tech_id: Uuid,
|
|
channels: HashMap<String, ChannelType>,
|
|
},
|
|
}
|
|
```
|
|
|
|
**TunnelManager struct:**
|
|
- `open_tunnel()` - Transition from Heartbeat to Tunnel mode
|
|
- `close_tunnel()` - Transition back to Heartbeat mode
|
|
- `add_channel()` - Register new channel (terminal, file, etc.)
|
|
- `remove_channel()` - Cleanup channel
|
|
- `force_close()` - Emergency cleanup on disconnect
|
|
|
|
**Channel types (extensible for future phases):**
|
|
- `Terminal` - Command execution (Phase 1)
|
|
- `File` - File operations (Phase 2+)
|
|
- `Registry` - Registry operations (Phase 2+)
|
|
- `Service` - Service management (Phase 2+)
|
|
|
|
### 3. WebSocket Integration
|
|
|
|
**File:** `agent/src/transport/websocket.rs`
|
|
|
|
Updated WebSocket client to support tunnel operations:
|
|
|
|
**New handler functions:**
|
|
- `handle_tunnel_open()` - Process TunnelOpen request, send TunnelReady
|
|
- `handle_tunnel_close()` - Process TunnelClose request, cleanup state
|
|
- `handle_tunnel_data()` - Route tunnel data by channel (Phase 1: placeholder)
|
|
|
|
**Message loop updates:**
|
|
- Created `TunnelManager` instance in connection lifecycle
|
|
- Updated `handle_server_message()` signature to accept tunnel manager
|
|
- Added tunnel message logging (TunnelReady, TunnelData, TunnelError)
|
|
- Force-close tunnel on WebSocket disconnect
|
|
|
|
**Mode persistence:**
|
|
- Tunnel state maintained across message loop iterations
|
|
- Heartbeat continues in both modes (connection keepalive)
|
|
- Clean shutdown closes active sessions
|
|
|
|
### 4. Module Registration
|
|
|
|
**File:** `agent/src/main.rs`
|
|
|
|
Added tunnel module to module tree:
|
|
```rust
|
|
mod tunnel;
|
|
```
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### Unit Tests
|
|
|
|
Created comprehensive test suite in `agent/src/tunnel/mod.rs`:
|
|
|
|
**Test: `test_tunnel_lifecycle`**
|
|
- Starts in Heartbeat mode
|
|
- Opens tunnel successfully
|
|
- Rejects concurrent tunnel sessions
|
|
- Closes tunnel and returns to Heartbeat mode
|
|
|
|
**Test: `test_channel_management`**
|
|
- Rejects channel operations without active tunnel
|
|
- Adds multiple channels
|
|
- Retrieves channel types
|
|
- Removes channels
|
|
- Force-closes tunnel
|
|
|
|
**Test Results:**
|
|
```
|
|
running 2 tests
|
|
test tunnel::tests::test_tunnel_lifecycle ... ok
|
|
test tunnel::tests::test_channel_management ... ok
|
|
|
|
test result: ok. 2 passed; 0 failed; 0 ignored
|
|
```
|
|
|
|
### Compilation
|
|
|
|
**Status:** SUCCESSFUL
|
|
|
|
**Warnings (expected, non-critical):**
|
|
- `tech_id` field unused (will be used in Phase 2 for authorization)
|
|
- Some enum variants unused (File, Registry, Service - Phase 2+)
|
|
- Some methods unused (mode accessors - used in Phase 2)
|
|
|
|
---
|
|
|
|
## Protocol Flow
|
|
|
|
### Tunnel Open
|
|
```
|
|
Server → Agent: TunnelOpen { session_id, tech_id }
|
|
Agent: tunnel_manager.open_tunnel()
|
|
Agent → Server: TunnelReady { session_id }
|
|
```
|
|
|
|
### Tunnel Close
|
|
```
|
|
Server → Agent: TunnelClose { session_id }
|
|
Agent: tunnel_manager.close_tunnel()
|
|
```
|
|
|
|
### Terminal Command (Phase 1 - Placeholder)
|
|
```
|
|
Server → Agent: TunnelData {
|
|
channel_id: "...",
|
|
data: Terminal { command: "..." }
|
|
}
|
|
Agent: Log command (execution in Phase 2)
|
|
Agent → Server: TunnelData {
|
|
channel_id: "...",
|
|
data: TerminalOutput {
|
|
stdout: "",
|
|
stderr: "Not implemented",
|
|
exit_code: Some(-1)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Connection Loss
|
|
```
|
|
WebSocket disconnect detected
|
|
Agent: tunnel_manager.force_close()
|
|
Agent: Cleanup tasks, return to heartbeat mode
|
|
```
|
|
|
|
---
|
|
|
|
## Key Features
|
|
|
|
### Mode Switching
|
|
- Clean transition between Heartbeat and Tunnel modes
|
|
- Single active tunnel per agent (prevents session conflicts)
|
|
- Tunnel state persists across message loop iterations
|
|
|
|
### Channel Multiplexing
|
|
- HashMap-based channel routing by `channel_id`
|
|
- Extensible channel types (Terminal, File, Registry, Service)
|
|
- Channel lifecycle management (add, remove, cleanup)
|
|
|
|
### Error Handling
|
|
- Validates session IDs on close requests
|
|
- Rejects concurrent tunnel sessions
|
|
- Sends TunnelError messages for failures
|
|
- Force-close on unexpected disconnect
|
|
|
|
### Logging
|
|
- Info-level: Tunnel open/close, mode transitions
|
|
- Debug-level: Channel operations, TunnelData routing
|
|
- Warn-level: Errors, rejected operations
|
|
|
|
---
|
|
|
|
## Files Modified
|
|
|
|
1. `agent/src/transport/mod.rs` - Protocol message definitions
|
|
2. `agent/src/transport/websocket.rs` - WebSocket tunnel integration
|
|
3. `agent/src/main.rs` - Module registration
|
|
4. `agent/src/tunnel/mod.rs` - NEW: Tunnel manager implementation
|
|
|
|
---
|
|
|
|
## Next Steps (Phase 2)
|
|
|
|
### Terminal Command Execution
|
|
|
|
**Implementation required in `handle_tunnel_data()`:**
|
|
|
|
1. Parse `TunnelDataPayload::Terminal { command }`
|
|
2. Spawn process using `tokio::process::Command`
|
|
3. Capture stdout/stderr streams
|
|
4. Handle exit codes and timeouts
|
|
5. Send `TunnelDataPayload::TerminalOutput` response
|
|
|
|
**Considerations:**
|
|
- Shell selection (PowerShell, cmd, bash based on OS)
|
|
- Working directory restrictions (security)
|
|
- Timeout enforcement (prevent hung processes)
|
|
- Error handling (process spawn failures, permission errors)
|
|
|
|
### Integration Testing
|
|
|
|
**Manual testing with server:**
|
|
1. Deploy updated agent to test machine
|
|
2. Server sends TunnelOpen via WebSocket
|
|
3. Verify TunnelReady response
|
|
4. Send Terminal command
|
|
5. Verify TerminalOutput response (Phase 2)
|
|
6. Server sends TunnelClose
|
|
7. Verify graceful cleanup
|
|
|
|
---
|
|
|
|
## Compliance with Architecture Plan
|
|
|
|
**Alignment with `plans/real-time-tunnel-architecture.md`:**
|
|
|
|
- [OK] AgentMode state machine (Heartbeat ↔ Tunnel)
|
|
- [OK] Channel routing by channel_id
|
|
- [OK] TunnelOpen/TunnelClose lifecycle
|
|
- [OK] TunnelReady confirmation message
|
|
- [OK] TunnelDataPayload enum (Phase 1: Terminal only)
|
|
- [OK] Heartbeat maintained in tunnel mode
|
|
- [OK] Force-close on disconnect
|
|
- [OK] Unit tests for state machine
|
|
- [PENDING] Terminal command execution (Phase 2)
|
|
- [PENDING] File operations (Phase 3)
|
|
- [PENDING] MCP server integration (Phase 4)
|
|
|
|
---
|
|
|
|
## Known Limitations
|
|
|
|
1. **Terminal execution not implemented** - Phase 1 only handles protocol and state management. Actual command execution is Phase 2.
|
|
|
|
2. **No working directory restrictions** - Security layer for path validation will be added in Phase 2.
|
|
|
|
3. **Single tunnel per agent** - By design, prevents session conflicts. Multi-tech sessions deferred to future enhancement.
|
|
|
|
4. **No channel-level timeouts** - Will be added in Phase 2 with actual command execution.
|
|
|
|
---
|
|
|
|
## Security Notes
|
|
|
|
**Implemented:**
|
|
- Session validation (session_id matching on close)
|
|
- Single tunnel enforcement (rejects concurrent sessions)
|
|
- Clean state transitions (no lingering channels)
|
|
|
|
**Pending (Phase 2+):**
|
|
- Command sanitization (injection prevention)
|
|
- Working directory allowlist
|
|
- Rate limiting (server-side)
|
|
- Audit logging (server-side)
|
|
|
|
---
|
|
|
|
## Performance Impact
|
|
|
|
**Memory:**
|
|
- `TunnelManager`: ~200 bytes (enum + HashMap overhead)
|
|
- Active per connection, deallocated on disconnect
|
|
- Negligible impact on heartbeat mode
|
|
|
|
**CPU:**
|
|
- Mode checks: O(1) enum match
|
|
- Channel routing: O(1) HashMap lookup
|
|
- No continuous tasks in tunnel mode
|
|
- Heartbeat continues at 30s interval (unchanged)
|
|
|
|
**Network:**
|
|
- TunnelReady: Single message on tunnel open (~100 bytes)
|
|
- Heartbeat continues in tunnel mode (no change)
|
|
- TunnelData: Variable (depends on command output in Phase 2)
|
|
|
|
---
|
|
|
|
## Conclusion
|
|
|
|
Phase 1 agent implementation is **complete and tested**. The agent can now:
|
|
- Switch between Heartbeat and Tunnel modes
|
|
- Handle TunnelOpen/TunnelClose lifecycle
|
|
- Route tunnel messages by channel_id
|
|
- Maintain connection integrity in both modes
|
|
|
|
Ready for Phase 2: Terminal command execution implementation.
|
|
|
|
**Status:** READY FOR SERVER INTEGRATION TESTING
|