Personal Proxy IPv4 Input and IPv6 Output

This is the opposite of the previous post. In this scenario, we want to send a proxy server input over IPv4, but we want the output to the web to use IPv6 whenever possible. Obviously, you must have a dual-stack VPS for this to work.

We'll use Shadowsocks-Libev as the proxy software, and as in the previous post we use Socat for data transfer between IPv4 and IPv6. Our server runs Ubuntu 20.04, and we are logged in as root. Because of the way Shadowsocks works, you don't need a domain name or DNS records to get this scenario to work.

The test server for this tutorial was virtualized by KVM and had 1 GB of RAM. The free -m command indicated that 256 MB of RAM would be sufficient. The process was not tested with OpenVZ/Virtuozzo.

Before you begin, check that your server really does have outbound IPv6 connectivity. SSH into the server and ping Google's IPv6-only address:

ping6 ipv6.google.com

The Linux ping command continues running until you explicitly press Ctrl+c on your keyboard. This is in contrast to the Windows ping command, which automatically stops after four iterations.

You should get results back showing the number of milliseconds to return a response from the Google's IPv6-only server.

In the examples given in this post:

  • Your PC is on IPv4 address XX.XX.XX.XX
  • Your server is on IPv4 address YY.YY.YY.YY

Install and Configure Firewall

We'll use nftables for our firewall. Install and start the nftables service:

apt update && apt upgrade -y

apt install nftables -y

systemctl enable nftables

systemctl start nftables

Edit the nftables configuration file:

vi /etc/nftables.conf

For finer-grained control and counting, we'll implement separate IPv4 and IPv6 firewall rules. In the scenario in this post, you want SSH and Shadowsocks-Libev to receive their input over IPv4.

  • Replace XX.XX.XX.XX in the template below with your PC's actual IPv4 address.
  • Change port 8388 to your actual choice of port for Shadowsocks-Libev IPv4 input.
table ip filter {
        chain input {
                type filter hook input priority filter; policy accept;
                ct state { established, related } counter packets 0 bytes 0 accept
                iif "lo" counter packets 0 bytes 0 accept
                ip protocol icmp counter packets 0 bytes 0 accept
                tcp dport 22 ip saddr XX.XX.XX.XX counter packets 0 bytes 0 accept
                tcp dport 8388 counter packets 0 bytes 0 accept
                counter packets 0 bytes 0 drop
        }

        chain forward {
                type filter hook forward priority filter; policy accept;
        }

        chain output {
                type filter hook output priority filter; policy accept;
                tcp dport 80 counter packets 0 bytes 0 accept
                tcp dport 443 counter packets 0 bytes 0 accept
        }
}

table ip6 filter {
        chain input {
                type filter hook input priority filter; policy accept;
                ct state { established, related } counter packets 0 bytes 0 accept
                iif "lo" counter packets 0 bytes 0 accept
                ip6 nexthdr ipv6-icmp counter packets 0 bytes 0 accept
                counter packets 0 bytes 0 drop
        }

        chain forward {
                type filter hook forward priority filter; policy accept;
        }

        chain output {
                type filter hook output priority filter; policy accept;
                tcp dport 80 counter packets 0 bytes 0 accept
                tcp dport 443 counter packets 0 bytes 0 accept
        }
}

Save the nftables configuration file. Restart nftables with your revised configuration:

systemctl restart nftables

Install and Configure Socat

Socat is a Linux command-line utility that establishes two bidirectional byte streams and transfers data between them. We'll use Socat to convert IPv4 input on port 8388 to IPv6. Install the package:

apt install socat -y

Create the Socat systemd service file:

vi /lib/systemd/system/socat.service

Change port 8388 in the template below to your actual choice of port on which Shadowsocks-Libev will receive input:

[Unit]
Description=Socat

[Service]
User=root
ExecStart=/usr/bin/socat TCP4-LISTEN:8388,reuseaddr,fork TCP6:[::1]:8388

[Install]
WantedBy=multi-user.target

Save the Socat systemd service file. Start the service:

systemctl enable socat

systemctl start socat

Install and Configure Shadowsocks-Libev

Install Shadowsocks-Libev from the Ubuntu repositories. We'll also install the Haveged random number generator. This remedies low-entropy conditions in Linux's /dev/random, thus improving the quality of encryption.

apt install haveged shadowsocks-libev -y

Edit the Shadowsocks-Libev configuration file:

vi /etc/shadowsocks-libev/config.json

  • Notice that Shadowsocks-Libev listens only on IPv6 localhost.
  • Change port 8388 in the template below to your actual choice of port for Shadowsocks-Libev input.
  • Change the sample password to something stronger.
  • Make sure you use an AEAD encryption method, as listed at shadowsocks.org. The AES ciphers work best on devices with hardware AES acceleration. Therefore we choose chacha20-ietf-poly1305 in this example for maximum flexibility.
{
    "server": "::1",
    "server_port":8388,
    "local_port":1080,
    "password":"pass1234",
    "timeout":300,
    "method":"chacha20-ietf-poly1305",
    "ipv6_first": true
}

Save the Shadowsocks-Libev configuration file. When you've saved the file, restart the service:

systemctl restart shadowsocks-libev

Check Shadowsocks-Libev

Here are some ways you can check that Shadowsocks-Libev is running and listening as expected:

systemctl status shadowsocks-libev

journalctl -u shadowsocks-libev

apt install net-tools

netstat -tulpn

That last command should show:

  • Socat is listening on all IPv4 interfaces port 8388.
  • Shadowsocks-Libev is listening on ::1 IPv6 port 8388.

Install and Configure Client

We will use Windows as our example client. (Shadowocks clients also exist for macOS, Linux, Android, iOS, and OpenWRT.) Download the Windows client from GitHub. Unzip the downloaded zip file. Launch Shadowsocks.exe.

On the Server screen of the client, specify your server parameters, e.g.:

  • Server IP = YY.YY.YY.YY
  • Server Port = 8388
  • Password = pass1234
  • Encryption = chacha20-ietf-poly1305

Now configure your browser to use the SOCKS5 proxy listening on 127.0.0.1 port 1080. On Chrome, you can do this with the SwitchyOmega extension. On Firefox you can do this natively under Options > General > Network Settings > Settings.

Check Functionality

Visit a website known to have IPv4 and IPv6 address, e.g. https://www.google.com. It should display in your browser.

You can see that IPv6 is working at least some of the time by going to your server and checking the IPv6 output packet counts:

nft list ruleset

The IPv6 packet counts for HTTP/HTTPS should be non-zero.

For more conclusive evidence, capture 100 packets of HTTP or HTTPS:

tcpdump -i any -c100 -w web.pcap "dst port 80 or dst port 443"

Again, on your client, visit the website known to have both IPv4 and IPv6 connectivity such as https://www.google.com. A single visit should reach the 100-packet limit, so there is no need to explicitly stop tcpdump.

Display the results of the tcpdump capture:

tcpdump -nn -r web.pcap

If everything is working correctly, you should see IPv6 IP addresses.

Quit Proxy Session

When you're done browsing, quit your client's proxy session:

  • Revert your browser back to its original network settings, so that it no longer expects a proxy server on port 1080.
  • In the system tray, find the Shadowsocks paper airplane icon, right-click on it, and select Quit.

Comments

Sign In or Register to comment.