net-proxy banner

net-proxy

6 devlogs
16h 56m 11s

A super simple VPN made with Rust. The client uses the server as a proxy for all requests. You can use this to access your home network from anywhere or just use it as a normal vpn.

This project uses AI

used for debugging and help, creating network routing commands, and docker testing setup

Demo Repository

Loading README...

mattseq

Compiling

I was having problems cross-compiling for Linux because I wanted x86_64 but my raspberry pi is arm and couldn’t compile for it. Then I found cross, a Rust crate that allows for best cross-compiling using Docker which is exactly what I was looking for.

Testing

Testing Further in Docker

I tested the NetworkConfigurators to ensure that they were being set up and, most importantly, were resetting the network config after being shut down. Everything was working properly.

Testing For Real

I managed to convince one of my friends who has a Linux laptop to help me test it. I ran the server and he connected with the client. I fixed several dumb mistakes. One of the main ones was that I hardcoded the network device assuming it would be eth0 when I wasn’t using ethernet. I was stuck on that for a while.

Other Stuff

I added a README, finally. The screenshot below is of my friend’s computer. He connected to my vpn and looked up his IP and it showed mine. Obviously, you can’t see that because I censored it, but it was too cool not to share.

Attachment
0
mattseq

Nonce Window

When the client and server send encrypted messages to each other they include whats called a nonce (number used once). It’s supposed to make every message unique and prevent replay attacks. I implemented a NonceWindow class to verify that the nonce (u64) is close enough to the last nonce sent and verify that it hasn’t already been sent before. I did that by using a bitmap. It’s essentially used as an array of booleans which represent the window of nonces before the last seen nonce. I can toggle each bit to mark whether that nonce has been seen before and check whether the nonce is within the window or if its too late.

CLI

I made the server and client packages into libraries and made a root package for the CLI (which I used the clap crate for). I also had to update the docker setup to use one image instead of building 2. The server and client functions also accept ip, port, and password instead of hardcoding them. The gateway ip is parsed from the ip route show command.

In the screenshot below you can see the nonce checks. It almost always comes right in front of the window and they never really come late or too early.

Attachment
0
mattseq

Project Structure

Because there were so many shared elements between the server and client programs, I decided to restructure the project into a workspace with common, client, and server folders. In the common library I included the VpnEngine which handles the main connection between client and server and proxies requests. The code for client and server is so similar that it was easy to put it all into one VpnEngine.

NetworkConfigurator

I then made a trait in the common library called NetworkConfigurator which handles setting up and tearing down network rules like forwarding and NAT. The server and client programs define there own network configurators using that trait and implement their own custom setup and teardown. It also uses a Drop trait to ensure that teardown happens if the object is dropped.

Testing

Like before, I’m testing this on a single machine using Docker containers. I recently found out about the traceroute command and although I still don’t understand how it works, it did show that my client’s packets are being routed through the VPS. I attached an image below. 10.0.0.2 is the tun ip of the VPS btw (the client is 10.0.0.1). I executed traceroute on the client and it shows it routes through 10.0.0.2. Pretty cool :)

Next Steps

I still have to implement some more checks like for the nonce. After that I might actually test it for real but first I need to make it into a CLI tool. I’m thinking proxy connect <ip:port>, proxy disconnect, proxy serve. And maybe something like -d for detached. I don’t have a lot of time left though. I still intend to keep the docker setup so people don’t have to test the demo by exposing their computers to the internet. Which reminds me, I also need to find a better way to input the access key.

Attachment
0
mattseq

Encryption

Handshake

The initial handshake uses the x25519-dalek rust crate for Diffie-Hellman key exchange. The client’s handshake request includes the payload and the signed payload. The payload is client’s public key + timestamp (for verification and to prevent replay attacks, not implemented yet). It’s signed by the passkey which both the client and server share. The server checks the payload with the signed payload to verify the client’s passkey. Once that happens, it sends its own public key to the client for the rest of the Diffie-Hellman exchange. They both compute the shared key and that key is hashed to get the session key.

Encryption

The session key is then used for encryption. Every packet between the client and server is encrypted with ChaCha20-Poly1305, which uses a nonce to guard against replay attacks. The nonce is just a counter that increases every message and guarantees that each message is unique. I can also help verify messages use the nonce that was sent along with it, but I haven’t done that yet.

Testing & Debugging

I had a few issues with the handshake at first. It was because I wrote all of the handshake code to execute after I set up tun0 and routing, I think. I also made the client constantly try to send handshake requests. In the picture attached, you can see the handshake being logged and the following encrypted packets.

Next Steps

I still need to verify the handshake further using the timestamp and verify the encrypted packets using the nonce to guard against replay attacks. As I said in the previous devlog, I want to turn this into a CLI tool as well. Then I’ll test with my own home network.

Attachment
0
mattseq

Testing

Dockerize

In order to test the VPN without actually exposing my own network, I decided to run two docker containers on my machine, one as the client and the other as the server. After some debugging with creating the tun interfaces and routing, I finally got it working. I should probably test again in an actual environment, but its not secure enough for that yet, which leads me to this part.

Next Steps

Obviously the next step is to actually make this thing secure. That means authentication and encryption. I’m very excited to work on both. After that, I’ll test it for real. Another thing I want to do is make it into a CLI tool so the client can use it more easily.

Diagram

I used AI to help me generate a diagram for exactly what’s happening since my notes from the last devlog aren’t that good. I did my best to show exactly where tun comes in and why the commands are used.

Attachment
Attachment
0
mattseq

got started on the vpn. im writing it in rust which is maybe a bad idea bc im still learning it. learned about Arc and Mutex today. i havent actually tested the program yet but its mostly finished. since i havent tested it i cant show a screenshot but heres the notes i wrote during physics class about how it works

Attachment
Attachment
0