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>
72 lines
2.8 KiB
Rust
72 lines
2.8 KiB
Rust
//! REST API routes
|
|
//!
|
|
//! Provides endpoints for:
|
|
//! - Agent management (registration, listing, deletion)
|
|
//! - Client and site management
|
|
//! - Metrics retrieval
|
|
//! - Command execution
|
|
//! - User authentication
|
|
|
|
pub mod agents;
|
|
pub mod auth;
|
|
pub mod clients;
|
|
pub mod commands;
|
|
pub mod metrics;
|
|
pub mod sites;
|
|
pub mod tunnel;
|
|
|
|
use axum::{
|
|
routing::{delete, get, post, put},
|
|
Router,
|
|
};
|
|
|
|
use crate::AppState;
|
|
|
|
/// Build all API routes
|
|
pub fn routes() -> Router<AppState> {
|
|
Router::new()
|
|
// Authentication
|
|
.route("/auth/login", post(auth::login))
|
|
.route("/auth/register", post(auth::register))
|
|
.route("/auth/me", get(auth::me))
|
|
// Clients
|
|
.route("/clients", get(clients::list_clients))
|
|
.route("/clients", post(clients::create_client))
|
|
.route("/clients/:id", get(clients::get_client))
|
|
.route("/clients/:id", put(clients::update_client))
|
|
.route("/clients/:id", delete(clients::delete_client))
|
|
.route("/clients/:id/sites", get(sites::list_sites_by_client))
|
|
// Sites
|
|
.route("/sites", get(sites::list_sites))
|
|
.route("/sites", post(sites::create_site))
|
|
.route("/sites/:id", get(sites::get_site))
|
|
.route("/sites/:id", put(sites::update_site))
|
|
.route("/sites/:id", delete(sites::delete_site))
|
|
.route("/sites/:id/regenerate-key", post(sites::regenerate_api_key))
|
|
// Agents
|
|
.route("/agents", get(agents::list_agents_with_details))
|
|
.route("/agents", post(agents::register_agent))
|
|
.route("/agents/stats", get(agents::get_stats))
|
|
.route("/agents/unassigned", get(agents::list_unassigned_agents))
|
|
.route("/agents/:id", get(agents::get_agent))
|
|
.route("/agents/:id", delete(agents::delete_agent))
|
|
.route("/agents/:id/move", post(agents::move_agent))
|
|
.route("/agents/:id/state", get(agents::get_agent_state))
|
|
// Metrics
|
|
.route("/agents/:id/metrics", get(metrics::get_agent_metrics))
|
|
.route("/metrics/summary", get(metrics::get_summary))
|
|
// Commands
|
|
.route("/agents/:id/command", post(commands::send_command))
|
|
.route("/commands", get(commands::list_commands).delete(commands::clear_command_history))
|
|
.route("/commands/:id", get(commands::get_command).delete(commands::delete_command))
|
|
.route("/commands/:id/cancel", post(commands::cancel_command))
|
|
// Legacy Agent (PowerShell for 2008 R2)
|
|
.route("/agent/register-legacy", post(agents::register_legacy))
|
|
.route("/agent/heartbeat", post(agents::heartbeat))
|
|
.route("/agent/command-result", post(agents::command_result))
|
|
// Tunnel management
|
|
.route("/v1/tunnel/open", post(tunnel::open_tunnel))
|
|
.route("/v1/tunnel/close", post(tunnel::close_tunnel))
|
|
.route("/v1/tunnel/status/:session_id", get(tunnel::get_tunnel_status))
|
|
}
|