A minimal ngrok liked reverse proxy implemented in Rust.

Overview

rok

A minimal ngrok implementation in Rust, for educational purpose. This work is largely based on rathole, especially the very first commit. Other honorable references are stated below here.

This is by no mean an idiomatic or correct Rust implementation. I am learning Rust and fairly new to writing networking code with tokio.

Implementation and limitations

Last Updated: 21st June 2022

Currently, it's a half baked implementation. It basically:

  • Have a server to listen to incoming Init packet from a client and reply it with a domain the client can use.
  • Upon running the client, it will send an Init packet, to get the domain name.
  • Then, the client send a DataInit packet to the server, setting up another TCP tunnel listening to request from the server and proxy it to the local server. It's currently hardcoded to forward to localhost:4000
  • Once the server receive an DataInit packet, it will spin up a TcpListener on the assigned port for the particular domain name, which is hardcoded as well. This will then allow the public internet to send request to the domain, which will be proxy to the client local server through the TCP tunnel we setup.
  • Once the client tunnal receive and respond with a request, the whole TCP tunnel will be shutdown, and it will resend the DataInit packet again to setup another TCP tunnel.
  • Same applied to the TcpListener that is exposed to the internet. Hence, it's not an efficient implementation as it will spin up and shutdown 2 TCP connection on every request.

On top of that:

  • It only support 3 domain name, that means it currently support at most 3 client at a time.
  • It doesn't work with websocket.
  • The domain name is not recycle. This mean that if a client lost it's connection, the domain assigned do not goes back to the domain name pool.
  • It doesn't support logging HTTP request and response time.

Flow

Last Updated: 24th May 2022

The following flow will be the initial first attempt to make a minimal reverse proxy. It's subject to changes as we involved our implementation.

  1. First, a client send an Init packet to setup the control channel with the server. Then, the server will assigned a domain and port for the control channel and reply the domain name to the client.
        Init
Client ------> Server
       <------
       Domain
  1. Once the client receive the domain name, it send an Ack packet and the server will proceed to update the state to keep track of the control channel spawned.
         Ack
Client ------> Server (proceed to store the control channel information in the state)
  1. Client will then proceed to setup a data channel with the server and a TcpStream to connect to the local service, waiting for the incoming request from the server, so it can proxy the request to the local service and respond back to the server through the data channel.
                TCP connection            DataInit
Local Service <----------------> Client -----------> Server
  1. When server receive the DataInit packet, it start listening to the particular port assigned to the domain name and start to forward any request to the data channel to the client, which the client will proxy it to the local service and reply the responses to us through the same data channel, which we then return to the incoming request.
            TCP listener            Proxy request
Internet <---------------> Server <---------------> Server
  1. Once a request is served, the data channel, TCP listner on the server and TCP connection to the local service from the client will all be shutdown. Step 3, 4 and 5 will be repeated.

References

  • ngrok: To understand how to setup tunneling on a high level.
  • rathole: The first commit of a lightweight Rust ngrok liked implementation. Use TCP all the way.
  • tunnelto: Another Rust ngrok liked implementation. It use websockets to setup the control channel.
  • tokio.rs: Tokio tutorials.
You might also like...
Proxy sentry request to a sentry server using a tunnel/proxy endpoint

Sentry Tunnel This is a proxy that forwards tunneled sentry requests to the real sentry server. The implementation is based on the explanation provide

UDP proxy with Proxy Protocol and mmproxy support
UDP proxy with Proxy Protocol and mmproxy support

