Skip to content

Commit

Permalink
Merge pull request #4 from keminar/todo
Browse files Browse the repository at this point in the history
内网穿透功能
  • Loading branch information
keminar authored Apr 17, 2021
2 parents f68e75d + 22338fe commit 223d2ec
Show file tree
Hide file tree
Showing 32 changed files with 1,775 additions and 218 deletions.
7 changes: 3 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
.history
.vscode
dist/
logs/
anyproxy
anyproxy-alpine
anyproxy-darwin
anyproxy-windows.exe
tunneld
tunneld-alpine
tunnel/tunneld
tunnel/tunneld-alpine
logs/
tunnel/logs/
tunnel/logs/
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

linux:
bash ./scripts/build.sh linux
mac:
bash ./scripts/build.sh mac
windows:
bash ./scripts/build.sh windows
alpine:
bash ./scripts/build.sh alpine
all:
bash ./scripts/build.sh all
clean:
rm -rf dist/*
40 changes: 20 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

anyproxy 是一个部署在Linux系统上的tcp流转发器,可以直接将本地或网络收到的请求发出,也可以将请求转到tunneld或SOCKS或charles等代理。可以代替Proxifier做Linux下的客户端, 也可以配合Proxifier当它的服务端。经过跨平台编译,如果只做网络包的转发可以在windows等平台使用。

[下载Linux包](http://cloudme.io/anyproxy)[下载Mac包](http://cloudme.io/anyproxy-darwin)
[下载Windows包](http://cloudme.io/anyproxy-windows.exe)[下载alpine包](http://cloudme.io/anyproxy-alpine)
[下载Linux包](http://cloudme.io/anyproxy)[下载Mac包](http://cloudme.io/anyproxy-darwin)[下载Windows包](http://cloudme.io/anyproxy-windows.exe)

提醒:请使用浏览器右键的“链接另存为”下载文件

tunneld 是一个anyproxy的服务端,部署在服务器上接收anyproxy的请求,并代理发出请求或是转到下一个tunneld。用于跨内网访问资源使用
tunneld 是一个anyproxy的服务端,部署在服务器上接收anyproxy的请求,并代理发出请求或是转到下一个tunneld。用于跨内网访问资源使用。非anyproxy请求一概拒绝处理

# 路由支持

Expand All @@ -28,26 +27,28 @@ tunneld 是一个anyproxy的服务端,部署在服务器上接收anyproxy的
# or
+----------+ +----------+ +---------+ +---------+ +----------+
| Computer | <==> | anyproxy | <==> | tunneld | <==> | tunneld | <==> | Internet |
| Computer | <==> | anyproxy | <==> | tunneld | <==> | socks5 | <==> | Internet |
+----------+ +----------+ +---------+ +---------+ +----------+
# or
+----------+ +----------+ +---------+ +---------+ +----------+
| Computer | <==> | anyproxy | <==> | tunneld | <==> | socks5 | <==> | Internet |
+----------+ +----------+ +---------+ +---------+ +----------+
+----------+ +---------+ +-----------+ ws +-----------+ +---------+
| Computer | <==> | Nginx A | <==> | anyproxy S| <==> | anyproxy C| <==> | Nginx B |
+----------+ +---------+ +-----------+ +-----------+ +---------+
```

# 使用案例
> 案例1:解决Docker pull官方镜像的问题
`使用iptables将本用户下tcp流转到anyproxy,再进行docker pull操作`

![解决Docker pull问题](examples/docker_pull.png)

> 案例2: 解决相同域名访问网站不同测试环境的问题
`本地通过内网 anyproxy 代理上网,遇到测试服务器域名则跳到外网tunneld转发,网站的nginx根据来源IP进行转发到特定测试环境(有几个环境就需要有几个tunneld服务且IP要不同)`

> 案例3: 解决HTTPS抓包问题
`本地将https请求到服务器,服务器解证书后增加特定头部转到anyproxy websocket服务端,本地另起一个anyproxy的websocket客户端接收并将http请求转发到Charles`

# 源码编译

> 安装Go环境并设置GOPROXY
Expand All @@ -64,7 +65,7 @@ go env -w GOPROXY=https://goproxy.cn,direct
```
git clone https://github.com/keminar/anyproxy.git
cd anyproxy
go build anyproxy.git
make all
```

> 本机启动
Expand All @@ -77,10 +78,10 @@ sudo -u anyproxy ./anyproxy
./anyproxy -daemon
# 示例3. 启动tunneld
./tunneld
./anyproxy -mode tunnel
# 示例4. 启动anyproxy并将请求转给tunneld
./anyproxy -p '127.0.0.1:3001'
./anyproxy -p 'tunnel://127.0.0.1:3001'
# 示例5. 启动anyproxy并将请求转给socks5
./anyproxy -p 'socks5://127.0.0.1:10000'
Expand Down Expand Up @@ -149,7 +150,6 @@ sudo iptables -t nat -D OUTPUT 2

> ~~划线~~ 部分为已实现功能
* ~~可将请求转发到Tunnel服务~~
* 根据CIDR做不同出口请求
* ~~对域名支持加Host绑定~~
* ~~对域名配置请求出口~~
* ~~增加全局默认出口配置~~
Expand All @@ -161,17 +161,17 @@ sudo iptables -t nat -D OUTPUT 2
* ~~可以自定义代理server,如果不可用则用全局的~~
* ~~server多级转发~~
* ~~加域名黑名单功能,不给请求~~
* 请求Body内容体记录, 涉及安全,可能不会实现
* 服务间通信http请求完全加密(header+body)
* HTTPS的SNI的支持?
* ~~支持转发到socket5服务~~
* TCP 增加更多协议解析支持,如rtmp,ftp等
* 与Tunnel的多账户认证,账户可设置有效期
* ~~支持HTTP/1.1 keep-alive 一外链接多次请求不同域名~~
* HTTP/1.1 keep-alive后端也能复用tcp
* ~~修复iptables转发后百度贴吧无法访问的问题~~
* 转发的mysql的连接请求会一直卡住
* ~~支持windows平台使用~~
* ~~通过websocket实现内网穿透(必须为http的非CONNECT请求)~~
* ~~订阅增加邮箱标识,用于辨别在线用户~~
* ~~与Tunnel功能合并,使用mode区分~~
* ~~启用ws-listen后的平滑重启问题~~
* ~~监听配置文件变化重新加载路由~~
* TCP 增加更多协议解析支持,如rtmp,ftp, socks5, https(SNI)等
* TCP 转发的mysql的连接请求会一直卡住

# 感谢

Expand Down
59 changes: 40 additions & 19 deletions anyproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,25 @@ import (
"net/http"
_ "net/http/pprof"
"os"
"strings"
"time"

"github.com/keminar/anyproxy/config"
"github.com/keminar/anyproxy/grace"
"github.com/keminar/anyproxy/logging"
"github.com/keminar/anyproxy/nat"
"github.com/keminar/anyproxy/proto"
"github.com/keminar/anyproxy/utils/conf"
"github.com/keminar/anyproxy/utils/daemon"
"github.com/keminar/anyproxy/utils/help"
"github.com/keminar/anyproxy/utils/tools"
)

var (
gListenAddrPort string
gProxyServerSpec string
gWebsocketListen string
gWebsocketConn string
gMode string
gHelp bool
gDebug int
gPprof string
Expand All @@ -31,10 +36,12 @@ func init() {
flag.Usage = help.Usage
flag.StringVar(&gListenAddrPort, "l", "", "Address and port to listen on")
flag.StringVar(&gProxyServerSpec, "p", "", "Proxy servers to use")
flag.StringVar(&gWebsocketListen, "ws-listen", "", "Websocket address and port to listen on")
flag.StringVar(&gWebsocketConn, "ws-connect", "", "Websocket Address and port to connect")
flag.StringVar(&gMode, "mode", "", "Run mode(proxy, tunnel). proxy mode default")
flag.IntVar(&gDebug, "debug", 0, "debug mode (0, 1, 2)")
flag.StringVar(&gPprof, "pprof", "", "pprof port, disable if empty")
flag.BoolVar(&gHelp, "h", false, "This usage message")

}

func main() {
Expand All @@ -52,20 +59,14 @@ func main() {
}

cmdName := "anyproxy"
logDir := "./logs/"
if conf.RouterConfig.Log.Dir != "" {
logDir = conf.RouterConfig.Log.Dir
}
logDir := config.IfEmptyThen(conf.RouterConfig.Log.Dir, "./logs/", "")
envRunMode := fmt.Sprintf("%s_run_mode", cmdName)
fd := logging.ErrlogFd(logDir, cmdName)
// 是否后台运行
daemon.Daemonize(envRunMode, fd)

gListenAddrPort = config.IfEmptyThen(gListenAddrPort, conf.RouterConfig.Listen, ":3000")
// 支持只输入端口的形式
if !strings.Contains(gListenAddrPort, ":") {
gListenAddrPort = ":" + gListenAddrPort
}
gListenAddrPort = tools.FillPort(gListenAddrPort)
config.SetListenPort(gListenAddrPort)

var writer io.Writer
Expand All @@ -77,23 +78,43 @@ func main() {

logging.SetDefaultLogger(logDir, cmdName, true, 3, writer)
// 设置代理
gProxyServerSpec = config.IfEmptyThen(gProxyServerSpec, conf.RouterConfig.Proxy, "")
gProxyServerSpec = config.IfEmptyThen(gProxyServerSpec, conf.RouterConfig.Default.Proxy, "")
config.SetProxyServer(gProxyServerSpec)

// 调试模式
if len(gPprof) > 0 {
go func() {
// 支持只输入端口的形式
if !strings.Contains(gPprof, ":") {
gPprof = ":" + gPprof
}
gPprof = tools.FillPort(gPprof)
//浏览器访问: http://:5001/debug/pprof/
log.Println("Starting pprof debug server ...")
// 这里不要使用log.Fatal会在平滑重启时导致进程退出
// 因为http server现在没办法加入平滑重启,第一次重启会报端口冲突,可以通过重启两次来启动到pprof
log.Println(http.ListenAndServe(gPprof, nil))
// 因为http server现在没办法一次平滑重启,会报端口冲突,可以通过多次重试来启动pprof
for i := 0; i < 10; i++ {
log.Println(http.ListenAndServe(gPprof, nil))
time.Sleep(10 * time.Second)
}
}()
}
server := grace.NewServer(gListenAddrPort, proto.ClientHandler)
server.ListenAndServe()

// websocket 服务端
gWebsocketListen = config.IfEmptyThen(gWebsocketListen, conf.RouterConfig.Websocket.Listen, "")
if gWebsocketListen != "" {
gWebsocketListen = tools.FillPort(gWebsocketListen)
go nat.NewServer(&gWebsocketListen)
}
// websocket 客户端
gWebsocketConn = config.IfEmptyThen(gWebsocketConn, conf.RouterConfig.Websocket.Connect, "")
if gWebsocketConn != "" {
gWebsocketConn = tools.FillPort(gWebsocketConn)
go nat.ConnectServer(&gWebsocketConn)
}

// 运行模式
if gMode == "tunnel" {
server := grace.NewServer(gListenAddrPort, proto.ServerHandler)
server.ListenAndServe()
} else {
server := grace.NewServer(gListenAddrPort, proto.ClientHandler)
server.ListenAndServe()
}
}
63 changes: 45 additions & 18 deletions conf/router.yaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
log:
dir: ./logs/
# 使用的DNS服务器 local 当前环境, remote远程, 仅当target使用remote有效
dns: local
# 默认环境,local 当前环境, remote 远程, deny 禁止
# auto根据dial选择,local dial失败则remote
target: auto
tcpTarget: remote
# 默认域名比对方案
match: equal
# 全局代理服务器, 优先级低于启动传参
proxy:
# 监听端口IP, 优先级低于启动传参
listen:
# 域名
# 日志目录
log:
dir: ./logs/
# 监听配置文件变化
watcher: true
# anyproxy 和 tunnel通信密钥, 必须16位长度
token: anyproxyproxyany
# 可访问的客户端IP,为空不限制
#allowIP:

# 默认操作,可热加载
default:
# 使用的DNS服务器 local 当前环境, remote远程, 仅当target使用remote有效
dns: local
# 默认环境,local 当前环境, remote 远程, deny 禁止
# auto根据dial选择,local dial失败则remote
target: auto
# tcp 请求环境,local 当前环境, remote 远程, deny 禁止
tcpTarget: remote
# 默认域名比对方案,contain 包含,equal 完全相等, preg 正则
match: equal
# 全局代理服务器, 优先级低于启动传参
proxy:

# 域名,可热加载
hosts:
- name: github
# contain 包含,equal 完全相等, preg 正则
Expand All @@ -21,7 +33,7 @@ hosts:
# 如果有用proxy自定义代理可用,target强制当remote使用,proxy代理不可用,target按原逻辑处理
target: remote
# 参考全局localDns
dns: local
dns: remote
# 支持 http:// , tunnel:// , socks5:// 三种协议,默认 tunnel://
proxy: http://127.0.0.1:8888
- name: golang.org
Expand All @@ -36,7 +48,22 @@ hosts:
target: deny
- name: dev.example.com
ip: 127.0.0.1
# anyproxy 和 tunnel通信密钥, 必须16位长度
#token: anyproxyproxyany
# 可访问的客户端IP,为空不限制
#allowIP:

#websocket配置
websocket:
# 监听端口
listen:
# ip 端口
connect:
# connect 域名
host:
# 用户名
user:
# 密码
pass:
# Email用于定位用户
email:
# 订阅头部信息
subscribe:
- key:
val:
Loading

0 comments on commit 223d2ec

Please sign in to comment.