How to deploy a socks5 proxy service on openwrt and how to use socks5 proxy in Linux server

Background - Why I need this

I have a friend, also my business partner living in China. His Claude account is banned due to usage of dirty IP. So I want to share my OpenWRT router as exit node for him to use. We have a Linux server in Finland, so I will use it as a middle node to forward traffic to the OpenWRT router. I only have a IPV4 address on the OpenWRT router, so this tutorial is based on IPV4 only.

Why socks5 proxy

There are various ways to run as proxy, like socks, shadowsocks, v2ray, etc. At first I plan to use shadowsocks. But I changed my mind later. It is not necessary to encrypt traffic between the Linux server and OpenWRT router since there is no GFW in Europe. Without encryption, socks5 proxy has better performance. Compared to http proxy, socks5 proxy can handle more types of traffic, not just http. So I choose socks5 proxy. I use hev-socks5-server on OpenWRT router because it is well maintained and has good performance. hev-socks5-server even is available in OpenWRT’s official package repository, so it is very easy to install and run. In addition, I use whitelist rules in firewall of OpenWRT router for better security.

Why sing box on Linux server as a socks5 proxy client

There are various socks5 proxy client software, like dante, 3proxy, etc. Also you can use socks5 directly as environment variable in Linux. But I choose sing box. It is a simple and famous proxy client software. And it is actively maintained open source project. My purpose is only proxy traffic of claude, which can be easily achieved by sing box’s routing rules. So I choose sing box.

How to run socks5 proxy on OpenWRT

Log into your openwrt as root

1
2
3
opkg update
opkg install hev-socks5-server
vim /etc/hev-socks5-server/main.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
main:
workers: 4
port: 12345 # use a random port instead of default one is better for security
listen-address: '::' # listen on all ipv4 and ipv6 address though I only have ipv4 address, it is better for future proof
listen-ipv6-only: false
domain-address-type: unspec

auth:
username: your_username
password: your_password

misc:
log-file: /var/log/hev-socks5-server.log
log-level: warn
pid-file: /var/run/hev-socks5-server.pid

Append new rules in firewall in the end of config file.

1
vim /etc/config/firewall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
config rule
option name 'Allow-SOCKS5-MyServer'
option src 'wan'
option dest_port '12345' # replace with the port you set in main.yml
option proto 'tcp'
option src_ip 'your_linux_server_ip' # only allow your linux server to access socks5 proxy for better security
option target 'ACCEPT'

config rule
option name 'Deny-SOCKS5-All'
option src 'wan'
option dest_port '12345' # replace with the port you set in main.yml
option proto 'tcp'
option target 'DROP'
1
vim /etc/config/hev-socks5-server # update UCI config file to enable and start socks5 server
1
2
3
config hev-socks5-server 'config'
option enabled '1'
option conffile '/etc/hev-socks5-server/main.yml'
1
2
3
4
5
6
7
8
/etc/init.d/hev-socks5-server enable
/etc/init.d/hev-socks5-server start
# Check if socks5 server is running
ps | grep hev-socks5
netstat -tlnp | grep 12345 # replace 12345 with the port you set in main.yml
# if you updated auth credentials, commands below can help you update cache to make new credentials take effect immediately
killall hev-socks5-server
/etc/init.d/hev-socks5-server start

How to use socks5 proxy in Linux server

Log into your Linux server as root

1
2
3
# Quick install sing-box (recommended)
curl -fsSL https://sing-box.app/deb-install.sh | sudo bash
vim /etc/sing-box/config.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
{
"log": {
"level": "info"
},
"dns": {
"servers": [
{
"tag": "google-dns",
"type": "tls",
"server": "8.8.8.8"
}
],
"strategy": "ipv4_only"
},
"inbounds": [
{
"type": "tun",
"tag": "tun-in",
"address": ["172.19.0.1/30"],
"auto_route": true,
"auto_redirect": true,
"strict_route": false,
"route_exclude_address": [
"10.42.0.0/16",
"10.43.0.0/16",
"192.168.0.0/16",
"8.8.8.8/32"
],
"exclude_interface": [
"cni0",
"flannel.1"
],
"stack": "system"
}
],
"outbounds": [
{
"type": "direct",
"tag": "direct"
},
{
"type": "socks",
"tag": "openwrt-proxy",
"server": "your_openwrt_router_ip", # replace with your openwrt router ip
"server_port": 12345, # replace with the port you set in main.yml
"version": "5",
"username": "your_username", # replace with the username you set in main.yml
"password": "your_password" # replace with the password you set in main.yml
}
],
"route": {
"auto_detect_interface": true,
"rules": [
{
"action": "sniff"
},
{
"protocol": "dns",
"action": "hijack-dns"
},
{
"ip_cidr": ["10.42.0.0/16", "10.43.0.0/16", "8.8.8.8/32"],
"action": "route",
"outbound": "direct"
},
{
"domain": ["domain_of_openwrt_if_you_use_DDNS"],
"action": "route",
"outbound": "direct"
},
{
"domain_suffix": ["claude.ai"],
"action": "route",
"outbound": "openwrt-proxy"
},
{
"domain_keyword": ["anthropic"],
"action": "route",
"outbound": "openwrt-proxy"
},
{
"domain_suffix": ["ifconfig.me"], # For testing if traffic is routed to socks5 proxy successfully
"action": "route",
"outbound": "openwrt-proxy"
}
],
"final": "direct"
}
}

