NaiveProxy + Caddy 2

NaiveProxy is an anticensorship tool that uses Chrome’s network stack to better camouflage your traffic. Recent versions of NaiveProxy (we are currently on NaiveProxy 91) are implemented as customized builds of Caddy 2’s ForwardProxy plugin. Caddy 2 is a major revision of Caddy 1.

Prebuilt clients for NaiveProxy are available for Android, Linux, OpenWRT, macOS, and Windows.

In this article, you’ll learn how to install and configure NaiveProxy and Caddy 2 on a Debian or Ubuntu server and client. The procedure has been tested with a server running Ubuntu 21.04.

1. Set Up Server

1.1. Get Domain Name and Server

Before you set up your NaiveProxy server, you’ll need to acquire a domain name. If you do not already have a domain name registrar, you can check out Freenom (free) or Porkbun (paid) or any other registrar of your choosing.

You’ll also need a virtual private server (VPS). If you do not already have a VPS provider, you can check out Linode or Google or any other provider of your choice.

At your domain name registrar, create DNS type A records pointing from the naked domain and the www subdomain to the public IP address of your VPS. You’ll need to allow some time for the DNS addition to propagate. On a PC, you can check to see if the propagation has happened by looking up your domain and subdomain. For example, if your domain name is example.com, then open a command prompt and issue the commands:

nslookup example.com
nslookup www.example.com

1.2. Install and Configure Firewall

There are multiple ways to implement a firewall on a Debian/Ubuntu server: nftables, iptables, ufw, and firewalld. The modern way is nftables, and that is what we will use here.

SSH into your server as root. Issue each of the following commands in turn to install and start nftables:

apt update && apt upgrade -y
apt install nftables -y
systemctl enable nftables
systemctl start nftables

Configure the firewall to accept related traffic, internal traffic, and ping requests:

nft add rule inet filter input ct state related,established counter accept
nft add rule inet filter input iif lo counter accept
nft add rule inet filter input ip protocol icmp icmp type echo-request counter accept
nft add rule inet filter input ip6 nexthdr icmpv6 icmpv6 type echo-request counter accept

Open port 22 for SSH. (If you can restrict the port 22 rule so that only certain source IP addresses are whitelisted for SSH access, then so much the better.)

nft add rule inet filter input tcp dport 22 counter accept

Open ports 80 and 443 for HTTP and HTTPS requests, respectively:

nft add rule inet filter input tcp dport { http, https } counter accept

Drop all unexpected input:

nft add rule inet filter input counter drop

Save the rules:

nft list ruleset > /etc/nftables.conf

1.3. Implement BBR

Bottleneck Bandwidth and Round-trip propagation time (BBR) is a TCP congestion control algorithm developed at Google. Under certain types of network congestion, it will improve your latency. Implement BBR TCP congestion control on your server with the following commands:

cat >> /etc/sysctl.d/50-bbr.conf <<EOF
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF
sysctl -p /etc/sysctl.d/50-bbr.conf

1.4. Create Camouflage Website

So that your server can pass a manual inspection, install a few pages of web content. Make a directory for the web pages:

mkdir -p /var/www/html

Install the prerequisite utilities:

apt install wget zip unzip -y

Get some sample web pages from GitHub:

wget https://github.com/arcdetri/sample-blog/archive/master.zip

Unzip the downloaded sample web pages:

unzip master.zip

Copy the web pages into place in your directory for web pages:

