2016年7月26日星期二

用 tun2socks 配置全局 SOCKS5 代理转发

笔者要在安装了 ArchLinuxARM 的 Raspberry Pi 上配置 SOCKS5 代理转发,并把局域网内其它计算机的网关、DNS 服务器设置成 Raspberry Pi,做到全局 SOCKS5 代理转发。
ArchLinuxARM 的系统控制服务是 Systemd。如果你不用 Systemd,可能需要调整某些步骤。
在开始之前,安装这些软件包:
badvpn-tun2socks iproute2 iptables pdnsd
首先配置 pdnsd 的 DNS 转发:
/etc/pdnsd.conf
 
global {
    max_ttl=300;
    min_ttl=0;
    neg_domain_pol=off;
    query_method=tcp_only;
    run_as=pdnsd;
    timeout=10;
}
 
server {
    ip=8.8.8.8,8.8.4.4;
}
然后写启动和终止脚本:
/usr/local/bin/socksfwd
 
#!/bin/bash
 
SOCKS_SERVER=xxx.xxx.xxx.xxx # SOCKS 服务器的 IP 地址
SOCKS_PORT=1080 # SOCKS 服务器的端口
GATEWAY_IP=192.168.1.1 # 家用网关(路由器)的 IP 地址
BYPASS_IPS=(yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz) # 不需要代理的 IP 地址
TUN_NETWORK_DEV=tun0 # 选一个不冲突的 tun 设备号
TUN_NETWORK_PREFIX=10.210.0 # 选一个不冲突的内网 IP 段的前缀
 
start_fwd() {
    ip tuntap add dev "$TUN_NETWORK_DEV" mode tun
    ip addr change "$TUN_NETWORK_PREFIX.1/30" dev "$TUN_NETWORK_DEV"
    ip link set "$TUN_NETWORK_DEV" up
    ip route del default via "$GATEWAY_IP"
    ip route add default via "$GATEWAY_IP" metric 600
    for i in "${BYPASS_IPS[@]}"
    do
        ip route add "$i" via "$GATEWAY_IP" metric 5
    done
    ip route add default via "$TUN_NETWORK_PREFIX.2" metric 6
    badvpn-tun2socks --tundev "$TUN_NETWORK_DEV" --netif-ipaddr "$TUN_NETWORK_PREFIX.2" --netif-netmask 255.255.255.252 --socks-server-addr "$SOCKS_SERVER:$SOCKS_PORT" &
    TUN2SOCKS_PID="$!"
}
 
stop_fwd() {
    ip route del default via "$TUN_NETWORK_PREFIX.2" metric 6
    for i in "${BYPASS_IPS[@]}"
    do
        ip route del "$i" via "$GATEWAY_IP" metric 5
    done
    ip tuntap del dev "$TUN_NETWORK_DEV" mode tun
}
 
start_fwd
trap stop_fwd INT TERM
wait "$TUN2SOCKS_PID"
写 Systemd 服务:
/etc/systemd/system/socksfwd.service
 
[Unit]
Description=Transparent SOCKS5 forwarding
After=network-online.target
 
[Service]
Type=simple
ExecStart=/usr/local/bin/socksfwd
LimitNOFILE=1048576
 
[Install]
WantedBy=multi-user.target
打开流量转发:
/etc/sysctl.d/sysctl.conf
 
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
net.ipv4.tcp_congestion_control=westwood
net.ipv4.tcp_syn_retries = 5
net.ipv4.tcp_synack_retries = 5
/etc/iptables/iptables.rules
 
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [1:1500]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING ! -s 127.0.0.0/8 -j MASQUERADE
COMMIT
最后设置 DNS 为本机 pdnsd。如果用 systemd-network,可以这么写,其中 eth0 改成对外的网卡名称:
/etc/systemd/network/eth0.network
 
[Match]
Name=eth0
 
[Network]
DHCP=yes
DNS=127.0.0.1
IPForward=yes
 
[DHCP]
UseDNS=no
 
[Route]
Metric=100
如果不用 systemd-network,也可以改 /etc/resolv.conf
/etc/resolv.conf
 
nameserver 127.0.0.1
最后启动服务并重启:
sudo systemctl enable socksfwd iptables
systemctl reboot

P.S. 这个方案也可以在桌面 Linux 或者 ChromeOS 上运行,只需要安装依赖并设法运行 socksfwd 脚本即可,其它步骤可以不做。
可以不使用 pdnsd 来转发 DNS,但是 DNS 服务器就需要加到 BYPASS_IPS 中。
在 ChromeOS 平台,创建的 tun 设备会被 shill 服务自动删掉,需要在执行 socksfwd 之前执行:
stop shill
start shill BLACKLISTED_DEVICES=tun0