diff --git a/warpgate-common/src/protocols/mod.rs b/warpgate-common/src/protocols/mod.rs index 8c91b48..675eeee 100644 --- a/warpgate-common/src/protocols/mod.rs +++ b/warpgate-common/src/protocols/mod.rs @@ -24,5 +24,5 @@ pub enum TargetTestError { #[async_trait] pub trait ProtocolServer { async fn run(self, address: SocketAddr) -> Result<()>; - async fn test_target(self, target: Target) -> Result<(), TargetTestError>; + async fn test_target(&self, target: Target) -> Result<(), TargetTestError>; } diff --git a/warpgate-protocol-http/src/lib.rs b/warpgate-protocol-http/src/lib.rs index b07735c..27d9333 100644 --- a/warpgate-protocol-http/src/lib.rs +++ b/warpgate-protocol-http/src/lib.rs @@ -29,7 +29,7 @@ use tokio::sync::Mutex; use tracing::*; use warpgate_admin::admin_api_app; use warpgate_common::{ - ProtocolServer, Services, Target, TargetTestError, TlsCertificateAndPrivateKey, + ProtocolServer, Services, Target, TargetOptions, TargetTestError, TlsCertificateAndPrivateKey, TlsCertificateBundle, TlsPrivateKey, }; use warpgate_web::Assets; @@ -166,7 +166,16 @@ impl ProtocolServer for HTTPProtocolServer { Ok(()) } - async fn test_target(self, _target: Target) -> Result<(), TargetTestError> { + async fn test_target(&self, target: Target) -> Result<(), TargetTestError> { + let TargetOptions::Http(options) = target.options else { + return Err(TargetTestError::Misconfigured("Not an HTTP target".to_owned())); + }; + let request = poem::Request::builder().uri_str("http://host/").finish(); + crate::proxy::proxy_normal_request(&request, poem::Body::empty(), &options) + .await + .map_err(|e| { + return TargetTestError::ConnectionError(format!("{e}")); + })?; Ok(()) } } diff --git a/warpgate-protocol-mysql/src/client.rs b/warpgate-protocol-mysql/src/client.rs index ba326c8..5fafe98 100644 --- a/warpgate-protocol-mysql/src/client.rs +++ b/warpgate-protocol-mysql/src/client.rs @@ -29,6 +29,30 @@ pub struct ConnectionOptions { pub capabilities: Capabilities, } +impl Default for ConnectionOptions { + fn default() -> Self { + ConnectionOptions { + collation: 33, + database: None, + max_packet_size: 0xffff_ffff, + capabilities: Capabilities::PROTOCOL_41 + | Capabilities::PLUGIN_AUTH + | Capabilities::FOUND_ROWS + | Capabilities::LONG_FLAG + | Capabilities::NO_SCHEMA + | Capabilities::PLUGIN_AUTH_LENENC_DATA + | Capabilities::CONNECT_WITH_DB + | Capabilities::SESSION_TRACK + | Capabilities::IGNORE_SPACE + | Capabilities::INTERACTIVE + | Capabilities::TRANSACTIONS + | Capabilities::DEPRECATE_EOF + | Capabilities::SECURE_CONNECTION + | Capabilities::SSL, + } + } +} + impl MySqlClient { pub async fn connect( target: &TargetMySqlOptions, diff --git a/warpgate-protocol-mysql/src/lib.rs b/warpgate-protocol-mysql/src/lib.rs index f484f7a..e091f1e 100644 --- a/warpgate-protocol-mysql/src/lib.rs +++ b/warpgate-protocol-mysql/src/lib.rs @@ -12,12 +12,13 @@ use std::sync::Arc; use anyhow::{Context, Result}; use async_trait::async_trait; +use client::{ConnectionOptions, MySqlClient}; use rustls::server::NoClientAuth; use rustls::ServerConfig; use tokio::net::TcpListener; use tracing::*; use warpgate_common::{ - ProtocolServer, Services, SessionStateInit, Target, TargetTestError, + ProtocolServer, Services, SessionStateInit, Target, TargetOptions, TargetTestError, TlsCertificateAndPrivateKey, TlsCertificateBundle, TlsPrivateKey, }; @@ -108,7 +109,13 @@ impl ProtocolServer for MySQLProtocolServer { } } - async fn test_target(self, _target: Target) -> Result<(), TargetTestError> { + async fn test_target(&self, target: Target) -> Result<(), TargetTestError> { + let TargetOptions::MySql(options) = target.options else { + return Err(TargetTestError::Misconfigured("Not a MySQL target".to_owned())); + }; + MySqlClient::connect(&options, ConnectionOptions::default()) + .await + .map_err(|e| TargetTestError::ConnectionError(format!("{e}")))?; Ok(()) } } diff --git a/warpgate-protocol-ssh/src/lib.rs b/warpgate-protocol-ssh/src/lib.rs index eaf563e..df13480 100644 --- a/warpgate-protocol-ssh/src/lib.rs +++ b/warpgate-protocol-ssh/src/lib.rs @@ -47,7 +47,7 @@ impl ProtocolServer for SSHProtocolServer { run_server(self.services, address).await } - async fn test_target(self, target: Target) -> Result<(), TargetTestError> { + async fn test_target(&self, target: Target) -> Result<(), TargetTestError> { let TargetOptions::Ssh(ssh_options) = target.options else { return Err(TargetTestError::Misconfigured("Not an SSH target".to_owned())); }; diff --git a/warpgate/src/commands/test_target.rs b/warpgate/src/commands/test_target.rs index e92c8e4..02017e1 100644 --- a/warpgate/src/commands/test_target.rs +++ b/warpgate/src/commands/test_target.rs @@ -1,6 +1,6 @@ use anyhow::Result; use tracing::*; -use warpgate_common::{ProtocolServer, Services, Target, TargetTestError}; +use warpgate_common::{ProtocolServer, Services, Target, TargetOptions, TargetTestError}; use crate::config::load_config; @@ -19,7 +19,22 @@ pub(crate) async fn command(cli: &crate::Cli, target_name: &String) -> Result<() let services = Services::new(config.clone()).await?; - let s = warpgate_protocol_ssh::SSHProtocolServer::new(&services).await?; + let s: Box = match target.options { + TargetOptions::Ssh(_) => { + Box::new(warpgate_protocol_ssh::SSHProtocolServer::new(&services).await?) + } + TargetOptions::Http(_) => { + Box::new(warpgate_protocol_http::HTTPProtocolServer::new(&services).await?) + } + TargetOptions::MySql(_) => { + Box::new(warpgate_protocol_mysql::MySQLProtocolServer::new(&services).await?) + } + TargetOptions::WebAdmin(_) => { + error!("Unsupported target type"); + return Ok(()); + } + }; + match s.test_target(target).await { Err(TargetTestError::AuthenticationError) => { error!("Authentication failed");