goproxy

module
v3.0.0+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2017 License: BSD-3-Clause, GPL-2.0

README

Build Status

goproxy

goproxy是基于go写的隧道代理服务器,主要用于翻墙。

主要分为两个部分,客户端和服务器端。客户端使用http协议向其他程序提供一个标准代理。当客户端接受请求后,会加密连接服务器端,请求服务器端连接目标。

具体工作细节是。首先查询国外DNS并获得正确结果(未污染结果),然后把结果和IP段表对比。如果落在国内,直接代理。如果国外,多个请求复用一个加密tcp把请求转到国外vps上处理。加密是预共享密钥。

注意,新版本不再内置dns清洗,建议采用其他dns清理方案。但带有udp port forward和dns over msocks的功能。

加密协议

协议(一般)使用AES-CBC来加密数据,在服务器-客户端间预先共享一个key。在连接时互相交换IV。

如果使用AES,双方需要先保持16bytes的随机数用做密钥。这些随机数被base64编码放在key字段中。服务器和客户端需要保持一致。

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)。

在连接时,会寻找承载tcp最少的一根去用。如果所有连接中,承载tcp最小的连接数大于一定值(配置中的maxconn),那么会在后台再增加一根tcp(不影响当前连接的选择)。

当msocks连接断开时,在上面承载的tcp不会主动迁移到其他msocks上,而是会跟着断开。如果连接池满足一定规则(如上所述),那么断开的连接会重新发起。

连接池不会主动释放链接。但是在断开时不满足规则的链接不会被重建。这使得连接池可以借助链接的主动断开回收msocks连接。

总体来说,连接池使得每个tcp承载的最大连接数保持在15-25左右。避免大量连接堵塞在一个tcp上,同时也尽力避免频繁的tcp连接握手和释放。

服务器选择规则

当链接数不足时,会发起新连接。由于配置允许写入多个服务器端,因此程序会随机选择一个配置尝试连接。如果尝试失败(无法握手或者超时),会选择下一个配置。如此重复两轮,如果都无法连接,则连接发起失败。

用法和配置说明

配置和路径

系统默认使用/etc/goproxy/config.json作为配置文件,这一路径可以通过命令行参数-config来修改。

配置文件内使用json格式,其中可以指定以下内容:

  • mode: 运行模式,可以为server/http/留空。留空是个特殊模式,表示不要启动。
  • listen: 监听地址,一般是:port,表示监听所有interface的该端口。
  • logfile: log文件路径,留空表示输出到stdout。在deb包中建议留空,用init脚本的机制来生成日志文件。
  • loglevel: 日志级别,必须设定。支持EMERG/ALERT/CRIT/ERROR/WARNING/NOTICE/INFO/DEBUG。
  • adminiface: 服务器端的控制端口,可以看到服务器端有多少个连接,分别是谁。
  • dnsnet: dns的网络模式,默认为udp模式,可选用tcp模式,设定为https采用google dns-over-https。client模式下连接过程走msocks。
  • dnsaddrs: dns查询的目标地址列表。如不定义则采用系统自带的dns系统,会读取默认配置并使用。

internal模式下,使用msocks来查询远程数据(具体命中到哪个zone不确定)。https模式下,使用google dns-over-https来查询数据。https模式带有edns client subnet功能,会自动修正国内地址。

server模式

服务器模式运行在境外机器上,监听某个端口提供服务。客户端可以连接服务器端,通过他连接目标tcp。中间所用的协议是tcp级的。

服务器模式一般需要定义用户名/密码对以验证客户端身份。用户名/密码对在配置文件的auth项目下定义。

server模式专用配置

  • cryptmode: 字符串。tls表示使用tls模式,其他表示使用cryptconn模式。
  • rootcas: 字符串,只在tls模式下生效。以回车分割的多行字符串,每行一个文件路径,表示服务器认可的客户端ca根。不设定的话服务器端不做客户端证书验证。
  • certfile: 字符串,只在tls模式下生效。服务器端使用的证书文件。
  • certkeyfile: 字符串,只在tls模式下生效。服务器端使用的证书密钥。
  • forceipv4: 布尔型。是否强制任何拨号都使用ipv4。
  • cipher: 加密算法,只在cryptconn模式下生效。可以为aes/des/tripledes,默认aes。
  • key: 密钥,只在cryptconn模式下生效。16个随机数据base64后的结果,客户端必须严格匹配方能通讯。
  • auth: dict类型。认证用户名/密码对。

