#remote #server #redirect #port #local #traffic #tunnels

nightly app ratrod

Tunnels a local port to a remote server, which then redirects traffic to a specified remote host

3 releases

new 0.3.2 Feb 17, 2025
0.3.1 Feb 17, 2025
0.3.0 Feb 17, 2025

#295 in Network programming

Download history 118/week @ 2025-02-11

131 downloads per month

MIT license

57KB
864 lines

Build and Test codecov Version Crates.io GitHub all releases Documentation Rust License:MIT

ratrod

A (likely semi-inefficient) TCP tunneler that uses public/private key authentication without subsequent encryption. Basically, it's ssh -L, but without the encryption. This is useful for tunneling through a machine that doesn't support SSH.

Basic Usage

Usage

$ ratrod -h
Tunnels a local port to a remote server, which then redirects the traffic to a specified remote host

Usage: ratrod [COMMAND]

Commands:
  serve             Start a server on this machine that listens for incoming connections and forwards them to a remote server (as specified by the client)
  connect           Connects to a server and forwards traffic from a local port to a remote `host:port` "through" the server
  generate-keypair  Generates a keypair and prints it to the console
  help              Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version

Generate Keypair

$ ratrod generate-keypair -h
Generates a keypair and prints it to the console

Usage: ratrod generate-keypair

Options:
  -h, --help  Print help (see more with '--help')
$ ratrod generate-keypair
2025-02-16T21:46:14.114260Z  INFO 📢 Public key: `kRIPwCb48iyW6F_48GA2GX_7iIXaDSzN1xrZpxJVrR4`
2025-02-16T21:46:14.114288Z  INFO 🔑 Private key: `MFECAQEwBQYDK2VwBCIEIC0eDq_DR7flRXszmG_4USM6f4hM12TjAxLbJotjP-OzgSEAkRIPwCb48iyW6F_48GA2GX_7iIXaDSzN1xrZpxJVrR4`

Start a Server

$ ratrod serve -h
Start a server on this machine that listens for incoming connections and forwards them to a remote server (as specified by the client)

Usage: ratrod serve [OPTIONS] <BIND>

Arguments:
  <BIND>  Specifies the local `host:port` to bind to

Options:
  -k, --key <KEY>                    Specifies an optional private key to use for generating the keypair
  -r, --remote-regex <REMOTE_REGEX>  Specifies an optional regex restriction on the remote hostnames that can be connected to. This is used to prevent clients from connecting to arbitrary through the server [default: .*]
  -h, --help                         Print help (see more with '--help')

Basic usage generates a random private key, which printed to the console for the user to give to clients.

$ ratrod serve 0.0.0.0:19000
2025-02-16T21:46:54.989623Z  INFO 🔑 Private key (for clients): `MFECAQEwBQYDK2VwBCIEILrnsOXw6V5tYH7Svvxxtrhjn8DojNSomOtCDARkokDGgSEAwIB407MrQdO3iGRFn7pGjJDnJKPFXCnGFelX1OJa0oc`.
2025-02-16T21:46:54.989651Z  INFO 🚀 Starting server on `[::]:19000` ...

If you have a generated keypair, you can specify the private key with the --key (-k) flag.

