Total Pageviews

Thursday, 8 October 2015

利用GOPROXY翻墙

项目地址:https://github.com/shell909090/goproxy
1. 编译:
安装golang1.5.1,配置好环境变量,然后
cd ~/go/gopath
go get -u -v github.com/luckypoem/goproxy-3/goproxy
(可执行文件goproxy就会出现在~/go/gopath/bin/里面)

创建key:
head -c 16 /dev/random | base64
记住这个KEY.
创建goproxy_config.json文件,内容如下:
{
    "mode": "server",
    "listen": ":5678",

    "logfile": "my.log",
    "loglevel": "WARNING",
    "adminiface": "127.0.0.1:5234",

    "cipher": "aes",
    "key": "your key",

    "passwd": {
        "username": "password"
    }
}

把key的值和你所设的username,password的值填进去,然后保存文件。
然后运行: goproxy --config goproxy_config.json &
服务端全部搞定.
 客户端:
wget http://www.dwhd.org/goproxy/goproxy_darwin_amd64
chmod 755 goproxy_darwin_amd64
然后创建config.json,内容如下:
{
    "mode": "http",
    "listen": ":8000",
    "server": "VPS-IP:5678",

    "logfile": "my.log",
    "loglevel": "WARNING",
    "adminiface": "127.0.0.1:5234",
    "dnsnet": "internal",
    "cipher": "aes",
    "key": "your key",
   
    "username": "username",
    "password": "password"
}
同样把key的值和你所设的username,password的值填进去,存盘退出。
(windows桌面系统下,新建一个批处理文件client.bat,内容: 
goproxy --config config.json,然后运行client.bat;)
linux桌面系统下,把linux vps上编译好的可执行文件goproxy(即/root/go/bin/goproxy文件)下载到linux桌面系统。然后chmod 755 goproxy,
运行: ./goproxy --config config.json
在mac桌面系统下,运行: ./goproxy_darwin_amd64 --config config.json
最后设置浏览器的http代理-127.0.0.1:8000,enjoy!
我测试成功,能够翻墙.
项目地址:
https://github.com/shell909090/goproxy/
https://github.com/shell909090/goproxy/issues/14
https://www.dwhd.org/20150829_125826.html
-------------
基于go写的隧道代理服务器,主要用于翻墙。
主要分为两个部分,客户端和服务器端。客户端使用http协议向其他程序提供一个标准代理。当客户端接受请求后,会加密连接服务器端,请求服务器端连接目标。
具体工作细节是。首先查询国外DNS并获得正确结果(未污染结果),然后把结果和IP段表对比。如果落在国内,直接代理。如果国外,多个请求复用一个加密tcp把请求转到国外vps上处理。加密是预共享密钥。
注意,新版本不再内置dns清洗,建议采用其他dns清理方案。但带有udp port forward和dns over msocks的功能。

msocks协议

msocks协议最大的改进是增加了连接复用能力,这个功能允许你在一个TCP连接上封装多个tcp连接。由于qsocks协议非常快速的建立和释放连接,并且每次连接时必然是连接方向目标方发送大量数据,目标方再反向发送。因此有可能被流量模型发现。msocks保持这个连接,因此连接建立速度更快,没有大量的打开和关闭开销,而且流量模型很难发现。
但是由于多个tcp复用封装到一个tcp内,导致单tcp过慢时所有请求的速度都受到压制。因此记得调优tcp配置,增强LFN下的网络效率。而且注意,当高速下载境外资源时,其他翻墙访问会受到影响。
net.ipv4.tcp_congestion_control = htcp
net.core.rmem_default = 2621440
net.core.rmem_max = 16777216
net.core.wmem_default = 655360
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096        2621440 16777216
net.ipv4.tcp_wmem = 4096        655360  16777216

连接池规则

