use crate::ssh::ReceiveSyncService; use crate::ssh::SshTokenService; use crate::ssh::handle::SSHandle; use crate::ssh::rate_limit::SshRateLimiter; use db::cache::AppCache; use db::database::AppDatabase; use deadpool_redis::cluster::Pool as RedisPool; use russh::server::Handler; use slog::{Logger, info, warn}; use std::io; use std::net::SocketAddr; use std::sync::Arc; pub struct SSHServer { pub db: AppDatabase, pub cache: AppCache, pub redis_pool: RedisPool, pub logger: Logger, pub rate_limiter: Arc, pub token_service: SshTokenService, } impl SSHServer { pub fn new( db: AppDatabase, cache: AppCache, redis_pool: RedisPool, logger: Logger, token_service: SshTokenService, ) -> Self { SSHServer { db, cache, redis_pool, logger, rate_limiter: Arc::new(SshRateLimiter::new()), token_service, } } } impl russh::server::Server for SSHServer { type Handler = SSHandle; fn new_client(&mut self, addr: Option) -> Self::Handler { if let Some(addr) = addr { let ip = addr.ip().to_string(); info!( self.logger, "New SSH connection from {}:{}", ip, addr.port() ); let rate_limiter = self.rate_limiter.clone(); let logger = self.logger.clone(); tokio::spawn(async move { if !rate_limiter.is_ip_allowed(&ip).await { warn!(logger, "IP rate limit exceeded"; "ip" => %ip); } }); } else { info!(self.logger, "New SSH connection from unknown address"); } let sync_service = ReceiveSyncService::new(self.redis_pool.clone(), self.logger.clone()); SSHandle::new( self.db.clone(), self.cache.clone(), sync_service, self.logger.clone(), self.rate_limiter.clone(), self.token_service.clone(), addr, ) } fn handle_session_error(&mut self, error: ::Error) { match error { russh::Error::Disconnect => { info!(self.logger, "Connection disconnected by peer"); } russh::Error::Inconsistent => { warn!(self.logger, "Protocol inconsistency detected"); } russh::Error::NotAuthenticated => { warn!(self.logger, "Authentication failed"); } russh::Error::IO(ref io_err) => { let error_msg = format!( "IO error: kind={:?}, message={}, raw_os_error={:?}", io_err.kind(), io_err, io_err.raw_os_error() ); warn!(self.logger, "{}", error_msg); if io_err.kind() == io::ErrorKind::UnexpectedEof { warn!( self.logger, "Client disconnected during handshake or before authentication" ); } } _ => { let error_msg = format!("SSH session error: {}", error); warn!(self.logger, "{}", error_msg); } } } }