There are two ways to use it.
Option A: Environment variables (per-app)
replace the inbounds section in config.json with below content

1
2
3
4
5
6
7
"inbounds": [
{
"type": "mixed",
"tag": "mixed-in",
"listen": "127.0.0.1",
"listen_port": 2080
}

Usage:

1
2
3
4
export http_proxy=http://127.0.0.1:2080
export https_proxy=http://127.0.0.1:2080
curl https://ifconfig.me # → proxy
curl https://google.com # → direct

Option B: TUN mode (system-wide, transparent)
This is how my tutorial is based on. With this mode, you don’t need to set environment variables. Sing-box will automatically route traffic based on the routing rules in config.json. So it is more convenient to use. But it is also more dangerous if you configure routing rules wrong, because it may cause loss of connection to your server. So be careful when you use this mode and make sure you have a way to rescue your server if something goes wrong.

Rule matching cheatsheet:

Matcher Example Matches
domain "www.claude.ai" "www.claude.ai" (exact full domain)
domain_suffix "claude.ai" "www.claude.ai", "claude.ai",
"api.claude.ai"
domain_keyword "claude" "www.claude.ai", "claude.ai",
"claude.com"
domain_regex ".*claude\\.ai$" "www.claude.ai", "api.claude.ai",
"claude.ai"
ip_cidr "192.168.1.0/24" Destination IP in 192.168.1.0
192.168.1.255
source_ip_cidr "10.0.0.0/8" Source IP in 10.0.0.0
10.255.255.255
port 443 Destination port 443
port_range "1000:2000" Destination port 10002000

For more details about rule matching, you can check sing-box’s official documentation: https://sing-box.sagernet.org/configuration/dns/rule/#network

1
2
3
4
5
6
7
# start sing-box
systemctl enable sing-box
systemctl start sing-box
# check status of sing-box
systemctl status sing-box
# test if socks5 proxy works
curl https://ifconfig.me # if the output is your openwrt router's public ip, then it means traffic is routed to socks5 proxy successfully

We need to exclude address “10.42.0.0/16”, “10.43.0.0/16”, “192.168.0.0/16”, exclude interface “cni0”, “flannel.1” in route rules because they are used by kubernetes cluster in my Linux server. You can check your kubernetes cluster network configuration and update exclude address and interface accordingly if you also have kubernetes cluster in your Linux server. If you don’t have kubernetes cluster, you can just ignore this part and don’t need to add any exclude address or interface. auto_route: true is important for running sing-box in a k3s node. It is possible to lost connection to the server if you configure routing rules wrong. So be prepared to rescue your server and never play it in production environment.

How to rescue your Linux server if you lost connection after running sing-box on Hetzer

Just in case you lost connection to your Linux server after running sing-box, you can follow the steps below to rescue your server. Why I know this, because I encountered this problem and I have to rescue my server by myself. So I want to share the solution here to help others who may encounter the same problem. Our server is on Hetzer, so we will use Hetzer’s rescue system to rescue our server. The steps are as follows:

  1. Log into your Hetzer account and go to the server management page.
  2. Click on the “Rescue” tab and then click on the “Activate Rescue System” button. Select your public SSH key and activate the rescue system. Or you can also use root password generated by Hetzer, but I prefer to use SSH key for better security.
  3. reboot your server by sending button press signal in Hetzer’s server Reset page. One is for shutdown and one more for booting. You cannnot reboot your server by command line because you have lost connection to your server. Then wait for rebooting and login as root.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # Check which disk is your system disk. In my case, I have a RAID1 disk, so I have /dev/md2
    lsblk
    cat /proc/mdstat
    # mount your system disk to /mnt. In my case, I need to mount /dev/md2
    mount /dev/md2 /mnt
    # You can either correct your sing-box config file or just disable sing-box service to make it stop running. I will share both methods here.
    # 1. Correct sing-box config
    vim /mnt/etc/sing-box/config.json
    # 2. Disable sing-box service
    rm /mnt/etc/systemd/system/sing-box.service
    # After that, poweroff and deactivate rescue system in Hetzer's Rescue page. Then boot your server normally by sending button press signal in Hetzer's server Reset page.