在msocks的客户端,一次会主动发起一个连接。当连接数低于一定个数时会主动补充(目前编译时设定为1),除非连接被ping-pong game关闭。
在连接时,会寻找承载tcp最少的一根去用。如果所有连接中,承载tcp最小的连接数大于一定值(目前是32),那么会在后台再增加一根tcp。
当msocks连接断开时,在上面承载的tcp不会主动迁移到其他msocks上,而是会跟着断开。如果连接池满足一定规则(如上所述),那么断开的连接会重新发起。
连接池不会主动释放链接。但是在断开时不满足规则的链接不会被重建。这使得连接池可以借助链接的主动断开回收msocks连接。
总体来说,连接池使得每个tcp承载的最大连接数保持在30-40左右。避免大量连接堵塞在一个tcp上,同时也尽力避免频繁的tcp连接握手和释放。

用法和配置说明

配置和路径

系统默认使用/etc/goproxy/config.json作为配置文件,这一路径可以通过命令行参数-config来修改。
配置文件内使用json格式,其中可以指定以下内容:
  • mode: 运行模式,可以为server/http/留空。留空是个特殊模式,表示不要启动。
  • listen: 监听地址,一般是:port,表示监听所有interface的该端口。
  • server: 服务地址,在http模式下需要,表示需要连接的目标地址。
  • logfile: log文件路径,留空表示输出到stdout。在deb包中建议留空,用init脚本的机制来生成日志文件。
  • loglevel: 日志级别,必须设定。支持EMERG/ALERT/CRIT/ERROR/WARNING/NOTICE/INFO/DEBUG。
  • adminiface: 服务器端的控制端口,可以看到服务器端有多少个连接,分别是谁。
  • dnsaddrs: dns查询的目标地址列表。如不定义则采用系统自带的dns系统,会读取默认配置并使用。
  • dnsnet: dns的网络模式,默认为udp模式,设定为tcp可以采用tcp模式,设定为internal采用内置模式。
  • cipher: 加密算法,可以为aes/des/tripledes,默认aes。
  • key: 密钥。16个随机数据base64后的结果。
  • blackfile: 黑名单文件,http模式下可选。
  • minsess: 最小session数,默认为1。
  • maxconn: 一个session的最大connection数,超过这个数值会启动新session。默认为16。
  • username: 连接用户名,http模式下需要。
  • password: 连接密码,http模式下需要。
  • auth: 认证用户名/密码对,server模式下需要。
  • portmaps: 端口映射配置,将本地端口映射到远程任意一个端口。
其中portmaps的配置应当是一个列表,每个成员都应设定如下的值。
  • net: 映射模式,支持tcp/tcp4/tcp6/udp/udp4/udp6。注意:6没测试过。
  • src: 源地址。
  • dst: 目标地址。

server模式

服务器模式运行在境外机器上,监听某个端口提供服务。客户端可以连接服务器端,通过他连接目标tcp。中间所用的协议是tcp级的。
服务器模式一般需要定义用户名/密码对以验证客户端身份。用户名/密码对在配置文件的auth项目下定义。

http模式

http模式运行在本地,需要一个境外的server服务器做支撑,对内提供http代理。
连接到http模式的服务上,可以按照http代理协议访问某个目标。服务首先会对目标做DNS查询,然后根据IP地址区分,如果在国内,直接连接。如果国外,使用server去连接。区分的基础是IP段分配列表。
地址黑名单(直接访问的项)在配置文件blackfile下定义。

黑名单文件

黑名单文件是一个路由文件,其中列出的子网将不会由服务器端代理,而是直接连接。这通常用于部分IP不希望通过服务器端的时候。
黑名单文件使用文本格式,每个子网一行。行内以空格分割,第一段为IP地址,第二段为子网掩码。允许使用gzip压缩,后缀名必须为gz,可以直接读取。routes.list.gz为样例。

port mapping

通过portmaps项,可以将本地的tcp/udp端口转发到远程任意端口。

dns配置

dns是goproxy中很特殊的一个功能。由于代理经常接到连接某域名的指令,因此为了进行ip匹配,需要先进行dns查询。
在老版本goproxy中,使用的是修改过的golang内置的dns系统。由于过滤系统依赖于污染返回地址仅限于特定地址的假定,所以当污染系统升级后,这个方案就不再可行。在新版goproxy中,建议使用内置方案。

dns over msocks

