Friday, November 11, 2016

局域网下免设置路由全局透明代理

如果你不想光为了透明代理而单独买一个可刷OpenWRT固件的路由器, 同时你在局域网内有至少一台linux机器(可虚拟机),那以下内容可以帮助你在不需要设置路由的情况下实现透明代理(当然本质上就是把那台机器当成路由来用)。

首先你需要一台linux机器,ubuntu/debian/centos等均可,能编译ss-libev,使用iptables(于是不能centos7,我不会调教那个firewall)。你可以使用真实机器(如树莓派)或虚拟机。如果使用虚拟机,网络类型需要设置为桥接到物理网络,如果是VMware,不要对“复制物理网络连接状态”打钩。我建议使用虚拟机或性能不差的真实机器,这样网速不会受到性能上的限制。

接下来,安装操作系统、安装编译依赖包、git clone ss-libev、configure、make等这里就不说了,网上说明有很多(虽然大多数是过时了)。

编译好后,如果你clone的是ssr的libev版本,里面自带了一个ssrlink.py,可以通过此脚本快速生成一个客户端使用的配置文件,例如执行:

python ssrlink.py ssr://MTkyLjE2OC4xLjI6MTA4MDphdXRoX2FlczEyOF9tZDU6YWVzLTEyOC1jZmI6dGxzMS4yX3RpY2tldF9hdXRoOk1USXpORFUyLz9vYmZzcGFyYW09ZEdWemRDNWpiMjAmZ3JvdXA9ZEdWemRB > config.json
即可生成一个配置好了的config.json

东西都准备好了,现在开始配置。首先是开启ipv4转发,编辑/etc/sysctl.conf,解除net.ipv4.ip_forward = 1的注释,或直接添加这么一行,保存文件,并执行sysctl -p使修改立即生效。然后,设置IP为静态的,例如你的路由器地址为192.168.0.1,那么你可以设置为192.168.0.2,具体设置方法参考linux文档。设置好以后,在需要全局代理的机器上,把网关地址改为192.168.0.2,测试能不能正常上网(现在还没有通过代理),如果不能,那检查防火墙配置,或尝试重启它再测试。能正常上网后再进行后面的配置。



然后,创建一个ipt.sh文件,文件内容如下:

#!/bin/bash

subnet="192.168.0.0/16"           # your subnet config
localaddr="192.168.0.2"           # this server ip (must be subnet ip)
boardcastaddr="192.168.0.255"
listenport=1080                   # local listen port (tcp)
    udplistenport=0               # set it if not the same udp port
NAT_OUTPUT=0                      # if 0: do not need to set server ip
if [ $NAT_OUTPUT -eq 1 ]; then
    serverip="100.86.100.10"          # ss server ip (tcp)
    serverip_udp=""                   # ss server ip (for udp), keep empty if the same
fi

iptables -t nat -D PREROUTING -p tcp -j SHADOWSOCKS
if [ $NAT_OUTPUT -eq 1 ]; then
    iptables -t nat -D OUTPUT -p tcp -j SHADOWSOCKS
fi
iptables -t nat -F SHADOWSOCKS
iptables -t nat -X SHADOWSOCKS

iptables -t mangle -D PREROUTING -p udp -j SHADOWSOCKS
iptables -t mangle -F SHADOWSOCKS
iptables -t mangle -X SHADOWSOCKS

ip rule del fwmark 0x01/0x01 table 100
ip route del local 0.0.0.0/0 dev lo table 100


if [ $udplistenport -eq 0 ]; then
    udplistenport=`expr $listenport`
fi

iptables -t nat -N SHADOWSOCKS
iptables -t nat -I PREROUTING -p tcp -j SHADOWSOCKS
if [ $NAT_OUTPUT -eq 1 ]; then
    iptables -t nat -I OUTPUT -p tcp -j SHADOWSOCKS
fi

ip rule add fwmark 0x01/0x01 table 100
ip route add local 0.0.0.0/0 dev lo table 100
iptables -t mangle -N SHADOWSOCKS
iptables -t mangle -A SHADOWSOCKS -d ${boardcastaddr} -j RETURN
iptables -t mangle -A SHADOWSOCKS -d 255.255.255.255/32 -j RETURN
iptables -t mangle -A SHADOWSOCKS -p udp --dport 137 -j RETURN
iptables -t mangle -A SHADOWSOCKS -p udp --dport 138 -j RETURN
iptables -t mangle -A SHADOWSOCKS -p udp -d ${localaddr} -j RETURN
iptables -t mangle -A SHADOWSOCKS -p udp -s ${localaddr} -j RETURN
iptables -t mangle -A SHADOWSOCKS -p udp -s ${subnet} -j TPROXY --on-port ${udplistenport} --tproxy-mark 0x01/0x01
iptables -t mangle -I PREROUTING -p udp -j SHADOWSOCKS

