斐讯N1 – 利用redsocks+ss实现全局翻墙
我们写程序难免要下载依赖库,然而大部分依赖库都托管在github之类的地方,并不是所有的依赖管理工具都支持配置代理,所以急需一种全局翻墙上网的方式。
大家都知道ss翻墙,它本质是socks5协议,socks5是4层代理协议(TCP/UDP),只不过在流量跨境的时候加了一层混淆。
ss平时浏览浏览网页是挺好用的,因为浏览器支持socks5协议封包发给本地的ss agent,可是并不是所有的软件都支持socks5,那么借助ss解决普遍的翻墙上网需求就很困难。
借助redsocks透明封包
斐讯N1是一个linux盒子,上面跑了ss的local端。
为了可以借助ss全局tcp翻墙,我需要把N1作为电脑的默认网关,这样所有上网流量都会通过N1。
在N1上,需要想办法把经过N1的原始的TCP流量拦截下来,识别其目标地址,将TCP流量封装到socks5协议中(此时相当于socks5客户端),然后将数据包的目标地址改为N1本机运行的ss local端,由ss local将socks5数据混淆发往境外。
直接使用iptables进行拦截篡改并不能实现识别目标地址与充当socks5客户端的能力,所以需要一个工作在linux内核层的特殊程序来帮我们搞定,这个程序就叫做redsocks。
我们只需要通过iptables将目标地址为外网IP的TCP流量改写到本机的redsocks端口,那么redsocks会在内核层解析到原始的TCP目标IP,然后在应用层进行socks5流量封装。
redsocks封装后的socks5流量将会在应用层转发给本机部署的ss local端,由ss local端完成进一步混淆并送往境外ss server端。
其实redsocks就是一个透明的socks5客户端,这样就不需要我们自己运行socks5客户端了。
优化Linux内核参数
redsocks作为一个代理程序,在实践过程中发现存在不稳定问题,netstat -tanlp观察到大量CLOSE_WAIT的端口,确认是redsocks没有关闭SOCKET导致了fd泄露,最终拒绝了服务。
对redsocks源代码作了简单的分析,确认redsocks作为一个TCP透明代理是没有应用层心跳的,所以当TCP连接断开时,socket层面无法感知到连接断开,所以导致逻辑上的fd泄露。
TCP协议默认有keepalive心跳,但是默认是2个小时,所以异常的TCP链接会在2小时后开始反馈到socket层面,fd才会逐渐被关闭。
解决这个问题的方法只有从linux内核参数入手,调短TCP keepalive的间隔,尽快让内核探测到连接断开的实事。
所以,下面跟随我做一些简单优化即可。
编辑/etc/sysctl.conf,增加如下配置(主要是TCP心跳优化,顺便调大了系统级fd限制,另外开启了bbr):
1 2 3 4 5 6 7 8 |
net.ipv4.tcp_keepalive_time = 5 net.ipv4.tcp_keepalive_probes = 2 net.ipv4.tcp_keepalive_intvl = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1 fs.file-max = 1000000 net.core.default_qdisc=fq net.ipv4.tcp_congestion_control=bbr |
编辑 /etc/security/limits.conf,增加如下配置(调达了用户级fd限制):
1 2 |
root soft nofile 10000 root hard nofile 10000 |
然后重启即可。
安装redsocks
注意,原版本的redsocks存在严重BUG,运行一段时间就会hung死,所以大家不要用apt安装。
我们需要使用国人维护的redsocks2版本,它修复了大量bug并增加了一些新的特性,广泛用于openwrt镜像中,稳定性应该是有保障的。
项目地址:https://github.com/semigodking/redsocks
首先下载代码:
1 |
git clone https://github.com/semigodking/redsocks.git |
然后安装编译依赖libevent和openssl:
1 2 |
apt install libevent-dev apt install libssl-dev |
然后执行编译:
1 |
make DISABLE_SHADOWSOCKS=true |
将二进制挪到标准路径下:
1 |
mv redsocks2 /usr/local/bin/redsocks |
拷贝一份配置文件模板并挪到标准路径下:
1 |
cp redsocks.conf.example /etc/redsocks.conf |
然后对其进行如下修改:
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 |
redsocks { /* `bind' defaults to 127.0.0.1:0 for security reasons, * use 0.0.0.0 if you want to listen on every interface. * `bind' are used as ip:port to redirect to. */ bind = "0.0.0.0:12345"; // listen() queue length. Default value is SOMAXCONN and it should be // good enough for most of us. // listenq = 128; // SOMAXCONN equals 128 on my Linux box. // `max_accept_backoff` is a delay to retry `accept()` after accept // failure (e.g. due to lack of file descriptors). It's measured in // milliseconds and maximal value is 65535. `min_accept_backoff` is // used as initial backoff value and as a damper for `accept() after // close()` logic. // min_accept_backoff = 100; // max_accept_backoff = 60000; // `relay' is IP address and port of proxy-server. Domain name is not // supported yet. // Can be: // [IPv6Address]:port // [IPv6Address] // IPv6Address // IPv4Address:port // IPv4Address // If no port is given, 0 is used. Usually, a valid port is required. relay = "127.0.0.1:5000"; |
- bind参数:redsocks监听在本机所有网卡的12345端口(一定要改成0.0.0.0,否则后续iptables规则无法奏效),后续将在iptables规则中将TCP流量拦截到这个端口。
- relay参数:配置指向本机运行的ss local端地址。
后续配置全部注释即可:
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 |
//redudp { // // `bind' should not be 0.0.0.0:0 as it's also used for outgoing // // packets that are sent as replies - and it should be fixed // // if we want NAT to work properly. // bind = "127.0.0.1:10053"; // // // `relay' is ip and port of socks5 proxy server. // relay = "10.0.0.1:1080"; // login = username;// field 'login' is reused as encryption // // method of shadowsocks // password = pazzw0rd; // // // know types: socks5, shadowsocks // type = socks5; // // // redsocks knows about two options while redirecting UDP packets at // // linux: TPROXY and REDIRECT. TPROXY requires more complex routing // // configuration and fresh kernel (>= 2.6.37 according to squid // // developers[1]) but has hack-free way to get original destination // // address, REDIRECT is easier to configure, but requires `dest` // // to be set, limiting packet redirection to single destination. // // [1] http://wiki.squid-cache.org/Features/Tproxy4 // dest = "8.8.8.8:53"; // // // Do not set it large if this section is for DNS requests. Otherwise, // // you may encounter out of file descriptor problem. For DNS requests, // // 10s is adequate. // udp_timeout = 30; // // udp_timeout_stream = 180; //} //tcpdns { // // Transform UDP DNS requests into TCP DNS requests. // // You can also redirect connections to external TCP DNS server to // // REDSOCKS transparent proxy via iptables. // bind = "192.168.1.1:1053"; // Local server to act as DNS server // tcpdns1 = "8.8.4.4:53"; // DNS server that supports TCP DNS requests // tcpdns2 = "8.8.8.8" ; // DNS server that supports TCP DNS requests // timeout = 4; // Timeout value for TCP DNS requests //} //autoproxy { // // Specify interface for outgoing connections. // // This is useful when you have multiple connections to // // internet or when you have VPN connections. // // interface = wlan0; // // no_quick_check_seconds = 60; // Directly relay traffic to proxy if an IP // // is found blocked in cache and it has been // // added into cache no earlier than this // // specified number of seconds. // // Set it to 0 if you do not want to perform // // quick check when an IP is found in blocked // // IP cache, thus the connection will be // // redirected to proxy immediately. // quick_connect_timeout = 3; // Timeout value when performing quick // // connection check if an IP is found blocked // // in cache. //} // //ipcache { // // Configure IP cache // cache_size = 4; // Maximum number of IP's in 1K. // stale_time = 900; // Seconds to stale an IP in cache since it is added // // into cahce. // // Set it to 0 to disable cache stale. // port_check = 1; // Whether to distinguish port number in address // cache_file = "/tmp/ipcache.txt"; // File used to store blocked IP's in cache. // autosave_interval = 3600; // Interval for saving ip cache into file. // // Set it to 0 to disable autosave. // // When autosave_interval and stale_time are both 0, IP cache behaves like // // a static blacklist. //} |
这些注释掉的部分的功能说明如下,你可以根据情况自己调节:
- redudp:SOCKS5也支持UDP代理,但是因为SS对udp支持不是太好,所以就不配置UDP透明SOCKS5封包了。
- tcpdns:redsocks2内置了一个UDP DNS转给TCP upstream DNS的功能,我也不用了。
- autoproxy:redsocks支持TCP直连探测IP,如果可以联通那就不走SS翻墙了,以便实现国内网站加速,但是因为后续是我会iptables拦截全局TCP流量,所以即便打开autoproxy也是无效的。
- ipcache:与autoproxy相关,一旦探测直连失败就会加入黑名单,后续一段时间就不会再直连探测,而是直接走proxy。
最后配置一下Redsocks开机启动:
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 |
root@aml:~/redsocks# cat /etc/systemd/system/redsocks.service [Unit] # 服务描述 Description=redsocks # 要求必须执行网络 Requires=network-online.target # 在网络启动之后启动 After=network-online.target [Service] # 简单服务 Type=simple # 运行用户与用户组 User=root Group=root # 进程退出立即重启 Restart=always # 执行命令 ExecStart=/usr/local/bin/redsocks -c /etc/redsocks.conf # 进程工作目录 WorkingDirectory=/root [Install] # 在系统启动后加载UNIT WantedBy=multi-user.target |
然后执行:
1 |
systemctl enable redsocks && systemctl start redsocks |
配置iptables流量拦截
现在redsocks已经部署完成,接下来只要把发往外网的TCP流量拦截给redsocks,它就可以帮我们在应用层完成SOCKS5封包并发往本机的SS local端了。
但是外网的IP段很多,配置iptables很难覆盖,所以我们可以通过排除法来排除掉内网IP段,达到同样的效果。
首先在nat表里配置一个自定义链叫做REDSOCKS(名字不重要),把拦截和转发规则写在这里面:
1 |
iptables -t nat -N REDSOCKS |
然后在这个链里跳过所有的内网IP,排除后的就是发往外网IP的流量,将他们拦截给redsocks进行socks5封包即可。
1 2 3 4 5 6 7 8 9 10 11 12 |
iptables -t nat -A REDSOCKS -d 0.0.0.0/8 -j RETURN iptables -t nat -A REDSOCKS -d 10.0.0.0/8 -j RETURN iptables -t nat -A REDSOCKS -d 100.64.0.0/10 -j RETURN iptables -t nat -A REDSOCKS -d 127.0.0.0/8 -j RETURN iptables -t nat -A REDSOCKS -d 169.254.0.0/16 -j RETURN iptables -t nat -A REDSOCKS -d 172.16.0.0/12 -j RETURN iptables -t nat -A REDSOCKS -d 192.168.0.0/16 -j RETURN iptables -t nat -A REDSOCKS -d 198.18.0.0/15 -j RETURN iptables -t nat -A REDSOCKS -d 224.0.0.0/4 -j RETURN iptables -t nat -A REDSOCKS -d 240.0.0.0/4 -j RETURN iptables -t nat -A REDSOCKS -d xx.xx.xx.xx/32 -j RETURN iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-ports 12345 |
RETURN就是指停止遍历该链的后续规则,也就是不拦截。
如果不属于任何一个内网IP段,那么就REDIRECT到redsocks的12345端口。
注意:网上有人反馈说内网电脑无法上网,而从N1盒子本机可以上网,这是因为REDIRECT规则只能在当前网口完成端口改写,所以大家一定要注意让redsocks监听在0.0.0.0而不是localhost,这样内网流量才能顺利流入redsocks。(iptables这一块的坑点请见:https://qiuqi.github.io/2017/06/01/iptables-dnat%E5%92%8Credirect%E5%A4%B1%E6%95%88%E7%9A%84%E5%8E%9F%E5%9B%A0/)
注意:在倒数第2条规则,是我境外的ss server端IP,即发往该IP的流量虽然属于外网但是不能拦截,否则ss local发出去的翻墙包又会被拦截,再次被发往redsocks,造成递归封装。
最后呢,我们要让从内网发到N1的TCP流量,以及从N1本机发起的TCP流量,均经过REDSOCKS链的判定处理:
1 2 |
iptables -t nat -A PREROUTING -p tcp -j REDSOCKS iptables -t nat -A OUTPUT -p tcp -j REDSOCKS |
默认iptables规则重启后会清空,我们需要安装一个工具来保存iptables规则,并且自动帮我们开机加载规则:
1 |
apt-get install iptables-persistent |
首次安装会询问你是否立即保存当前的iptables规则,选择y即可。
后续修改了iptables,则需要重新保存执行:
1 |
netfilter-persistent save |
验证效果
从N1本机验证OUTPUT链是否工作正常:
1 2 3 4 5 6 7 |
curl 'https://216.58.195.78' -H 'Host: google.com' -k <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>301 Moved</TITLE></HEAD><BODY> <H1>301 Moved</H1> The document has moved <A HREF="https://www.google.com/">here</A>. </BODY></HTML> |
这里IP地址是我从境外解析到的谷歌IP,可见访问正常。
为什么要手动指定IP呢?因为现在是通过TCP透明拦截封包的方式使用socks5,所以域名的解析是本地完成的(实际上socks5支持dns远程解析),这就可能被DNS污染解析到错误IP。
至于如何解决DNS污染问题,后续我会再写博客说明如何自己架设一个不会被污染的本地DNS服务。
为了验证PREROUTING链是否正常翻墙,可以将内网电脑的默认网关改为N1,这样电脑所有流量都会过N1,因此就实现了电脑全局TCP翻墙。
当然这里还有一个坑,就是目前N1只拦截了目标IP是外网的TCP流量到本机redsocks,除此之外的流量仍旧是需要走N1路由表直接forward出去的,因此我们别忘记了打开N1上的ip_forward:
1 |
echo 1 > /proc/sys/net/ipv4/ip_forward |
为了持久化配置,需要修改/etc/sysctl.conf文件,将如下的行删除注释:
1 2 |
# Uncomment the next line to enable packet forwarding for IPv4 net.ipv4.ip_forward=1 |
另外,我们不需要在POSTROUTING配置SNAT,因为redsocks流量是应用层处理的,而非redsocks通路的回程流量可以直接发回到真实内网源IP机器而不需要再经过N1盒子socks5解密,这样少一层forward转发效率更高。
最后
如果仅仅是为了全局翻墙,可以考虑利用上述手段实现,而不用折腾openwrt。
整个过程需要对iptables和网络有一定理解,可以看一下我的另外一篇博客《2小时学会iptables》。
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~

牛逼立体!
保姆及
redsocks这是什么毛病,报 warning base.c:262 getdestaddr_iptables(…) getsockopt: No such file or directory ,系统5.4.25-amlogic-flippy-30
没遇到过啊,你谷歌一下
https://github.com/semigodking/redsocks/issues/121
找到issue说是内核问题,博主请问你的n1是什么系统版本啊?
我记得是5.9.5之类的,你翻翻我的其他博客。
找到是armbian5.77,装了后遇到新错误,warning base.c:251 getdestaddr_iptables(…) getsockopt: Protocol not available,可能是我用了tproxy + v2ray的缘故…请问这个redsocks + shadowsocks可以达到full cone吗?
make: *** No targets specified and no makefile found. Stop.
请问下大佬在make DISABLE_SHADOWSOCKS=true时报这个错误是为啥呢
看看Makefile
好blog。如果能够增加这个,说明“为什么redirect后还能知道原始dst ip与dst port”,就更好。
https://unix.stackexchange.com/questions/166692/how-does-a-transparent-socks-proxy-know-which-destination-ip-to-use