当dnsnet设定为internal时,采用dns over msocks工作。此时dns透过msocks众多连接中的一条发往远程,由远程goproxy解析。远程goproxy的解析配置由远程配置文件中的dnsaddrs指定。

dns over udp port mapping

注意到goproxy提供了udp port mapping的功能。这个功能提供了一种可能性,将本地的53端口映射到远程的dns服务器53端口,从而转发dns请求。
有两个原因不推荐这个方案。
  1. 从不同地址/端口,向同一个端口发出的不同请求,在远程必须从不同的udp bind port发出,否则就无法区分回包需要被转发到哪个回端。而dns请求时,经常会在本地任意指定端口,这导致会占用大量服务器上的udp bind port。
  2. udp并没有连接的概念,因此没有“连接断开”的概念。在udp客户端对本地端口的占用不继续后,goproxy并不能立刻感知这一变化,并关闭远程udp bind port。因此需要用超时(目前设定为5分钟)来控制关闭。这一行为加剧了服务器上udp bind port的消耗。

key的生成

可以使用以下语句生成,写入两边的config即可。
head -c 16 /dev/random | base64

服务器端配置样例

{
    "mode": "server",
    "listen": ":5233",

    "logfile": "",
    "loglevel": "WARNING",
    "adminiface": "127.0.0.1:5234"

    "cipher": "aes",
    "key": "[your key]",

    "passwd": {
        "username": "password"
    }
}

客户端配置样例

{
    "mode": "http",
    "listen": ":5233",
    "server": "srv:5233",

    "logfile": "",
    "loglevel": "WARNING",
    "adminiface": "127.0.0.1:5234"

    "dnsnet": "internal",

    "cipher": "aes",
    "key": "[your key]",
    "blackfile": "/usr/share/goproxy/routes.list.gz",

    "username": "username",
    "password": "password"
}

port mapping样例

{
    "mode": "http",
    "listen": ":5233",
    "server": "srv:5233",

    "logfile": "",
    "loglevel": "WARNING",
    "adminiface": "127.0.0.1:5234"

    "cipher": "aes",
    "key": "[your key]",
    "blackfile": "/usr/share/goproxy/routes.list.gz",

    "username": "username",
    "password": "password",
    "portmaps": [
        {
            "net": "udp",
            "src": ":53",
            "dst": "114.114.114.114:53"
        },
        {
            "net": "tcp",
            "src": ":80",
            "dst": "115.239.211.112:80"
        }
    ]
}

admin界面

在http模式下,直接访问代理端口,可以看到当前工作中的所有msocks链接和其中承载的tcp链接。

状态解说

  • sess: 显示msocks链接的id,其实为本地端口号。
  • id: 显示连接在msocks中的编号,随着时间递增而增加。
  • state: 显示链接状态。msocks显示承载了多少tcp(下面的行数),和lastping。
  • Recv-Q: 接收后尚未读取的字节数,如果长时间不为0应该是bug。如果是msocks,则显示粗略的每秒接收字节数。
  • Send-Q: 发送后未确认的字节数。如果长时间只增长可能是对方没有回应(例如链接断开)。如果是msocks,则显示粗略的每秒发送字节数。
  • Target: 远程的地址。msocks行是服务器/客户端地址。连接行是这个链接所链接到的目标。

last ping

goproxy利用自定的ping-pong规则来检查和保持tcp的活跃。从一端开始发出ping包,对方收到后间隔一段时间后回复。如果超时不回复,则主动断开连接。如果在多次ping-pong中都没有数据收发,则主动断开。
这个机制的保活效果比tcp keepalive更加激进一些,可以在秒级检查连接通畅。但是相应的,更容易受到网络抖动影响而误判为失去连接。lastping上面显示的是最后一次ping的时间间隔。如果超过一定值(目前设定值为30s),则断开连接。

cut off

切断所有连接。一般用于所有链接都处于断开状态。大多数情况用不到。

deb包解说

