-
I want to build SSH server for cloning git repository over SSH. command:
My golang code that works. package main
import (
"fmt"
"io"
"log"
"os/exec"
"github.com/gliderlabs/ssh"
)
func main() {
ssh.Handle(func(s ssh.Session) {
cmd := s.Command()
if len(cmd) > 0 && cmd[0] == "git-upload-pack" {
handleGitUploadPack(s, cmd[1])
} else {
io.WriteString(s, "Unknown command\n")
}
})
log.Println("Starting SSH server on :22...")
log.Fatal(ssh.ListenAndServe(":22", nil))
}
func handleGitUploadPack(s ssh.Session, repoPath string) {
cmd := exec.Command("git-upload-pack", repoPath)
cmd.Stdout = s
cmd.Stderr = s
cmd.Stdin = s
if err := cmd.Run(); err != nil {
io.WriteString(s, fmt.Sprintf("Failed to run git-upload-pack: %v\n", err))
}
} I want to rewrite the code above in rust using russh library. use std::collections::HashMap;
use std::sync::Arc;
use anyhow::Result;
use async_trait::async_trait;
use russh::server::{Msg, Server as _, Session};
use russh::*;
use russh_keys::*;
use tokio::sync::Mutex;
#[tokio::main]
async fn main() -> Result<()> {
env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.init();
let config = russh::server::Config {
inactivity_timeout: Some(std::time::Duration::from_secs(3600)),
auth_rejection_time: std::time::Duration::from_secs(3),
auth_rejection_time_initial: Some(std::time::Duration::from_secs(0)),
keys: vec![russh_keys::key::KeyPair::generate_ed25519().unwrap()],
..Default::default()
};
let config = Arc::new(config);
let mut sh = Server {
clients: Arc::new(Mutex::new(HashMap::new())),
id: 0,
};
sh.run_on_address(config, ("0.0.0.0", 22)).await.unwrap();
Ok(())
}
#[derive(Clone)]
struct Server {
clients: Arc<Mutex<HashMap<(usize, ChannelId), russh::server::Handle>>>,
id: usize,
}
impl server::Server for Server {
type Handler = Self;
fn new_client(&mut self, _: Option<std::net::SocketAddr>) -> Self {
let s = self.clone();
self.id += 1;
s
}
}
#[async_trait]
impl server::Handler for Server {
type Error = anyhow::Error;
async fn channel_open_session(
&mut self,
channel: Channel<Msg>,
session: &mut Session,
) -> Result<bool, Self::Error> {
{
let mut clients = self.clients.lock().await;
clients.insert((self.id, channel.id()), session.handle());
}
Ok(true)
}
async fn auth_publickey(
&mut self,
_: &str,
_: &key::PublicKey,
) -> Result<server::Auth, Self::Error> {
Ok(server::Auth::Accept)
}
async fn exec_request(
&mut self,
channel: ChannelId,
data: &[u8],
session: &mut Session,
) -> Result<(), Self::Error> {
// How to handle `git-upload-pack ./repo/path` command here.
let mut cmd = Command::new("git-upload-pack")
.arg("/home/git/repo");
// ...
Ok(())
}
} In code above when i execute |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
You can translate it pretty much very closely. Start your process with e.g. tokio::process, convert your channel into an R/W pair with make_writer and make_reader and pass that as the processes' stdio |
Beta Was this translation helpful? Give feedback.
You can translate it pretty much very closely. Start your process with e.g. tokio::process, convert your channel into an R/W pair with make_writer and make_reader and pass that as the processes' stdio