diff options
Diffstat (limited to 'teleterm/src/cmd/server.rs')
-rw-r--r-- | teleterm/src/cmd/server.rs | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/teleterm/src/cmd/server.rs b/teleterm/src/cmd/server.rs new file mode 100644 index 0000000..c4d2075 --- /dev/null +++ b/teleterm/src/cmd/server.rs @@ -0,0 +1,181 @@ +use crate::prelude::*; +use std::io::Read as _; + +#[derive(serde::Deserialize, Debug, Default)] +pub struct Config { + #[serde(default)] + server: crate::config::Server, + + #[serde( + rename = "oauth", + deserialize_with = "crate::config::oauth_configs", + default + )] + oauth_configs: std::collections::HashMap< + crate::protocol::AuthType, + crate::oauth::Config, + >, +} + +impl crate::config::Config for Config { + fn merge_args<'a>( + &mut self, + matches: &clap::ArgMatches<'a>, + ) -> Result<()> { + self.server.merge_args(matches) + } + + fn run( + &self, + ) -> Box<dyn futures::future::Future<Item = (), Error = Error> + Send> + { + if let Some(tls_identity_file) = &self.server.tls_identity_file { + create_server_tls( + self.server.listen_address, + self.server.read_timeout, + tls_identity_file, + self.server.allowed_login_methods.clone(), + self.oauth_configs.clone(), + self.server.uid, + self.server.gid, + ) + } else { + create_server( + self.server.listen_address, + self.server.read_timeout, + self.server.allowed_login_methods.clone(), + self.oauth_configs.clone(), + self.server.uid, + self.server.gid, + ) + } + } +} + +pub fn cmd<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> { + crate::config::Server::cmd(app.about("Run a teleterm server")) +} + +pub fn config( + config: Option<config::Config>, +) -> Result<Box<dyn crate::config::Config>> { + let config: Config = if let Some(config) = config { + config + .try_into() + .context(crate::error::CouldntParseConfig)? + } else { + Config::default() + }; + Ok(Box::new(config)) +} + +fn create_server( + address: std::net::SocketAddr, + read_timeout: std::time::Duration, + allowed_login_methods: std::collections::HashSet< + crate::protocol::AuthType, + >, + oauth_configs: std::collections::HashMap< + crate::protocol::AuthType, + crate::oauth::Config, + >, + uid: Option<users::uid_t>, + gid: Option<users::gid_t>, +) -> Box<dyn futures::future::Future<Item = (), Error = Error> + Send> { + let listener = match listen(address, uid, gid) { + Ok(listener) => listener, + Err(e) => return Box::new(futures::future::err(e)), + }; + + let acceptor = listener.incoming().context(crate::error::Acceptor); + let server = crate::server::Server::new( + Box::new(acceptor), + read_timeout, + allowed_login_methods, + oauth_configs, + ); + + Box::new(server) +} + +fn create_server_tls( + address: std::net::SocketAddr, + read_timeout: std::time::Duration, + tls_identity_file: &str, + allowed_login_methods: std::collections::HashSet< + crate::protocol::AuthType, + >, + oauth_configs: std::collections::HashMap< + crate::protocol::AuthType, + crate::oauth::Config, + >, + uid: Option<users::uid_t>, + gid: Option<users::gid_t>, +) -> Box<dyn futures::future::Future<Item = (), Error = Error> + Send> { + let tls_acceptor = match accept_tls(tls_identity_file) { + Ok(acceptor) => acceptor, + Err(e) => return Box::new(futures::future::err(e)), + }; + + let listener = match listen(address, uid, gid) { + Ok(listener) => listener, + Err(e) => return Box::new(futures::future::err(e)), + }; + + let acceptor = listener + .incoming() + .context(crate::error::Acceptor) + .map(move |sock| tls_acceptor.accept(sock)); + let server = crate::server::tls::Server::new( + Box::new(acceptor), + read_timeout, + allowed_login_methods, + oauth_configs, + ); + + Box::new(server) +} + +fn listen( + address: std::net::SocketAddr, + uid: Option<users::uid_t>, + gid: Option<users::gid_t>, +) -> Result<tokio::net::TcpListener> { + let listener = tokio::net::TcpListener::bind(&address) + .context(crate::error::Bind { address })?; + drop_privs(uid, gid)?; + log::info!("Listening on {}", address); + Ok(listener) +} + +fn accept_tls(tls_identity_file: &str) -> Result<tokio_tls::TlsAcceptor> { + let mut file = std::fs::File::open(tls_identity_file).context( + crate::error::OpenFileSync { + filename: tls_identity_file, + }, + )?; + let mut identity = vec![]; + file.read_to_end(&mut identity) + .context(crate::error::ReadFileSync)?; + let identity = native_tls::Identity::from_pkcs12(&identity, "") + .context(crate::error::ParseIdentity)?; + let acceptor = native_tls::TlsAcceptor::new(identity) + .context(crate::error::CreateAcceptor)?; + + Ok(tokio_tls::TlsAcceptor::from(acceptor)) +} + +fn drop_privs( + uid: Option<users::uid_t>, + gid: Option<users::gid_t>, +) -> Result<()> { + if let Some(gid) = gid { + users::switch::set_both_gid(gid, gid) + .context(crate::error::SwitchGid)?; + } + if let Some(uid) = uid { + users::switch::set_both_uid(uid, uid) + .context(crate::error::SwitchUid)?; + } + Ok(()) +} |