$ ratrod serve -k MFECAQEwBQYDK2VwBCIEIC0eDq_DR7flRXszmG_4USM6f4hM12TjAxLbJotjP-OzgSEAkRIPwCb48iyW6F_48GA2GX_7iIXaDSzN1xrZpxJVrR4 [::]:19000
2025-02-16T23:06:23.204880Z  INFO 🔑 Private key (for clients): `MFECAQEwBQYDK2VwBCIEIC0eDq_DR7flRXszmG_4USM6f4hM12TjAxLbJotjP-OzgSEAkRIPwCb48iyW6F_48GA2GX_7iIXaDSzN1xrZpxJVrR4`.
2025-02-16T23:06:23.204911Z  INFO 🚀 Starting server on `[::]:19000` ...
2025-02-16T23:06:28.764912Z  INFO conn{id="BPxETR"}: ✅ Accepted connection from `[::ffff:127.0.0.1]:48720`.
2025-02-16T23:06:28.765303Z  INFO conn{id="BPxETR"}: 🚧 Sending handshake challenge to client ...
2025-02-16T23:06:28.766762Z  INFO conn{id="BPxETR"}: ✅ Handshake challenge completed!
2025-02-16T23:06:28.766838Z  INFO conn{id="BPxETR"}: ✅ Handshake completed.
2025-02-16T23:06:28.801000Z  INFO conn{id="BPxETR"}: ✅ Connected to remote server `google.com:80`.
2025-02-16T23:06:28.801077Z  INFO conn{id="BPxETR"}: ⛽ Pumping data between client and remote ...
2025-02-16T23:06:28.916065Z  INFO conn{id="BPxETR"}: ✅ Connection closed.
2025-02-16T23:06:42.180789Z  INFO conn{id="nyulYr"}: ✅ Accepted connection from `[::ffff:127.0.0.1]:53786`.
2025-02-16T23:06:42.181316Z  INFO conn{id="nyulYr"}: 🚧 Sending handshake challenge to client ...
2025-02-16T23:06:42.183300Z  INFO conn{id="nyulYr"}: ✅ Handshake challenge completed!
2025-02-16T23:06:42.183360Z  INFO conn{id="nyulYr"}: ✅ Handshake completed.
2025-02-16T23:06:42.200463Z  INFO conn{id="nyulYr"}: ✅ Connected to remote server `google.com:80`.
2025-02-16T23:06:42.200516Z  INFO conn{id="nyulYr"}: ⛽ Pumping data between client and remote ...
2025-02-16T23:06:42.367411Z  INFO conn{id="nyulYr"}: ✅ Connection closed.
2025-02-16T23:06:42.792522Z  INFO conn{id="Zht5ma"}: ✅ Accepted connection from `[::ffff:127.0.0.1]:53790`.
2025-02-16T23:06:42.792788Z  INFO conn{id="Zht5ma"}: 🚧 Sending handshake challenge to client ...
2025-02-16T23:06:42.794003Z  INFO conn{id="Zht5ma"}: ✅ Handshake challenge completed!
2025-02-16T23:06:42.794052Z  INFO conn{id="Zht5ma"}: ✅ Handshake completed.
2025-02-16T23:06:42.806509Z  INFO conn{id="Zht5ma"}: ✅ Connected to remote server `google.com:80`.
2025-02-16T23:06:42.806561Z  INFO conn{id="Zht5ma"}: ⛽ Pumping data between client and remote ...
2025-02-16T23:06:42.973926Z  INFO conn{id="Zht5ma"}: ✅ Connection closed.

Connect to a Server

