/ReverseTCPProxy

Create reverse TCP proxies

Primary LanguageGoMIT LicenseMIT

Reverse TCP Proxy

A port forwarder which the server sends the SYN packet to client.

What?

A simple TCP port forwarder usually works by creating an outgoing connection to a destination server for each incoming local connection. In nutshell, a program in client connects to port forwarder client, port forwarder client connects to a port forward sever, server establishes the connection between itself and client and then creates a connection to another host. Now the traffic between the initializer and last host is proxied between port forwarder client/server.

One thing you have to note here is the fact that the client initiates the connection between itself and server. On some cases, you may want the server to start the connection with the client. Obviously this requires the client to be exposed to the internet, either by port forwarding or by not having NAT at all.

How the fuck?

Next question is how does the server connect to the client if the client is the one which shall establish the connections and knows when it needs another stream? The answer: WE CHEAT

We don't want to make direct connection to our server, right? But it's fine for a CDN (like cloudflare) to make connection with our server. So the server program also listens for HTTP requests on another port and upon receiving a specific request, it will initiate a connection with the client.

Each connection has an ID which it's generated by the client. They serve no purpose other than logging. When the client asks for a connection (via CDN), it also specifies a random ID. In the first packet of server to client the ID is sent back to client.

Life cycle of a TCP connection

We have two programs: jailed and freedom. jailed is our client and freedom is our server.

  1. A program starts a TCP connection with jailed.
  2. Jailed creates a random ID and sends it via a simple POST request to freedom via a CDN like Cloudflare. Note that jailed is not establishing a connection to freedom; CDN is establishing it!
  3. freedom establishes a connection with jailed. The ID is given in the first packet. The first packet is obfuscated as a simple HTTP request.
  4. While this connection is being established, freedom also establishes a connection with remote host which packets should be forwarded to.
  5. jailed does the handshake (parsing the first packet and extracting the ID). The connection is rejected if the handshake fails.
  6. Now we have a stream between jailed and freedom which freedom has started it.
  7. A connection is closed on both ends upon either side closing it.

Schematic

Schematic of the connection can be illustrated in the diagram below:

Local Service             Destination Host
     ↕                               ↕
  Jailed <------- Direct -------> Freedom
     ↧                               ↥
     ┕ ----------- CDN ------------- ┙ 

Running

Building

Before anything, you must have something to run! Before starting, make sure that you have Go 1.20. Then simply clone this repo and execute following commands to build both jailed and freedom.

go build ./cmd/jailed/
go build ./cmd/freedom/

Freedom

To run the freedom app, create a file called config.json next to it. A sample config file exists in the examples directory. Here is another sample config:

{
  "control_listen": "127.0.0.1:53141",
  "jailed_computer_address": "1.1.1.1:32521",
  "destination_address": "127.0.0.1:51109"
}

All the keys are necessary and have the following meanings:

  • control_listen: The address that the control messages (the ones relayed through the CDN) comes in.
  • jailed_computer_address: The address which should be dialed to connect to the jailed PC.
  • destination_address: Where the packets should go after being proxied.

Jailed

A sample config JSON file looks like this:

{
  "local_listen_address": "127.0.0.1:24519",
  "freedom_listen_address": "0.0.0.0:32521",
  "control_endpoint": "https://my-domain.com/",
  "remote_wait_timeout": 5000
}

All keys except remote_wait_timeout are necessary.

  • local_listen_address: On which interface and port connections are being accepted from the local service.
  • freedom_listen_address: On which interface and port should we listen for direct connections from freedom.
  • control_endpoint: What is the domain behind CDN which connects us to control_listen of freedom. (Hint: You might want to use a reverse proxy like Caddy or Nginx)
  • remote_wait_timeout: How long should be waited before terminating a local connection because of the absence of remote connection. In other words, freedom has remote_wait_timeout milliseconds to make a connection with jailed, otherwise the local connection is dropped. The unit of this option is milliseconds and the default value is 10 seconds.