Reading a Remote File Using Rust

1. Introduction

Remote servers are essential in modern computing and are used by many institutions from small businesses to enterprises. SSH (Secure Shell) is a protocol that allows secure remote access to systems. By using SSH, we can interact with files on a remote server without downloading them locally.

In this tutorial, we’ll explore how to use the ssh2 crate to establish a connection to a remote server. Also, we’ll explore how to execute a command to read a file on a remote server. Finally, we’ll display the remote file content in our local console.

2. Project Setup

To begin, let’s create a new Rust project using Cargo, Rust’s package manager and build tool:

$ cargo new file_reader

The command above creates a new Rust project with a src directory and Cargo.toml file.

Next, let’s add the ssh2 crate to our Cargo.toml file by opening the file and adding the crate under [dependencies] section:

  [dependencies]
  ssh2 = "0.9.4"

This dependency provides methods to establish a connection to a remote server through SSH. Alternatively, we can add the dependency by using the cargo add command:

$ cargo add ssh2

Now that we have the necessary dependency, we can start implementing our remote file reader.

3. Connection Details

Next, define our connection details to the remote server:

const HOST: &str = "HOST_IP";
const USERNAME: &str = "USERNAME";
const PATH_TO_KEY: &str = "PATH_TO_KEY";
const PORT: i32 = 22;

The HOST stores the IP to the remote server while PATH_TO_KEY stores the path to the key on our local machine for authentication. Also, we define the USERNAME and port to SSH. Notably, SSH runs on port 22 by default.

4. Creating SSH Session

After defining the connection details, we need to create an SSH session to establish a connection to the remote server.

First, let’s define the connection address to the remote server:

let address = format!("{}:{}", HOST, PORT);

In the code above, we define an address that combines the HOST and PORT. Next, let’s create a TCP stream:

let tcp = TcpStream::connect(&address)?;

TCP stream is essential because it provides the reliable, connection-oriented communication channel that SSH requires to operate.

Then, let’s create an SSH session on top of the TCP connection:

let mut session = Session::new()?;
session.set_tcp_stream(tcp);

In the code above, we create a session variable of the type Session from the ssh2 crate. Then we invoke the session.set_tcp_stream() on the session and pass the TCP as an argument.

Next, let’s add our authentication credentials:

session.handshake()?;
session.userauth_pubkey_file(USERNAME, None, Path::new(PATH_TO_KEY), None)?;

In the code above, we added our authentication credentials to get access to the remote server.

5. Opening Channel and Executing Command

Furthermore, let’s open a new channel on the established session:

let mut channel = session.channel_session()?;
channel.exec("cat /home/opc/file.txt")?;

Here, we establish a new channel to execute a command on the remote server by invoking the channel_session() method on a session. Then we invoke exec() method on a channel and write a command. In the command, we use cat to read a file. Notably, we define the path of the file after the cat command.

Finally, let’s output the content of the file to the console:

let mut file_content = String::new();
channel.read_to_string(&mut file_content)?;
println!("{}",file_content);

In the code above, we read the output of the command we executed earlier to a string using the read_to_string() method and print it to the console.

6. Closing the Channel

After successful execution, we need to close the SSH channel:

channel.wait_close()?;
println!("Exit status: {}", channel.exit_status()?);

We invoke the wait_close() command on the channel to close it after a successful operation. Closing the channel allows us to free resources.

7. Conclusion

In this article, we learned how to connect to a remote server. Additionally, we saw how to create a channel on the established server and execute commands on the remote server.

The full example code for this article is available on GitHub.