http模式

http模式运行在本地,需要一个境外的server服务器做支撑,对内提供http代理。

连接到http模式的服务上,可以按照http代理协议访问某个目标。服务首先会对目标做DNS查询,然后根据IP地址区分,如果在国内,直接连接。如果国外,使用server去连接。区分的基础是IP段分配列表。

地址黑名单(直接访问的项)在配置文件blackfile下定义。

http模式专用配置

  • blackfile: 黑名单文件,http模式下可选。
  • minsess: 最小session数,默认为1。
  • maxconn: 一个session的最大connection数,超过这个数值会启动新session。默认为16。
  • servers: 服务器列表。
  • httpuser: 客户端访问此http代理服务时的用户名。
  • httppassword: 客户端访问此http代理服务时的密码。
  • portmaps: 端口映射配置,将本地端口映射到远程任意一个端口。

其中servers是一个列表,成员定义如下:

  • server: 中间代理服务器地址。
  • cryptmode: 字符串。tls表示使用tls模式,其他表示使用cryptconn模式。
  • rootcas: 字符串,只在tls模式下生效。以回车分割的多行字符串,每行一个文件路径,表示客户认可的服务器端ca根。不设定的话使用系统根证书设定。
  • certfile: 字符串,只在tls模式下生效。客户端使用的证书文件。
  • certkeyfile: 字符串,只在tls模式下生效。客户端使用的证书密钥。
  • cipher: 加密算法,可以为aes/des/tripledes。默认为aes。
  • key: 密钥。16个随机数据base64后的结果。
  • username: 连接用户名。
  • password: 连接密码。

其中portmaps的配置应当是一个列表,每个成员都应设定如下的值。

  • net: 映射模式,支持tcp/tcp4/tcp6/udp/udp4/udp6。注意:6没测试过。
  • src: 源地址。
  • dst: 目标地址。

黑名单文件

黑名单文件是一个路由文件,其中列出的子网将不会由服务器端代理,而是直接连接。这通常用于部分IP不希望通过服务器端的时候。

黑名单文件使用文本格式,每个子网一行。行内以空格分割,第一段为IP地址,第二段为子网掩码。允许使用gzip压缩,后缀名必须为gz,可以直接读取。routes.list.gz为样例。

CIDR style ip range definition is acceptable.

port mapping

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

UDP support not tested yet.

dns配置

dns是goproxy中很特殊的一个功能。由于代理经常接到连接某域名的指令,因此为了进行ip匹配,需要先进行dns查询。

在老版本goproxy中,使用的是修改过的golang内置的dns系统。由于过滤系统依赖于污染返回地址仅限于特定地址的假定,所以当污染系统升级后,这个方案就不再可行。在新版goproxy中,建议使用内置方案。

If dns response get multiple result, one of them matched blacklist will made goproxy connect target directly.

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",
 
	"logfile": "",
	"loglevel": "WARNING",
	"adminiface": "127.0.0.1:5234"

	"dnsnet": "internal",
	"cipher": "aes",

	"blackfile": "/usr/share/goproxy/routes.list.gz",

    "servers": [
	    {
		    "server": "srv:5233",
			"key": "[your key]",
		    "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"
		}
	]
}

TLS权限问题

默认情况下,goproxy使用nobody和nogroup作为启动用户和组。这是一个非常小权限的组,在系统内相对比较安全。

但是在TLS模式下,goproxy需要读取证书文件。这些文件(尤其是key)出于安全理由,往往都指定为root读写,其他人没有权限。在这种情况下,需要修改/lib/systemd/system/goproxy.service,将nobody和nogroup替换为root。然后再用systemctl daemon-reload重新加载配置,用systemctl restart goproxy重启服务。

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来启动程序。

整个包不需要安装,手工启动和关闭。如果需要自动启动机制,请自行处理。

鸣谢

在此表示感谢

授权

Copyright (C) 2012-2014 Shell Xu

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

http://www.gnu.org/licenses/gpl-2.0.html

TODO

  • Found out why connection always blocked.
  • Enable and Disable servers
  • 增加dns对外服务?
  • Encapsulate tcp into http.
  • Speed control, low speed go first?

Directories

Path Synopsis
Package lru implements an LRU cache.
Package lru implements an LRU cache.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL