Bypassing Deep Packet Inspection for VPN

When Wireguard and typical VPN gets blocked in your country and you're needing a little bit more...

Background

Following some civil unrests in my country, the government has slowly began to block off civilians from accessing a lot of sites. Unsurprisingly, people just got around this by simply downloading VPNs and using them to bypass government blockades. This worked for a while but then they started cracking down on VPNs too. Most of the free VPNs would simply refuse to work. For the paid apps, the government would setup checkpoints on the road where they would stop the cars and search the phones for VPN or other unwanted information and would be made to pay huge fines. This was something me and my family had to deal with and we would always have to delete the VPN apps when traveling downtown.

To bypass this, I deployed a wireguard server on Digital Oceanarrow-up-right, and setup a Wireguard server which was cheaper then the monthly subscriptions that we were paying for VPNs. However, this didn't last long either as only a few months passed before the Wireguard connections were being throttled and sometimes not connecting at all.

I suspected that Wireguard connections were being blocked by DPI. Deep Packet Inspection (DPI) is a form of network packet filtering that examines the data part (and possibly also the header) of a packet as it passes an inspection point.DPI systems can recognize the patterns and signatures unique to Wireguard's protocol, even if the traffic is encrypted. This allows them to identify and block Wireguard connections. That is when I found ShadowSocks.

Solution

Shadowsocksarrow-up-right is a secure socks5 proxy designed to protect your internet traffic. It was initially created by a Chinese developer to circumvent the Great Firewall of China, which imposes strict censorship on internet access. Shadowsocks works by setting up a proxy server. When you connect to the internet through this proxy, your internet traffic is encrypted and sent to the proxy server, which then forwards the traffic to its final destination. Shadowsocks is designed to make its traffic look like regular HTTPS traffic, which helps in evading DPI systems that block or throttle VPN traffic. A bonus is that Shadowsocks is less resource-intensive compared to traditional VPNs, resulting in faster performance and lower latency.

Configuring ShadowSocks server

First I created another droplet server on Digital Ocean with Ubuntu 24.04 LTS as the OS and with the data center nearest to my country

Creating a VPS on Digital Ocean

Next, I decided to go with lightest possible ShadowSocks implementation available which is the ShadowSocks-libevarrow-up-right written in C#. There were few drawbacks like not having Workers - which handles multiple high traffic connections efficiently and also does not have Graceful Restart - which allows connections to be maintained even if the service was restarted.

My use case was for my family who does mostly browsing and social media so the traffic load wouldn't be high and a simple reconnection won't cause any significant issues for family use. A full table of the different implementations of ShadowSocks and its clients can be found herearrow-up-right.

After setting up the server on Digital Ocean, I ssh into it. Then I installed the shadowsocks-libev:

Confirm to complete the installation. Next is to setup the password and ports. I use nano as my editor for simplicity but anything else is also fine.

This will open up config.json file similar to this:

Change the server port to a less known port. Put a strong and secure password.

Method is the encryption method used for the connection. I have tried many different methods and some did not work well with the android clients or are not supported at all. Some desktop clients also do not support all available encryption. The ones that works best for me is "chacha20-ietf-poly1305" and "aes-256-gcm" which is also the recommended one but feel free to try out all encryptions. You can find the link to all types of encryption herearrow-up-right. After that, save your config.json file.

Now all you need to do is start the service:

After that check if its running:

This should return something like this:

Setting up client

When I used Wireguard, wg-easy had a convenient interface allowing for me to easily generate QR codes. This was a bonus for me as my parents are not tech-savvy at all and typing the IP address and port number with passwords would greatly confuse them. However, this feature is not available in the shadowsocks-libev. However, I found a way to generate one through the client.

First, I downloaded ShadowSocks for Windowsarrow-up-right and ran the application. A grey icon resembling a paper-plane should appear in your system tray.

  1. Right click on the icon

  2. Go to servers

  3. Click on Edit Servers

  4. Select and delete the sample server that was given

  5. Click add

  6. Fill the ip address and password (in config.json) for your server

  7. Next select the encryption that you chose

  8. Click ok

  9. Now go back to ShadowSocks icon in the system tray, right click on it

  10. Go to System Proxy

  11. Choose Global

  12. This should now enable and redirect all your system traffic through the proxy

Check and make sure all the website and connections are working. I ran a few speedtests as well to see how much load my server was accumulation. It was hitting barely 15% with two simultaneous speedtest (around 250 Mbps). If you are satisfied with the tests its now time to generate QR code.

  1. Go to the system tray

  2. Right click on the ShadowSocks icon

  3. Select Edit Servers

  4. Click on Share Server Config

  5. Now a QR code should pop up in the menu

  6. Make sure you choose the right server and take a screenshot of the QR

  7. Now it can be shared to anyone and imported easily

  8. (Optional) but you can also share the server url instead it works on most Windows and Android devices.

For Multiple users and passwords

I only use one password and user for my family so multiple passwords and ports are not necessary but there is a way to do so. All you have to do is duplicate the config.json file and create another service. Shadowsocks is written in a way that there's very little memory overhead even if multiple processes are run.

After writing another config.json file lets say config1.json

Make sure your port and local port are different for each config files. Then run this command:

  • ss-server is the shadowsocks-libev service

  • -c config1.json is the path to the .json file

  • -f /var/run/ss-server1.pid is used to the pid (process number) of this specific instance. This is useful if you want to stop this specific instance for any reason

If you want to kill a specific shadowsocks instance all you have to do is run:

A side note: I had this error on the Journalctl

Too many open files

This means the amount of file descriptors available for this shadowsocks process was exceeded. This made to enter a loop where it continuously opens and closes each file descriptor trying to read the network packages resulting 100% CPU usage. To fix this I simply increased the amount file descriptors available for the shadowsocks process and CPU usage went down.

For this code to work, I needed to stop and restart all my shadowsocks-libev process. This was made easy as I can just read the files from /var/run:

Then I just do htop to see if there's any lingering shadowsocks processes left before restarting each one

Generating QR Codes

If you have multiple config.json files, it gets a bit tedious to generate QR codes for each of them. I wrote bash script that would allow me to instantly generate QR codes for all the config.json files which can be used to scan and connect directly.

The prerequisites to run this is to have qrencode and jq libraries installed.

After installing create a text file with your favorite text editor and paste this script.

Make sure the script is in the same folder where you store your config files. Add your server ip between the quotation marks for server_ip variable. You can change were the qr_cdoes are stored by specifying a different folder in OUTPUT_DIR

Then make the script file executable

Now you can run it to generate all QR codes at once or by specifying a config

Last updated