$ ratrod connect -k MFECAQEwBQYDK2VwBCIEIC0eDq_DR7flRXszmG_4USM6f4hM12TjAxLbJotjP-OzgSEAkRIPwCb48iyW6F_48GA2GX_7iIXaDSzN1xrZpxJVrR4 192.168.1.100:19000 2000:google.com:80
2025-02-16T23:08:37.303408Z  INFO 📻 Listening on `localhost:2000`, and routing through `localhost:19000` to `google.com:80` ...
2025-02-16T23:08:39.812532Z  INFO conn{id="Be8spx"}: ✅ Connected to server `localhost:19000` ...
2025-02-16T23:08:39.812662Z  INFO conn{id="Be8spx"}: ✅ Sent preamble to server ...
2025-02-16T23:08:39.813250Z  INFO conn{id="Be8spx"}: 🚧 Handshake challenge received ...
2025-02-16T23:08:39.813810Z  INFO conn{id="Be8spx"}: ⏳ Awaiting challenge validation ...
2025-02-16T23:08:39.815189Z  INFO conn{id="Be8spx"}: ✅ Challenge accepted ...
2025-02-16T23:08:39.815217Z  INFO conn{id="Be8spx"}: ✅ Handshake successful: connection established!
2025-02-16T23:08:39.815230Z  INFO conn{id="Be8spx"}: ⛽ Pumping data between client and remote ...
2025-02-16T23:08:40.000492Z  INFO conn{id="Be8spx"}: ✅ Connection closed.
2025-02-16T23:08:40.948126Z  INFO conn{id="jWLwHH"}: ✅ Connected to server `localhost:19000` ...
2025-02-16T23:08:40.948305Z  INFO conn{id="jWLwHH"}: ✅ Sent preamble to server ...
2025-02-16T23:08:40.949282Z  INFO conn{id="jWLwHH"}: 🚧 Handshake challenge received ...
2025-02-16T23:08:40.950223Z  INFO conn{id="jWLwHH"}: ⏳ Awaiting challenge validation ...
2025-02-16T23:08:40.951574Z  INFO conn{id="jWLwHH"}: ✅ Challenge accepted ...
2025-02-16T23:08:40.951625Z  INFO conn{id="jWLwHH"}: ✅ Handshake successful: connection established!
2025-02-16T23:08:40.951650Z  INFO conn{id="jWLwHH"}: ⛽ Pumping data between client and remote ...
2025-02-16T23:08:41.126602Z  INFO conn{id="jWLwHH"}: ✅ Connection closed.
2025-02-16T23:08:41.813399Z  INFO conn{id="fPku4k"}: ✅ Connected to server `localhost:19000` ...
2025-02-16T23:08:41.813513Z  INFO conn{id="fPku4k"}: ✅ Sent preamble to server ...
2025-02-16T23:08:41.814171Z  INFO conn{id="fPku4k"}: 🚧 Handshake challenge received ...
2025-02-16T23:08:41.815062Z  INFO conn{id="fPku4k"}: ⏳ Awaiting challenge validation ...
2025-02-16T23:08:41.816399Z  INFO conn{id="fPku4k"}: ✅ Challenge accepted ...
2025-02-16T23:08:41.816450Z  INFO conn{id="fPku4k"}: ✅ Handshake successful: connection established!
2025-02-16T23:08:41.816474Z  INFO conn{id="fPku4k"}: ⛽ Pumping data between client and remote ...
2025-02-16T23:08:41.955483Z  INFO conn{id="fPku4k"}: ✅ Connection closed.

The host argument accepts the form [local_host:[local_port:[remote_host:]]]remote_port. This means you could have various scenarios like this:

  • 0.0.0.0:2000:google.com:80: connects to google.com:80 and listens on 0.0.0.0:2000.
  • 2000:google.com:80: connects to google.com:80 and listens on localhost:2000.
  • 2000:80: connects to server:80 and listens on localhost:2000.
  • 80: connects to server:80 and listens on localhost:80.

Install

Windows:

$ iwr https://github.com/twitchax/ratrod/releases/latest/download/ratrod_x86_64-pc-windows-gnu.zip
$ Expand-Archive ratrod_x86_64-pc-windows-gnu.zip -DestinationPath C:\Users\%USERNAME%\AppData\Local\Programs\ratrod

Mac OS (Apple Silicon):

$ curl -LO https://github.com/twitchax/ratrod/releases/latest/download/ratrod_aarch64-apple-darwin.zip
$ unzip ratrod_aarch64-apple-darwin.zip -d /usr/local/bin
$ chmod a+x /usr/local/bin/ratrod

Linux:

$ curl -LO https://github.com/twitchax/ratrod/releases/latest/downloadratrod_x86_64-unknown-linux-gnu.zip
$ unzip ratrod_x86_64-unknown-linux-gnu.zip -d /usr/local/bin
$ chmod a+x /usr/local/bin/ratrod

Cargo:

$ cargo install ratrod

Dependencies

~13–24MB
~415K SLoC