if [ $NAT_OUTPUT -eq 0 ]; then
    iptables -t nat -A SHADOWSOCKS -d ${localaddr} -j RETURN
    iptables -t nat -A SHADOWSOCKS -s ${localaddr} -j RETURN
else
    iptables -t nat -A SHADOWSOCKS -d ${serverip} -j RETURN
    iptables -t nat -A SHADOWSOCKS -s ${serverip} -j RETURN
    if [ -z $serverip_udp ]; then
   
    iptables -t nat -A SHADOWSOCKS -d ${serverip} -j RETURN
   
    iptables -t nat -A SHADOWSOCKS -s ${serverip} -j RETURN
    fi
fi

iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d ${subnet} -j RETURN

iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-ports ${listenport}


注意你应该按你的本地设定修改前四行的配置,一般你也只需要修改前四行的配置项就行了,然后保存并执行。执行后,它可以重定向你所有的出网TCP和UDP包。

最后,运行ss-redir:./ss-redir -c config.json -u
此时便应该是全局,可以愉快地玩耍了

注意的一点:UDP包如果不能成功转发,可能是被GFW拦截,那么这时只能更换服务器了。而这可能导致DNS查询失败,可以先把DNS设置为路由的DNS地址以便于排查问题。

ipt.sh参数说明:
listenport 应该与你的ss配置文件所配置的local_port一致
localaddr 你的这台linux机器的静态IP地址

subnet 你的局域网网络地址段
boardcastaddr 你的局域网的广播地址(地址段中最大的地址,由网络掩码决定)

其它说明:如果你不需要全局代理,你可以在ipt.sh中模仿以下指令
iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN
在此后面添加你不需要代理的地址段即可

20 comments:

  1. 然而centos7能安装iptables再关掉friewalld

    ReplyDelete
  2. 貌似买支持OpenWrt的路由器更简单直接

    ReplyDelete
    Replies
    1. 不想花时间就花钱,不过有时候花钱也并非更快

      Delete
  3. 我直接虚拟机装openwrt,占用地方也不大=_-

    ReplyDelete
  4. 好妖的字体和背景,快被掰歪了。。。
    firewalld转发更简单
    firewall-cmd --permanent --add-masquerade
    firewall-cmd --permanent --add-forward-port=port=【本地端口】:proto=tcp:toport=【目标端口】:toaddr=【目标地址】
    firewall-cmd --reload
    udp同理。参考地址 https://www.rootusers.com/how-to-use-firewalld-rich-rules-and-zones-for-filtering-and-nat/

    另外想问下python版client有没有PAC 如何使用

    ReplyDelete
    Replies
    1. 谢谢啊,不过UDP同理?UDP有着和TCP不一样的地方啊。。。关于PAC你可以弄一个PAC生成服务器

      Delete
    2. udp具体的我也没有研究,但是在中转服务器上按tcp的方式写了。中转服务器上没有运行SSR,只对ssr端口进行转发到ssr服务器。PC C#客户端中服务器ip填中转服务器ip。测试udp tcp均正常使用。

      Delete
    3. 哦 我说的不是局域网的设置。我中转是直接用的--add-forward-port。文中的局域网规则可能要用到rich-rules。还没研究过

      Delete
  5. This comment has been removed by the author.

    ReplyDelete
  6. 既然linux可以实现那么用虚拟机装openwrt应该也能实现吧 虽然我没有成功过

    ReplyDelete
  7. 我也觉得这字体有毒,尤其是命令相关用这种字体....

    ReplyDelete
  8. 问一个不太相关的问题,我的路由器安装了Python但不支持多线程(提示can't start new thread),该怎么办?

    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete
  10. 折腾了许久,我还是卡在了ss-redir上面,提示[udp] unable to get dest addr错误。
    这个应该代表udp数据是到了监听端口上了。
    而且我用ss-local测试过,服务器是支持udp转发的。
    能不能给点提示,到底问题出在哪里?

    ReplyDelete
    Replies
    1. 问题解决了,换了系统内核就行了,打扰

      Delete
  11. 嘛,正好有一个pi2 闲置了,利用起来好了

    ReplyDelete