WireGuard + Shadowsocks

WireGuard is a popular new VPN protocol. A known limitation of WireGuard is that it is vulnerable to deep packet inspection. Combining WireGuard with Shadowsocks obfuscates the WireGuard protocol.

In this tutorial, WireGuard and Shadowsocks are implemented on an Ubuntu Linux server and an Ubuntu Linux client. The client is assumed to be at IP address 11.11.11.11, and the server is assumed to be at IP address 22.22.22.22. We will use port 1433 for Shadowsocks and port 51820 for WireGuard.

We assume that you SSH into the server as root but use a non-root username with sudo permissions on the client.

1. Server

1.1. Update Server

Before you do anything else, get your server completely up to date:

apt update && apt upgrade -y

1.2. Configure Firewall

We are going to use port 1433 for the Shadowsocks server. Although we use port 51820 for the WireGuard server, this port can be kept closed to the public, since that will better camouflage your server.

Here are some sample iptables rules for IPv4. In this example, SSH is confined to a single IP address, which is 11.11.11.11 in the example. Replace 11.11.11.11 by the actual IP address of your workstation.

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -s 11.11.11.11/32 -j ACCEPT
iptables -A INPUT -p udp --dport 1433 -j ACCEPT
iptables -P INPUT DROP

Here are some sample rules for IPv6. Note that in our example, we assume you always use IPv4 for SSH, so the firewall is not open for SSH input over IPv6:

ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -p icmpv6 -j ACCEPT
ip6tables -A INPUT -p udp --dport 1433 -j ACCEPT
ip6tables -P INPUT DROP

To persist these rules across reboots, install the package iptables-persistent:

apt install iptables-persistent -y

1.3. Install WireGuard

Install WireGuard using the script and instructions from https://github.com/angristan/wireguard-install:

apt install curl -y
curl -O https://raw.githubusercontent.com/angristan/wireguard-install/master/wireguard-install.sh
chmod +x wireguard-install.sh
./wireguard-install.sh

You can accept the defaults all the way through the script’s questions, except specify the server’s WireGuard port as 51820.

When prompted for the name of the first client, call it wgss. This causes the script to create a client configuration file named wg0-client-wgss.conf.

The server configuration is placed in /etc/wireguard/wg0.conf. You can check the status of the systemd service with the command:

systemctl status wg-quick@wg0

It should read active (exited).

Press the q key on your computer keyboard to quit the status display.

1.4. Install Shadowsocks

Install shadowsocks-libev from the repositories:

sudo apt install shadowsocks-libev

1.5. Generate Password for Shadowsocks

Generate a 192-bit password, expressed as 32 base-64 characters, with the command:

openssl rand -base64 24

We will use the example:

C3WOo2qwJCJaiJpe9DY4pGBUC/WGMDxx

In what follows, replace the example with your own generated password.

1.6. Configure Shadowsocks

Edit your Shadowsocks server configuration file:

vi /etc/shadowsocks-libev/config.json

Delete the existing contents, and insert contents as follows:

{
  "server": "0.0.0.0",
  "server_port": 1433,
  "password": "C3WOo2qwJCJaiJpe9DY4pGBUC/WGMDxx",
  "timeout": 300,
  "method": "chacha20-ietf-poly1305",
  "mode": "udp_only"
}

Replace C3WOo2qwJCJaiJpe9DY4pGBUC/WGMDxx by your actual password.

Write the file to disk. Quit the editor.

1.7. Run Shadowsocks

The Shadowsocks systemd service is already running. Restart the service with your new configuration file:

systemctl restart shadowsocks-libev

Exit your SSH session with the server:

exit

2. Client

2.1. Update Client

Before you do anything else, get your client completely up to date:

sudo apt update && sudo apt upgrade -y

2.2. Install Shadowsocks

Install shadowsocks-libev from the repositories:

sudo apt install shadowsocks-libev

2.3. Configure Shadowsocks

Edit your Shadowsocks client configuration file:

sudo vi /etc/shadowsocks-libev/config.json