udppp UDP proxy with Proxy Protocol and mmproxy support. Features Async Support Proxy Protocol V2 SOCKET preserve client IP addresses in L7 proxies(mm

Lightweight proxy that allows redirect HTTP(S) traffic through a proxy.

Proxyswarm Proxyswarm is a lightweight proxy that allows redirect HTTP(S) traffic through a proxy. WARNING: This app isn't recomended for download lar

Web3-proxy: a fast caching and load balancing proxy for web3 (Ethereum or similar) JsonRPC servers.

web3-proxy Web3-proxy is a fast caching and load balancing proxy for web3 (Ethereum or similar) JsonRPC servers. Signed transactions (eth_sendRawTrans

 RedLizard - A Rust TCP Reverse Shell with SSL
RedLizard - A Rust TCP Reverse Shell with SSL

RedLizard - A Rust TCP Reverse Shell with SSL RedLizard Rust TCP Reverse Shell Server/Client This is a reverse shell in Rust called RedLizard, basical

Interactive bind/reverse PTY shell with Windows&Linux support implementation by Rust.
Interactive bind/reverse PTY shell with Windows&Linux support implementation by Rust.

Cliws Lightweight interactive bind/reverse PTY shell with Windows&Linux support implementation by Rust. Features WebSocket Full pty support: VIM, SSH,

Utility for working with reverse DNS

RDNS RDNS is a small Rust CLI utility for performing single and bulk reverse DNS (PTR) lookups. Usage RDNS 0.1.0 Joe Banks [email protected] Utilities for

A fast, offline reverse geocoder in Python
A fast, offline reverse geocoder in Python

Reverse Geocoder A Python library for offline reverse geocoding. It improves on an existing library called reverse_geocode developed by Richard Penman

server security proxy write by Rust

server-security-proxy server security proxy write by Rust how to use config toml file

Comments
  • Connection Logger

    Connection Logger

    This PR is the result of pairing with @erwanor during our time in the Recurse Center. The PR is mainly about:

    • Logging each HTTP request/response we receive/respond into the client terminal.
    • On top of that, we also include the duration taken for the local server response to the client.
    • Lastly, it's beautify by adding alignments and colors to the log out puts.

    Implementation Details

    Support logging

    Initially, we are forwarding the bytes from one TcpStream to another TcpStream using copy_bidirectional. However, since it encapsulates the bytes copied, there's no way we could peek into the bytes we receive. Initially, we thought of two possible ways to implement the logging:

    • Write our own copy bidirectional manually. Basically, read on TcpStream and the write it to another manually.
    • Extend copy_bidirectional to accept an event channel. This mean that we can just pass in a Sender to the copy_bidirectional function, which will call send on every read it received. Then, our code can listen to the Receiver and process the bytes as needed.
      • This approach also means that we need to copy most of the code from the existing copy_bidirectional implementation.

    On the next day of Pairing, Erwan thought of another approach: we can implement our own AsyncRead and AsyncWrite trait to wrap the TcpStream and pass it to copy_bidrectional. This way, we could also peek into the bytes when poll_read is called by copy_bidirectional. We decided to approach with this approach and it works really well. This result in the Logger struct that implement AsyncRead + AsyncWrite, where the inner is a TcpStream in our case.

    Tracking state

    The next feature we want to support is to track the duration takes. This mean that we need to have an Instant::now() created when a request is received and called elapsed() when a response is received. We tried a couple of different ways and ended up adding a LoggerState struct that will hold the shared state by both Logger. The state is then wrapped in Arc<Mutex> to allow it to be hold by multiple struct.

    It works as we tested, but we also learn that in browser, a TCP connection might be reused across refresh and and tabs. So, we can't just assume that our TcpStream will be closed once a request is served. Hence, we need to reset the timestamp state everytime a response is logged. This is achieved by calling timestamp.take() to take out the Instant in our Option and replace it with None.

    opened by kw7oe 0
Owner
Kai
Kai
A high performence Socks5 proxy server with bind/reverse support implementation by Rust.

rsocx A high performence Socks5 proxy server with bind/reverse support implementation by Rust Features Async-std No unsafe code Single executable Linu

b23r0 259 Jan 6, 2023
A fast and stable reverse proxy for NAT traversal, written in Rust

rathole A fast and stable reverse proxy for NAT traversal, written in Rust rathole, like frp, can help to expose the service on the device behind the

Yujia Qiao 4.6k Dec 30, 2022
A lightweight Rust reverse proxy.

Brachyura A reverse proxy, which I am primarily using as a Rust / Hyper learning project. I utilize Nginx as part of my home lab providing reverse pro

William Howard 8 Jan 8, 2023
Reverse proxy for HTTP microservices and STDIO. Openfass watchdog which can run webassembly with wasmer-gpu written in rust.

The of-watchdog implements an HTTP server listening on port 8080, and acts as a reverse proxy for running functions and microservices. It can be used independently, or as the entrypoint for a container with OpenFaaS.

yanghaku 7 Sep 15, 2022
A firewall reverse proxy for preventing Log4J (Log4Shell aka CVE-2021-44228) attacks.

log4jail ??️ A fast firewall reverse proxy with TLS (HTTPS) and swarm support for preventing Log4J (Log4Shell aka CVE-2021-44228) attacks. ?? Table of

Mufeed VH 22 Dec 27, 2022
A multi-connection TCP reverse proxy server and client.

tprox A multi-connection TCP reverse proxy. The tprox server is able to proxy multiple incoming connections to the tprox client over a single TCP conn

Mohammed Ajmal Siddiqui 4 Sep 21, 2022
Hopper - Fast, configurable, lightweight Reverse Proxy for Minecraft

Hopper Hopper is a lightweight reverse proxy for minecraft. It allows you to connect multiple servers under the same IP and port, with additional func

Pietro 174 Jun 29, 2023
An easy-to-use tunnel to localhost built in Rust. An alternative to ngrok and frp.

rslocal English | 中文 What is rslocal? Rslocal is like ngrok built in Rust, it builds a tunnel to localhost. Project status support http support tcp su

saltbo 220 Jan 7, 2023
Self-hosted, fast, and efficient replacement for ngrok, built with Rust

reverse-proxy reverse-proxy is a self-hosted, fast, and efficient replacement for ngrok, built with Rust. The project leverages the power of Tokio and

null 4 May 22, 2023
A tcp port forwarding system like ngrok.

Pruxy A tcp port forwarding system like ngrok. Todo http request handler agent <-> server connection agent How to use Generate cert files mkdir ssl_ce

null 1 Jan 24, 2022