deb包是适用于debian/ubuntu的安装包。目前打包和测试都是在debian testing上完成,因此对此种系统的支持最完美。debian stable上可保证正常运行。ubuntu的兼容性希望得到反馈。同时希望有人做ubuntu移植,将启动模式改为upstart。
deb包中,主程序在/usr/bin下,启动项在/etc/init.d/goproxy下,配置文件在/etc/goproxy下。修改配置文件后重启服务生效。
默认black文件在/usr/share/goproxy/routes.list.gz。日志默认在/var/log/goproxy.log生成。日志的配置在init文件中修改。
在debian目录下有个默认的init脚本,负责将goproxy封装为服务。

tar包解说

tar包内包含主程序,routes.list.gz示例。没有config.json示例。因此你需要自行编写一个正确的config.json,然后使用goproxy -config config.json来启动程序。
整个包不需要安装,手工启动和关闭。如果需要自动启动机制,请自行处理。

鸣谢

TODO

  • http协议加入proxy authentication
  • 增加dns对外服务?(其实可以用udp端口映射来完成)

from https://github.com/shell909090/goproxy
(此goproxy不是彼goproxy- http://briteming.blogspot.com/2015/08/goproxy.html)

--------------------------------------------------------

服务端设置

先安装Go 1.5版本
wget https://storage.googleapis.com/golang/go1.5.linux-amd64.tar.gz
tar xf go1.5.linux-amd64.tar.gz -C /usr/local
设置环境变量 其中GOROOT为安装go的目录,GOPATH为一般工程目录,操作步骤:把以下内容,追加到/etc/profile文件末尾:
export GOROOT=/usr/local/go
export GOPATH=/root/go_path
export PATH=$GOROOT/bin:$GOPATH/bin:$PATH
重启reboot以使生效
编译并安装:
mkdir go_path
go get github.com/shell909090/goproxy/goproxy
go build github.com/shell909090/goproxy/goproxy
生成一个key,服务器和客户端都用到这个
head -c 16 /dev/random | base64
设置服务端config
cd go_path/src/bin
vi config.json
输入,表示监听本机8888端口
{
    "mode": "server",
    "listen": ":8888",
    "logfile": "my.log",
    "loglevel": "WARNING",
    "adminiface": "127.0.0.1:5234",
    "cipher": "aes",
    "key": "[your key]",
    "passwd": {
        "username": "password"
    }
}
保存,执行
./goproxy -config ./config.json
或者使用screen后台运行
screen -dmS goproxy ./goproxy -config ./config.json

客户端设置

在vps上面进行交叉编译:
参考文章: 编译教程- https://www.dwhd.org/20150829_125826.html
本站已经编译好了各种版本:
01
02
03
04
05
06
07
08
09
10
http://www.dwhd.org/goproxy/goproxy_linux_386
http://www.dwhd.org/goproxy/goproxy_linux_amd64
http://www.dwhd.org/goproxy/goproxy_windows_386.exe
http://www.dwhd.org/goproxy/goproxy_windows_amd64.exe
http://www.dwhd.org/goproxy/goproxy_darwin_386
http://www.dwhd.org/goproxy/goproxy_darwin_amd64
http://www.dwhd.org/goproxy/goproxy_freebsd_386
http://www.dwhd.org/goproxy/goproxy_freebsd_amd64
http://www.dwhd.org/goproxy/goproxy_arm
http://www.dwhd.org/goproxy/goproxy_arm64
32bit
GOOS=linux  GOARCH=386 go build -o /tmp/goproxy_linux_i386 github.com/shell909090/goproxy/goproxy
GOOS=darwin  GOARCH=386 go build -o /tmp/goproxy_darwin_i386 github.com/shell909090/goproxy/goproxy
GOOS=freebsd  GOARCH=386 go build -o /tmp/goproxy_freebsd_i386 github.com/shell909090/goproxy/goproxy
GOOS=windows  GOARCH=386 go build -o /tmp/goproxy_windows_i386.exe github.com/shell909090/goproxy/goproxy
64bit
GOOS=linux  GOARCH=amd64 go build -o /tmp/goproxy_linux_amd64 github.com/shell909090/goproxy/goproxy
GOOS=darwin  GOARCH=amd64 go build -o /tmp/goproxy_darwin_amd64 github.com/shell909090/goproxy/goproxy
GOOS=freebsd  GOARCH=amd64 go build -o /tmp/goproxy_freebsd_amd64 github.com/shell909090/goproxy/goproxy
GOOS=windows  GOARCH=amd64 go build -o /tmp/goproxy_windows_amd64.exe github.com/shell909090/goproxy/goproxy
ARM
GOOS=linux  GOARCH=arm go build -o /tmp/goproxy_arm github.com/shell909090/goproxy/goproxy
GOOS=linux  GOARCH=arm64 go build -o /tmp/goproxy_arm64 github.com/shell909090/goproxy/goproxy
配置文件config.json,表示监听本地8889端口
{
    "mode": "http",
    "listen": ":8889",
    "server": "SERVER_IP:82",
    "logfile": "",
    "loglevel": "WARNING",
    "adminiface": "127.0.0.1:5234"
    "dnsnet": "internal",
    "cipher": "aes",
    "key": "your key",
    // Windows client you should move route.list.gz to the dir of goproxy.exe
    // Add change to "./routes.list.gz"
    "blackfile": "/usr/share/goproxy/routes.list.gz",
    "username": "username",
    "password": "password"
}
------------------------------



goproxy和msocks简介


goproxy是我个人写的,和shadowsocks同类的软件。当然,在设计之初我完全不知道shadowsocks的存 在,goproxy的最初目标也不是成为shadowsocks的同类。只是我一直无法实现一个可靠的,能够达成目标的系统。最后想,那这样吧,我找一个 跳一跳能够够到的苹果。大幅简化的结果就是goproxy——后来我才知道shadowsocks。

shadowsocks的基本原理

shadowsocks的基本概念,就是利用某种不同于SSL的协议,将本地的socks数据流转发到远程。这个协议,在默认版本中是一个凯撒变 换,后来有了aes等加密算法。goproxy也采用了类似的做法,同样支持aes等加密算法。在每次连接时,客户端先用加密通道连接服务器端,然后完成 整个连接通路。这样的设计鲁棒性相当好,但是作为代价,也有不少缺陷。
首先,goproxy和shadowsocks不约而同的采用了自己的协议,而非将socks5透明的转发到远程的服务器端。为什么?因为 socksv5协议中,握手过程是三次交互。客户发送握手包,服务器响应允许的握手验证方法。客户发送验证报文,服务器端返回是否成功,客户发送要连接的 目标,服务器端返回是否成功。细节我记得不是很清楚,但是2-3次往返是必须的。
这种工作机制需要client -> proxy-client -> proxy-server -> server的一个链条,本身就比直连多了两次TCP握手。加上上述的往返过程,更加耗时。而且这个消耗在每次建立链接时都要来一次,而HTTP是一种短 连接协议——这就更加无法容忍了。因此改用自有协议,一次交互完成握手,就会更加快速。
更根本的原因在于,这两个系统都需要越过IDS,而三次交互的报文大小是几乎固定的——就算加密也无法改变报文大小。不但大小一样,而且由于用户名密码相同,起始加密过程和IV一致,因此采用socks协议的话,每个链接开始都有相同的来返数据。
我不知道shadowsocks怎么处理这个问题。qsocks协议(msocks)的前身规定,每次握手时客户端提供一组IV,然后发送一个头 部变长的字符串(256字符以内),在远程丢弃同样长度的随机字符。经过这样的处理,每次链接时的报文长度和内容序列都不一样,增加了破译难度。至于多出 来的几十个字节,和验证报文在一个报文内,开销相比一次RTO几乎可以忽略不计。
但是还是有一点无法避免的问题。如果你看到某个服务器上有一个端口,频繁的被一个或多个IP链接。每个链接都不长,每次都是客户端吐一堆数据,服务 器返回一堆,然后关闭链接。尽管协议无法破解,但是基本可以肯定这就是shadowsocks。根据这个特性,可以有效的阻挡服务——这也是我最近碰到的 问题。
而且每个链接都需要验证和TCP握手太慢了。

msocks的改进

所以,我参考SPDY协议,做了msocks。msocks的核心思路和qsocks很类似,主要修改是以下两点:
  1. 使用一个可靠链接(这里是经过加密的TCP),在这个链接里面封装多对传输。
  2. 每个链接只要一次验证。
这样做,首先减少了一次TCP握手和一次身份验证,工作速度更加快。其次多个传输叠加在一个流里面,流特征更加变化莫测。最后,无论是服务器端还是客户端的开销都小了很多。
当然,这也带来不少问题。例如TCP原本的拥塞控制窗口是为了一对传输序列设计的。当很多传输序列在一对TCP上传递的时候,丢报文造成的影响会作 用作用在全体传输序列上。包括丢了一个报文重传的时候,所有序列都必须阻塞。还有基础的TCP被施加了丢包,导致全体序列共享5k带宽。当然,经过评估 后,我觉得这些问题比频繁握手更加轻,所以就设计了msocks协议。
协议设计的时候,有几个细节问题。

多对复用

我采用了一个map,来记录某个id是否对应到了一个控制结构。这个映射只能被客户端更改,并且有个专门的函数负责查找空闲的id,每次生成的id都是递增的,如果碰到最大值则绕回。
id的大小是16位,足够容纳65536对同时链接。其实不修改内核的话,500对代理就会导致too many files。
实际上一般到id达到400后,单一的tcp就断线重连了。目前我还没见过上千的数字呢。

连接状态

连接一般情况下可以看到5种状态,连接请求发送,连接请求接收,连接建立,主动关闭连接中,被动关闭连接中。
当客户端请求代理连接一个远程服务器时,进入连接请求发送。代理远程端接受后在连接目标服务器的过程中,进入连接请求接收。当成功后,双方进入连接建立。
当关闭时,主动发起关闭一端进入主动关闭,另一端进入被动关闭。当被动关闭端调用close,或者主动关闭端收到对方关闭,整个链接就销毁。
由于tcp是可靠传输,因此三次握手和四次关闭都是不必须的。
简单吧。

拥塞控制

TCP原本是带有拥塞控制的——借助SSN双序列和窗口机制。但是在多路复用的时候,我们需要自行控制拥塞——而且不能采用会和机制。会和会导致后续已经到达的其他链接的报文被一个没人接收的报文阻挡。所以必须采用带拥塞控制的缓存队列机制。
不过幸好,TCP本身是可靠传输协议,所以我不用担心丢包重发之类的问题。我需要做的,就是把对方读取的字节数传递回来,减在控制器上,即可。
不过,我没有做对应于silly window syndrome的优化,在每次读取小数据量后,这个读取造成的window扩张都会被传回。当然,这么设计是有原因的。我默认采用了8K的buffer进行fd间拷贝,所以一般碰不到SWS。
为了解决tcp链接复用造成的单连接带宽问题,我强烈的建议你做以下的设定:
net.ipv4.tcp_congestion_control = htcp
net.core.rmem_default = 2621440
net.core.rmem_max = 16777216
net.core.wmem_default = 655360
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096    2621440 16777216
net.ipv4.tcp_wmem = 4096    655360  16777216

ip选择算法和DNS

在goproxy中,我沿用了一个做法。通过DNS获得请求的目标IP,和中国IP范围核对。如果在国内则直接访问,否则透过代理。这个方法能够极快的加速访问,而且几乎不依赖于需要更新的列表(中国IP列表相对来说固定)。
问题是DNS解析过程。msocks内置了DNS能力,可以帮助做DNS。但是实践下来发现这样做效果并不很好。而原本是采用直接DNS,丢弃特定的报文。这样可以过滤防火墙污染。
原因很简单。原本的模式会让DNS服务器感知到查询者位于中国,于是给出中国可以访问的最快地址。而新的模式则会将DNS请求者搬到美国——这无故 加重了代理的负担。例如www.qq.com,原本只需要请求得到一台深圳的服务器即可,现在则需要让DNS绕出去,再回来。如果不幸,QQ有一台位于美 国的服务器,那么我的访问都会通过这台服务器——这可比深圳的服务器慢多了。

from http://shell909090.org/blog/archives/2627
-----
https://github.com/justzx2011/goproxy ,这是一个fork