cp -rf sample-blog-master/html/* /var/www/html/

1.5. Install Go Language

On your PC, open Firefox and visit golang.org/dl. Determine the most recent version of Go for 64-bit Linux. At the time of writing, this is named go1.16.5.linux-amd64.tar.gz.

In your SSH session with your server, download the archive for the Go language:

wget https://golang.org/dl/go1.16.5.linux-amd64.tar.gz

Extract the archive into /usr/local:

tar -C /usr/local -xzf go1.16.5.linux-amd64.tar.gz

Add /usr/local/go/bin to your executable PATH by editing the system-wide profile:.

vi /etc/profile

Add a line at the end:

export PATH=$PATH:/usr/local/go/bin

Write the file to disk, and quit the editor. Log out and log in again:

exit
ssh root@www.example.com

Check that the Go language is installed by issuing the command:

go version

You should see a response that looks like this:

go version go1.16.5 linux/amd64

1.6. Get Source for ForwardProxy

Get the source code for the NaiveProxy version of Caddy’s ForwardProxy:

apt install git -y
git clone -b naive https://github.com/klzgrad/forwardproxy

1.7. Custom Build of Caddy 2

Download and install packages and dependencies for custom builds of the Caddy 2 web server:

go get -u github.com/caddyserver/xcaddy/cmd/xcaddy

Use xcaddy to build Caddy with the ForwardProxy plugin, as modified by the NaiveProxy project, which you just downloaded:

~/go/bin/xcaddy build --with github.com/caddyserver/forwardproxy=$PWD/forwardproxy

The download and build will take several minutes. The final lines read something like this:

2021/06/12 15:45:24 [INFO] exec (timeout=0s): /usr/local/go/bin/go build -o /root/caddy -ldflags -w -s -trimpath
2021/06/12 15:49:19 [INFO] Build complete: ./caddy
2021/06/12 15:49:19 [INFO] Cleaning up temporary folder: /tmp/buildenv_2021-06-12-1544.050638754

Once it has finished, copy the Caddy binary into a more central location:

cp caddy /usr/bin

1.8. Allow Caddy to Bind to Privileged Ports

Allow Caddy to bind to “privileged” ports (i.e. TCP/IP port numbers below 1024):

setcap cap_net_bind_service=+ep /usr/bin/caddy

1.9. Generate Strong Password

Generate a strong password to access your proxy server:

cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c32; echo;

The example result that we will use in the rest of this tutorial:

MFUZ4eMngSRJ9888tvM8S9HjEULm2Ptj

1.10. Configure Caddy with Forwardproxy

Edit the Caddy configuration JSON file:

mkdir /etc/caddy
vi /etc/caddy/config.json

Use the template below, making these changes:

{
  "admin": {"disabled": true},
  "logging": {
    "sink": {"writer": {"output": "discard"}},
    "logs": {"default": {"writer": {"output": "discard"}}}
  },
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [":443"],
          "routes": [{
            "handle": [{
              "handler": "forward_proxy",
              "hide_ip": true,
              "hide_via": true,
              "auth_user_deprecated": "yourname",
              "auth_pass_deprecated": "MFUZ4eMngSRJ9888tvM8S9HjEULm2Ptj",
              "probe_resistance": {"domain": ""}
            }]
          }, {
            "match": [{"host": ["example.com", "www.example.com"]}],
            "handle": [{
              "handler": "file_server",
              "root": "/var/www/html"
            }],
            "terminal": true
          }],
          "tls_connection_policies": [{
            "match": {"sni": ["example.com", "www.example.com"]}
          }]
        }
      }
    },
    "tls": {
      "automation": {
        "policies": [{
          "subjects": ["example.com", "www.example.com"],
          "issuer": {
            "email": "youremail@example.com",
            "module": "acme"
          }
        }]
      }
    }
  }
}

Write the file to disk, and quit the editor.

1.11. Create Systemd Service File

Create a systemd service file for Caddy:

vi /usr/lib/systemd/system/caddy.service

Insert contents like this:

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target

[Service]
ExecStart=/usr/bin/caddy run --config /etc/caddy/config.json

[Install]
WantedBy=multi-user.target

Write the file to disk, and quit the editor.

1.12. Run Caddy on Server

Start Caddy with the NaiveProxy version of ForwardProxy:

systemctl enable caddy
systemctl start caddy

Check that the status is active (running):

systemctl status caddy

Check that Caddy is listening on ports 80 and 443:

ss -tulpn | grep caddy

Exit your SSH session with the server:

exit

You can check that the camouflage website is in place by opening Firefox on your PC and visiting your host, e.g., https://www.example.com.

2. Set Up Client

2.1. Download NaiveProxy Client

Now go to work on your PC. Open your browser, and visit the releases page for NaiveProxy.

Download the latest release of the NaiveProxy client. We will use Windows as our example client. For example, at the time of writing the client for Windows is named naiveproxy-v91.0.4472.77-1-win-x64.zip.

Extract the zip file.

NaiveProxy files extracted from the zip file

2.2. Configure NaiveProxy Client

Edit the configuration file for the NaiveProxy client, which is named config.json:

Use the template below, making these changes:

{
"listen": "socks://127.0.0.1:1080",
"proxy": "https://yourname:MFUZ4eMngSRJ9888tvM8S9HjEULm2Ptj@www.example.com"
}

Save the file.

2.3. Run NaiveProxy

In a Command Prompt window on the client, run the NaiveProxy client with your configuration file:

cd Downloads\naiveproxy-v91.0.4472.77-1-win-x64\naiveproxy-v91.0.4472.77-1-win-x64
naive config.json

Leave the Command Prompt window open, with the NaiveProxy client still running in it. NaiveProxy is listening on port 1080.

2.4. Configure Firefox

Open Firefox. From the hamburger menu, select Settings > General. Scroll down to Network Settings. Click the Settings button.

2.5. End-to-end Test

Visit IP Chicken.

You should see the IP address of your remote server, not your local client.

3. Get Help and Report Issues

Report any NaiveProxy issues on GitHub.

Updated 2021-06-12