Delete the contents, and insert new contents as follows:

{
  "server": "22.22.22.22",
  "server_port": 1433,
  "local_address": "127.0.0.1",
  "local_port": 1080,
  "password": "C3WOo2qwJCJaiJpe9DY4pGBUC/WGMDxx",
  "timeout": 300,
  "method": "chacha20-ietf-poly1305",
  "mode": "udp_only",
  "tunnel_address": "127.0.0.1:51820"
}

Replace 22.22.22.22 by the actual IP address of your server. Replace C3WOo2qwJCJaiJpe9DY4pGBUC/WGMDxx by your actual password.

Write the file to disk. Quit the editor.

Edit the systemd service file:

sudo vi /usr/lib/systemd/system/shadowsocks-libev.service

Change the binary from ss-server to ss-tunnel:

ExecStart=/usr/bin/ss-tunnel -c $CONFFILE $DAEMON_ARGS

Write the file to disk. Quit the editor.

Reload systemd:

sudo systemctl daemon-reload

2.4. Run Shadowsocks

Shadowsocks systemd service is already running. Restart it with your new service file and configuration file:

sudo systemctl restart shadowsocks-libev

2.5. Install WireGuard

Install WireGuard on the client:

sudo apt install wireguard resolvconf -y

2.6. Configure WireGuard

Download the WireGuard client configuration file from the server.

cd ~/Downloads
scp root@22.22.22.22:wg0-client-wgss.conf .

Replace 22.22.22.22 in the above command by your actual server IP address.

Copy the configuration file into place:

sudo cp wg0-client-wgss.conf /etc/wireguard/wg0.conf

Edit the WireGuard client configuration file:

sudo vi /etc/wireguard/wg0.conf

Shadowsocks will take care of forwarding packets to the real server IP address. Therefore change the endpoint of the peer to be localhost port 1080, which is where Shadowsocks expects its input.

Here is a sample client configuration file after changes:

[Interface]
PrivateKey = kNBDeCk8Nz4ir2qsFd88QthH8CyCPOqS6x1z596MNGA=
Address = 10.66.66.2/32,fd42:42:42::2/128
DNS = 94.140.14.14,94.140.15.15

[Peer]
PublicKey = q+z9MX5ii/YaSivcFim4mXA1zPpopPTSt7YEAyK6s1o=
PresharedKey = Sj+DR9NR0FbXOFC8ht9sITdnEzSEhrjvts0ljZarEtA=
Endpoint = 127.0.0.1:1080
AllowedIPs = 0.0.0.0/0,::/0

If your client does not support IPv6, remove the IPv6 addresses (fd42:42:42::2/128 and ::/0).

Write the file to disk. Quit the editor.

2.7 Add Static Route

Add a static route from the client to the server, so that it does not try to send WireGuard traffic via Shadowsocks, then Shadowsocks via WireGuard, in a continuous and never-ending loop. For example, if your default gateway is 192.168.1.1 and your interface is wlp3s0:

sudo ip route add 22.22.22.22 via 192.168.1.1 dev wlp3s0

Replace 22.22.22.22 in the above by your actual server IP address. Replace 192.168.1.1 and wlp3s0 by your actual default gateway. If you do not know your default gateway already, you can obtain it by issuing the command ip r.

2.8. Run WireGuard

sudo wg-quick up wg0

You can test your connection by visiting https://whatismyipaddress.com in Firefox.

2.9. Disconnect

Stop WireGuard:

sudo wg-quick down wg0

Stop Shadowsocks:

sudo systemctl stop shadowsocks-libev

Delete the static route:

sudo ip route del 22.22.22.22 via 192.168.1.1 dev wlp3s0

Replace 22.22.22.22 in the above by your actual server IP address. Replace 192.168.1.1 and wlp3s0 by your actual default gateway.

3. Get Help and Report Issues

For WireGuard, the best place to get help is the #wireguard IRC channel on libera.chat.

For Shadowsocks issues, see the GitHub issues page.

Updated 2022-02-08