Eugeny / russh

Rust SSH client & server library
https://docs.rs/russh
954 stars 107 forks source link

client::Handle::channel_open_direct_tcpip blocks and does not return until connection fails. #329

Open dcechano opened 2 months ago

dcechano commented 2 months ago

I am trying to set up simple port forwarding through a jump host. I am doing this by using client::connect to get a client::Handle then (after authenticating) using the handle to open a Tcp/Ip forwarding channel through client::Handle::channel_open_direct_tcpip.

      let socketaddr: Vec<SocketAddr> =
            tokio::net::lookup_host(format!("{}:{}", SETTINGS.jump_host, SETTINGS.ssh_port))
                .await?
                .collect();

        let config = Arc::new(config);
        let sh = Client;

        let mut handle = client::connect(config, socketaddr.as_slice(), sh)
            .await
            .expect("Client connection failed");

        let auth_res = handle
            .authenticate_publickey(SETTINGS.username, Arc::new(key_pair))
            .await
            .expect("public key authentication failed");

        if !auth_res {
            anyhow::bail!("Authentication failed");
        }

        // This call blocks
        let channel = handle
            .channel_open_direct_tcpip(
                SETTINGS.remote_host,
                SETTINGS.remote_port,
                "localhost",
                SETTINGS.local_port,
            )
            .await
            .expect("Error while port forwarding.");

        // This line never gets to execute.
        println!("Forwarding channel (ostensibly) connected!");

        // let mut reader = channel.make_reader();
        // let mut writer = channel.make_writer();

        Ok(Self {
            handle,
            id: channel.id(),
        })

I have noticed that the Handler::openssh_ext_host_keys_announced is the last Handler method to execute. This suggests that the server is responding to the forwarding request and willing to allow it. I am suspicious that I am not implementing this method properly and the server is waiting for me to confirm the public keys and it eventually stops waiting and closes the connection. Is this correct? If so how do you confirm the public keys?

#[async_trait]
impl Handler for Client {
    type Error = russh::Error;

    /// Other methods are not shown.
    /// I have them written so they panic,
    /// so I know they are not being called.
    #[allow(unused_variables)]
    async fn check_server_key(
        &mut self,
        _server_public_key: &PublicKey,
    ) -> Result<bool, Self::Error> {
        //TODO impl proper public key checking
        Ok(true)
    }

    /// Called when the server signals success.
    #[allow(unused_variables)]
    async fn openssh_ext_host_keys_announced(
        &mut self,
        keys: Vec<PublicKey>,
        session: &mut Session,
    ) -> Result<(), Self::Error> {
        println!("openssh_ext_host_keys_announced");
        Ok(())
    }
}

To investigate I have confirmed that the server is responding to ssh and port forwarding by using the GNU ssh command that is native to linux. I have also opened connections without port forwarding (via russh) and they seem to return just fine. Does any one have any suggestions on avenues I could explore that may shed light on the issue? I appreciate the help. Thank you! (Console output below)

Opening forwarding channel.
openssh_ext_host_keys_announced
thread 'tokio-runtime-worker' panicked at /home/.../repos/p3-command-center2/src-tauri/src/session.rs:63:14:
Error while port forwarding.: ChannelOpenFailure(ConnectFailed)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at src-tauri/src/main.rs:110:6:
called `Result::unwrap()` on an `Err` value: JoinError::Panic(Id(9), ...)
Eugeny commented 2 months ago

Can you actually connect to {SETTINGS.remote_host}:{SETTINGS.remote_port} from your SSH server? The hang is likely your server waiting for the failed connection to timeout and ChannelOpenFailure(ConnectFailed) is the server stating it could not connect to the remote target.

dcechano commented 2 months ago

Can you actually connect to {SETTINGS.remote_host}:{SETTINGS.remote_port} from your SSH server? The hang is likely your server waiting for the failed connection to timeout and ChannelOpenFailure(ConnectFailed) is the server stating it could connect to the remote target.

Thank you for getting back to me. Yes I am able to connect to the ssh server. I tested it using the ssh -i /path/to/aws_key.pem -p 22 -L 12345:remote_host_ip:55022 user@jump_host_ip and it connects with no issue.

Eugeny commented 2 months ago

But are you then able to connect to localhost:12345? Just running an SSH connection with -L does not open a direct-tcpip channel by itself, that only happens once you actually connect to the local port.

dcechano commented 2 months ago

But are you then able to connect to localhost:12345? Just running an SSH connection with -L does not open a direct-tcpip channel by itself, that only happens once you actually connect to the local port.

Ok. I understand what you are saying. How do you suggest I connect to it from the ssh server? Would a ping be sufficient or should I open a tcp stream and try writing to it?

Eugeny commented 2 months ago

Yes, you can just use netcat: nc <target-ip> <target-port> on your SSH server

dcechano commented 2 months ago

Thank you! I will try this. I appreciate your help.