diff --git a/Cargo.toml b/Cargo.toml index 478c88c06..a6e78b09d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,24 +41,24 @@ serde_json = "1.0.132" tracing = "0.1.40" tracing-subscriber = "0.3.19" tracing-appender = "0.2" -thiserror = "2.0.4" +thiserror = "2.0.6" rand = "0.8.5" smallvec = "1.13.2" tokio = "1.42" -tokio-stream = "0.1.16" +tokio-stream = "0.1.17" tokio-test = "0.4.4" -clap = "4.5.21" +clap = "4.5.23" async-trait = "0.1.83" async-stream = "0.3.6" bytes = "1.8.0" memchr = "2.7.4" -chrono = "0.4.38" +chrono = "0.4.39" sha1 = "0.10.6" futures = "0.3.30" futures-util = "0.3.30" go-defer = "0.1.0" -russh = "0.46.0" -russh-keys = "0.46.0" +russh = "0.48.2" +russh-keys = "0.48.1" axum = "0.7.7" axum-extra = "0.9.4" axum-server = "0.7.1" diff --git a/docker/mono-engine-dockerfile b/docker/mono-engine-dockerfile index d9438e90b..a3cad9a70 100644 --- a/docker/mono-engine-dockerfile +++ b/docker/mono-engine-dockerfile @@ -46,7 +46,7 @@ fi # final image FROM debian:bookworm-slim -RUN apt-get update && apt-get install -y libssl-dev ca-certificates +RUN apt-get update && apt-get install -y libssl-dev ca-certificates less ARG BUILD_TYPE=release diff --git a/mono/src/api/user/user_router.rs b/mono/src/api/user/user_router.rs index 9e0418115..4a32bb634 100644 --- a/mono/src/api/user/user_router.rs +++ b/mono/src/api/user/user_router.rs @@ -5,7 +5,7 @@ use axum::{ routing::{get, post}, Json, Router, }; -use russh_keys::parse_public_key_base64; +use russh_keys::{parse_public_key_base64, HashAlg}; use common::model::CommonResult; @@ -54,7 +54,7 @@ async fn add_key( let res = state .user_stg() - .save_ssh_key(user.user_id, &title, &json.ssh_key, &key.fingerprint()) + .save_ssh_key(user.user_id, &title, &json.ssh_key, &key.fingerprint(HashAlg::Sha256).to_string()) .await; let res = match res { Ok(_) => CommonResult::success(None), diff --git a/mono/src/git_protocol/ssh.rs b/mono/src/git_protocol/ssh.rs index d4be7560b..68c786fcb 100644 --- a/mono/src/git_protocol/ssh.rs +++ b/mono/src/git_protocol/ssh.rs @@ -9,7 +9,7 @@ use chrono::{DateTime, Duration, Utc}; use futures::{stream, StreamExt}; use russh::server::{self, Auth, Msg, Session}; use russh::{Channel, ChannelId, MethodSet}; -use russh_keys::key; +use russh_keys::{self, HashAlg, PublicKey}; use tokio::io::AsyncReadExt; use ceres::lfs::lfs_structs::Link; @@ -25,7 +25,6 @@ type ClientMap = HashMap<(usize, ChannelId), Channel>; #[allow(dead_code)] #[derive(Clone)] pub struct SshServer { - pub client_pubkey: Arc, pub clients: Arc>, pub id: usize, pub context: Context, @@ -96,13 +95,13 @@ impl server::Handler for SshServer { // TODO handler ProtocolError let res = smart_protocol.git_info_refs().await.unwrap(); self.smart_protocol = Some(smart_protocol); - session.data(channel, res.to_vec().into()); - session.channel_success(channel); + session.data(channel, res.to_vec().into())?; + session.channel_success(channel)?; } //Note that currently mega does not support pure ssh to transfer files, still relay on the https server. //see https://github.com/git-lfs/git-lfs/blob/main/docs/proposals/ssh_adapter.md for more details about pure ssh file transfer. "git-lfs-transfer" => { - session.data(channel, "not implemented yet".as_bytes().to_vec().into()); + session.data(channel, "not implemented yet".as_bytes().to_vec().into())?; } // When connecting over SSH, the first attempt will be made to use // `git-lfs-transfer`, the pure SSH protocol, and if it fails, Git LFS will fall @@ -119,7 +118,7 @@ impl server::Handler for SshServer { expire_time.to_rfc3339() }, }; - session.data(channel, serde_json::to_vec(&link).unwrap().into()); + session.data(channel, serde_json::to_vec(&link).unwrap().into())?; } command => tracing::error!("Not Supported command! {}", command), } @@ -129,15 +128,15 @@ impl server::Handler for SshServer { async fn auth_publickey( &mut self, user: &str, - public_key: &key::PublicKey, + public_key: &PublicKey, ) -> Result { + let fingerprint = public_key.fingerprint(HashAlg::Sha256).to_string(); + tracing::info!( - "auth_publickey: {} / {:?}/ {}", + "auth_publickey: {} / {}", user, - public_key.name(), - public_key.fingerprint() + fingerprint ); - let fingerprint = public_key.fingerprint(); let res = self.context.user_stg().search_ssh_key_finger(&fingerprint).await.unwrap(); if !res.is_empty() { tracing::info!("Client public key verified successfully!"); @@ -171,7 +170,7 @@ impl server::Handler for SshServer { self.data_combined.extend_from_slice(data); } }; - session.channel_success(channel); + session.channel_success(channel)?; Ok(()) } @@ -190,8 +189,8 @@ impl server::Handler for SshServer { let mut clients = self.clients.lock().await; clients.remove(&(self.id, channel)); } - session.exit_status_request(channel, 0000); - session.close(channel); + session.exit_status_request(channel, 0000)?; + session.close(channel)?; Ok(()) } } @@ -206,7 +205,7 @@ impl SshServer { .unwrap(); tracing::info!("buf is {:?}", buf); - session.data(channel, String::from_utf8(buf.to_vec()).unwrap().into()); + session.data(channel, String::from_utf8(buf.to_vec()).unwrap().into()).unwrap(); while let Some(chunk) = send_pack_data.next().await { let mut reader = chunk.as_slice(); @@ -218,10 +217,10 @@ impl SshServer { break; } let bytes_out = smart_protocol.build_side_band_format(temp, length); - session.data(channel, bytes_out.to_vec().into()); + session.data(channel, bytes_out.to_vec().into()).unwrap(); } } - session.data(channel, smart::PKT_LINE_END_MARKER.to_vec().into()); + session.data(channel, smart::PKT_LINE_END_MARKER.to_vec().into()).unwrap(); } async fn handle_receive_pack(&mut self, channel: ChannelId, session: &mut Session) { @@ -247,6 +246,6 @@ impl SshServer { } tracing::info!("report status: {:?}", report_status); - session.data(channel, report_status.to_vec().into()); + session.data(channel, report_status.to_vec().into()).unwrap(); } } diff --git a/mono/src/server/ssh_server.rs b/mono/src/server/ssh_server.rs index 04c9f77e2..7fd68313c 100644 --- a/mono/src/server/ssh_server.rs +++ b/mono/src/server/ssh_server.rs @@ -7,10 +7,8 @@ use bytes::BytesMut; use clap::Args; use ed25519_dalek::pkcs8::spki::der::pem::LineEnding; -use ed25519_dalek::pkcs8::{DecodePrivateKey, EncodePrivateKey}; -use ed25519_dalek::SigningKey; -use russh::server::Server; -use russh_keys::key::KeyPair; +use russh::{server::Server, Preferred}; +use russh_keys::{ssh_key::rand_core::OsRng, PrivateKey}; use common::model::CommonOptions; use jupiter::context::Context; @@ -37,19 +35,17 @@ pub struct SshCustom { /// start a ssh server pub async fn start_server(context: Context, command: &SshOptions) { // we need to persist the key to prevent key expired after server restart. - let client_key = load_key(); - let client_pubkey = Arc::new(client_key.clone_public_key().unwrap()); - - let mut ru_config = russh::server::Config { + let p_key = load_key(); + let ru_config = russh::server::Config { auth_rejection_time: std::time::Duration::from_secs(3), + keys: vec![p_key], + preferred: Preferred { + // key: Cow::Borrowed(&[CERT_ECDSA_SHA2_P256]), + ..Preferred::default() + }, auth_rejection_time_initial: Some(std::time::Duration::from_secs(0)), - // preferred: Preferred { - // key: &[russh_keys::key::SSH_RSA], - // ..Default::default() - // }, ..Default::default() }; - ru_config.keys.push(client_key); let ru_config = Arc::new(ru_config); @@ -58,7 +54,6 @@ pub async fn start_server(context: Context, command: &SshOptions) { custom: SshCustom { ssh_port }, } = command; let mut ssh_server = SshServer { - client_pubkey, clients: Arc::new(Mutex::new(HashMap::new())), id: 0, context, @@ -70,28 +65,26 @@ pub async fn start_server(context: Context, command: &SshOptions) { ssh_server.run_on_address(ru_config, addr).await.unwrap(); } -pub fn load_key() -> KeyPair { +pub fn load_key() -> PrivateKey { let ssh_key = read_secret("ssh_server_key").unwrap(); if let Some(ssh_key) = ssh_key { - // load the keypair from the vault let data = ssh_key.data.unwrap(); - let secret_key = data["secret_key"].as_str().unwrap().to_string(); - let keypair = SigningKey::from_pkcs8_pem(&secret_key).expect("parsing key err"); - KeyPair::Ed25519(keypair) + let secret_key = data["secret_key"].as_str().unwrap(); + PrivateKey::from_openssh(secret_key).unwrap() } else { // generate a keypair if not exists - let keys = KeyPair::generate_ed25519(); - if let KeyPair::Ed25519(inner_pair) = &keys { - let secret = serde_json::json!({ - "secret_key": *inner_pair.to_pkcs8_pem(LineEnding::CR).unwrap() - }) - .as_object() - .unwrap() - .clone(); - write_secret("ssh_server_key", Some(secret)).unwrap_or_else(|e| { - panic!("Failed to write ssh_server_key: {:?}", e); - }); - } + let keys = + russh_keys::PrivateKey::random(&mut OsRng, russh_keys::Algorithm::Ed25519).unwrap(); + let secret = serde_json::json!({ + "secret_key": + *keys.to_openssh(LineEnding::CR).unwrap() + }) + .as_object() + .unwrap() + .clone(); + write_secret("ssh_server_key", Some(secret)).unwrap_or_else(|e| { + panic!("Failed to write ssh_server_key: {:?}", e); + }); keys } }