use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; use russh::server::{Auth, Handler, Msg, Session}; use russh::{Channel, ChannelId}; use tokio::sync::OwnedSemaphorePermit; use uuid::Uuid; use models::users::user; use crate::ssh::handler::git_op::GitOperation; use crate::ssh::service::GitSshService; pub mod git_op; mod auth; pub(crate) mod channel; mod command; #[derive(Default)] pub struct ChannelState { pub buffer: Vec, pub git_op: Option, pub repo_id: Option, pub space_id: Option, pub actor_id: Option, pub buffer_overflow: bool, pub nak_sent: bool, /// Set when a duplex git session (upload-pack / upload-archive) is running /// for this channel; client data is then relayed to its stdin instead of /// being buffered. pub dispatched: bool, pub stdin_tx: Option>>, /// Abort handle for the task that owns the streaming git child process. pub stream_abort: Option, /// Wall-clock budget for the duplex git process, captured at exec time. pub rpc_timeout_secs: u64, } pub struct GitSshHandler { pub service: Arc, pub client_addr: Option, pub session_permit: Option, pub operator: Option, pub channels: HashMap, pub over_capacity: bool, } impl GitSshHandler { pub fn new( service: Arc, client_addr: Option, session_permit: Option, ) -> Self { let over_capacity = session_permit.is_none(); Self { service, client_addr, session_permit, operator: None, channels: HashMap::new(), over_capacity, } } pub fn config(&self) -> &config::git::GitSshConfig { &self.service.config } } impl Handler for GitSshHandler { type Error = russh::Error; async fn auth_none(&mut self, user: &str) -> Result { tracing::debug!(user, client = ?self.client_addr, "ssh auth_none rejected"); Ok(Auth::reject()) } async fn auth_password(&mut self, user: &str, _password: &str) -> Result { tracing::debug!(user, client = ?self.client_addr, "ssh password auth rejected (publickey only)"); Ok(Auth::reject()) } async fn auth_publickey_offered( &mut self, user: &str, public_key: &russh::keys::PublicKey, ) -> Result { self.auth_publickey_offered_impl(user, public_key).await } async fn auth_publickey( &mut self, user: &str, public_key: &russh::keys::PublicKey, ) -> Result { self.auth_publickey_impl(user, public_key).await } async fn auth_openssh_certificate( &mut self, user: &str, _certificate: &russh::keys::Certificate, ) -> Result { tracing::debug!(user, "ssh certificate auth rejected"); Ok(Auth::reject()) } async fn authentication_banner(&mut self) -> Result, Self::Error> { Ok(self.config().banner.clone()) } async fn channel_open_session( &mut self, _channel: Channel, reply: russh::server::ChannelOpenHandle, _session: &mut Session, ) -> Result<(), Self::Error> { reply.accept().await; Ok(()) } async fn channel_close( &mut self, channel: ChannelId, _session: &mut Session, ) -> Result<(), Self::Error> { self.channel_close_impl(channel).await; Ok(()) } async fn channel_eof( &mut self, channel: ChannelId, session: &mut Session, ) -> Result<(), Self::Error> { self.channel_eof_impl(channel, session).await } async fn data( &mut self, channel: ChannelId, data: &[u8], session: &mut Session, ) -> Result<(), Self::Error> { self.data_impl(channel, data, session).await } async fn exec_request( &mut self, channel: ChannelId, data: &[u8], session: &mut Session, ) -> Result<(), Self::Error> { self.exec_request_impl(channel, data, session).await } async fn pty_request( &mut self, _channel: ChannelId, _term: &str, _col_width: u32, _row_height: u32, _pix_width: u32, _pix_height: u32, _modes: &[(russh::Pty, u32)], session: &mut Session, ) -> Result<(), Self::Error> { let _ = session.flush(); Ok(()) } async fn shell_request( &mut self, channel: ChannelId, session: &mut Session, ) -> Result<(), Self::Error> { self.shell_request_impl(channel, session).await } async fn subsystem_request( &mut self, _channel: ChannelId, _name: &str, session: &mut Session, ) -> Result<(), Self::Error> { let _ = session.flush(); Ok(()) } async fn env_request( &mut self, _channel: ChannelId, _variable_name: &str, _variable_value: &str, session: &mut Session, ) -> Result<(), Self::Error> { let _ = session.flush(); Ok(()) } async fn window_change_request( &mut self, _channel: ChannelId, _col_width: u32, _row_height: u32, _pix_width: u32, _pix_height: u32, session: &mut Session, ) -> Result<(), Self::Error> { let _ = session.flush(); Ok(()) } }