DISCLAIMER: This project is for study only, and does nothing with to-server requests. Use with caution and at your own risk.

Background

I really enjoy playing a game, which handles almost everything on client side, and its server side is for ranking, replays, online matches, etc. As always, the server has a bad network connection for players in mainland China like me. So I was thinking if I could setup a relay server on my VPS, to provide a better connection of the game.

1. Setup the relay server

What I was thinking is a topology as follows:

+----------+     https     +----------+     https     +----------+
|          ---------------->          ---------------->          |
|          |               |          |               |  Actual  |
|  Client  |               |   Relay  |               |  server  |
|          |               |          |               |          |
|          <----------------          <----------------          |
+----------+     https     +----------+     https     +----------+

Actually it was going quite well. I use Traefik in the frontend to route all related requests to the backend. And in the backend there is a nginx instance to do a just proxy_pass job.

One thing I'd like to mention is how nginx is dealing with SSL/TLS certification. Requests it received from the frontend is http w/o TLS. I had to add the marked lines to make it work with actual server side TLS certification:

server {
  listen 8080;
  server_name localhost;      # <--
  location / {
    resolver 8.8.8.8;
    proxy_pass https://actual.server.com$request_uri;
    proxy_ssl_server_name on; # <--
    proxy_ssl_session_reuse on;
  }
}

Let's take a closer look of the topology now:

                           +----------------------------------------------+
                           |                 Relay server                 |
                           |                                              |
+----------+     https     |   +---------+       http       +---------+   |     https     +----------+
|          -------------------->         ------------------->         -------------------->          |
|          |               |   |         |                  |         |   |               |  Actual  |
|  Client  |               |   | Traefik |                  |  nginx  |   |               |  server  |
|          |               |   |         |                  |         |   |               |          |
|          <--------------------         <-------------------         <--------------------          |
+----------+     https     |   +---------+       http       +---------+   |     https     +----------+
                           |                                              |
                           +----------------------------------------------+

So it works... Does it? Yeah technically it works very well, for doing the relay job. But here's an issue that about half of the time I can't hold a stable network connection between my client and the relay server! MANY THANKS TO THE FIREWALL. So it comes the bad news that the relay server doesn't do any help of a better connection.

But I would not stop here. Quickly I realized the whole traffic between the client and the server was going through my relay server, which meant that I could monitor anything in it, or even manipulate it as I want.

Let's insert a middleware within the relay server!

2. Insert the middleware

I'd like to get rid of all the SSL/TLS stuff. So the best place are there: between Traefik and nginx. I put a http proxy in Golang there (first time Golang experience!).

The http proxy instance is a modified version of wtsi-hgi/http-proxy-logger, with which I can dump the traffic I care to try to understand the packet structure. Now the topology looks like this:

                           +-------------------------------------------------------------+
                           |                         Relay server                        |
                           |                                                             |
+----------+     https     |  +---------+   http   +-------------+   http   +---------+  |     https     +----------+
|          ------------------->         -----------|-----(#)-----|---------->         ------------------->          |
|          |               |  |         |          |             |          |         |  |               |  Actual  |
|  Client  |               |  | Traefik |          |  Http Proxy |          |  nginx  |  |               |  server  |
|          |               |  |         |          |             |          |         |  |               |          |
|          <-------------------         <----------|-----(#)-----|-----------         <-------------------          |
+----------+     https     |  +---------+   http   +-------------+   http   +---------+  |     https     +----------+
                           |                                                             |
                           +-------------------------------------------------------------+

                                                         (#) - dump requests/responses

Everything is under my control. Can I do some funny things to the traffic?

3. Trick the client

Thanks to other excellent open-source private server implements, I can find out what messages are sent back to the client by directly reading their source code. It caught my attention that a very bit indicates the user is a supporter, which unlocks some more options on the client side (, and client side only!).

It took me a bit longer time to exactly understand how the bytes are transferred in the response body and extract them in Golang (code). And finally I was very surprised that the whole flow worked! Here's the topology now:

                           +-------------------------------------------------------------+
                           |                         Relay server                        |
                           |                                                             |
+----------+     https     |  +---------+   http   +-------------+   http   +---------+  |     https     +----------+
|          ------------------->         -----------|-------------|---------->         ------------------->          |
|          |               |  |         |          |             |          |         |  |               |  Actual  |
|  Client  |               |  | Traefik |          |  Http Proxy |          |  nginx  |  |               |  server  |
|          |               |  |         |          |             |          |         |  |               |          |
|          <-------------------         <----------|-----(*)-----|-----------         <-------------------          |
+----------+     https     |  +---------+   http   +-------------+   http   +---------+  |     https     +----------+
                           |                                                             |
                           +-------------------------------------------------------------+

                                                         (*) - trick here!

What's next?

Well, when I found that special bit, I was asking more options that the client could give me. But the reality is it only gives me about 50% of my expectation. It's not a actual issue because I was giving a best guess of it.

So I'm planing some further steps from here, including bringing back a monitor-only mode, finding more interesting bytes in the traffic, and one more step: can I do some server side jobs? Now I'm so curious if it's possible to implement the friend ranking in the relay side.

Will see later (if any)!

Published under Creative Commons Attribution-ShareAlike (CC-BY-SA) license. Feel free to comment or share :)