add docker support

Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
This commit is contained in:
arraykeys@gmail.com
2018-05-15 10:56:57 +08:00
parent bffd5891cc
commit 53df3b5578
10 changed files with 168 additions and 44 deletions

View File

@ -3,7 +3,7 @@ v4.8
1.优化了SPS连接HTTP上级的指令,避免了某些代理不响应的问题.
2.SPS功能增加了参数:
--disable-http:禁用http(s)代理
--disable-socks:禁用socks代理.
--disable-socks:禁用socks代理
默认都是false(开启).
3.重构了部分代码的日志部分,保证了日志按着预期输出.

View File

@ -62,6 +62,7 @@ This page is the v4.7 manual, and the other version of the manual can be checked
### Installation
- [Quick installation](#quick-installation)
- [Manual installation](#manual-installation)
- [Docker installation](#docker-installation)
### First use must read
- [Environmental Science](#environmental-science)
@ -170,6 +171,36 @@ chmod +x install.sh
./install.sh
```
#### Docker installation
Dockerfile root of project uses multistage build and alpine project to comply with best practices. Uses golang 1.8.5 for building as noted in the project README.md and will be pretty small image. total extracted size will be 17.3MB for goproxy version 4.7.
The default build process builds the master branch (latest commits/ cutting edge), and it can be configured to build specific version, just edit Dockerfile before build, following builds release version 4.7:
```
ARG GOPROXY_VERSION=v4.7
```
To Run:
1. Clone the repository and cd into it.
```
sudo docker build .
```
2. Tag the image:
```
sudo docker tag <id from previous step> goproxy/goproxy:latest
```
3. Run!
Just put your arguments to proxy binary in the OPTS environmental variable (this is just a sample http proxy):
```
sudo docker run -d --restart=always --name goproxy -e OPTS="http -p :33080" -p 33080:33080 goproxy/goproxy:latest
```
4. View logs:
```
sudo docker logs -f goproxy
```
## **First use must be read**  
### **Environmental Science**
@ -967,7 +998,7 @@ If you want to get a more detailed configuration and explanation of the KCP para
- HTTP (s) proxy support PAC?
- Welcome joining group feedback...
### How to contribute to the code?
### How to contribute to the code (Pull Request)?
First, you need to clone the project to your account, and then modify the code on the dev branch.
Finally, Pull Request to dev branch of goproxy project, and contribute code for efficiency.
PR needs to explain what changes have been made and why you change them.

View File

@ -61,6 +61,7 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
### 安装
1. [快速安装](#自动安装)
1. [手动安装](#手动安装)
1. [Docker安装](#docker安装)
### 首次使用必看
- [环境](#首次使用必看-1)
@ -169,6 +170,35 @@ chmod +x install.sh
./install.sh
```
#### Docker安装
项目根目录的Dockerfile文件用来构建,使用golang 1.8.5,构建基于goproxy v4.7,
全部大小17.3MB,默认情况下使用master分支,不过可以通过修改配置文件Dockerfile,指定构建的goproxy版本.
```
ARG GOPROXY_VERSION=v4.7
```
步骤:
1. 克隆仓库,然后cd进入仓库文件夹,执行:
```
sudo docker build .
```
2. 镜像打标签:
```
sudo docker tag <上一步的结果ID> goproxy/goproxy:latest
```
3. 运行
参数OPTS的值就是传递给proxy的所有参数
比如下面的例子启动了一个http服务:
```
sudo docker run -d --restart=always --name goproxy -e OPTS="http -p :33080" -p 33080:33080 goproxy/goproxy:latest
```
4. 查看日志:
```
sudo docker logs -f goproxy
```
## **首次使用必看**
### **环境**
@ -1012,7 +1042,7 @@ fast3`--nodelay=1 --interval=10 --resend=2 --nc=1`
- http(s)代理增加pac支持?
- 欢迎加群反馈...
### 如何贡献代码?
### 如何贡献代码(Pull Request)?
首先需要clone本项目到自己的帐号下面,然后在dev分支上面修改代码,
最后发Pull Request到goproxy项目的dev分支即可,为了高效贡献代码,
pr的时候需要说明做了什么变更,原因是什么.

View File

@ -227,7 +227,7 @@ func initConfig() (err error) {
spsArgs.ParentType = sps.Flag("parent-type", "parent protocol type <tls|tcp|kcp>").Short('T').Enum("tls", "tcp", "kcp")
spsArgs.LocalType = sps.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp")
spsArgs.Local = sps.Flag("local", "local ip:port to listen,multiple address use comma split,such as: 0.0.0.0:80,0.0.0.0:443").Short('p').Default(":33080").String()
spsArgs.ParentServiceType = sps.Flag("parent-service-type", "parent service type <http|socks>").Short('S').Enum("http", "socks")
spsArgs.ParentServiceType = sps.Flag("parent-service-type", "parent service type <http|socks|ss>").Short('S').Enum("http", "socks", "ss")
spsArgs.DNSAddress = sps.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String()
spsArgs.DNSTTL = sps.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
spsArgs.AuthFile = sps.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
@ -244,6 +244,8 @@ func initConfig() (err error) {
spsArgs.ParentCompress = sps.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
spsArgs.SSMethod = sps.Flag("ss-method", "").Hidden().Short('h').Default("aes-256-cfb").String()
spsArgs.SSKey = sps.Flag("ss-key", "").Hidden().Short('j').Default("sspassword").String()
spsArgs.ParentSSMethod = sps.Flag("parent-ss-method", "").Hidden().Short('H').Default("aes-256-cfb").String()
spsArgs.ParentSSKey = sps.Flag("parent-ss-key", "").Hidden().Short('J').Default("sspassword").String()
spsArgs.DisableHTTP = sps.Flag("disable-http", "disable http(s) proxy").Default("false").Bool()
spsArgs.DisableSocks5 = sps.Flag("disable-socks", "disable socks proxy").Default("false").Bool()
spsArgs.DisableSS = sps.Flag("disable-ss", "").Hidden().Default("false").Bool()

View File

@ -234,6 +234,8 @@ type SPSArgs struct {
ParentCompress *bool
SSMethod *string
SSKey *string
ParentSSMethod *string
ParentSSKey *string
DisableHTTP *bool
DisableSocks5 *bool
DisableSS *bool

View File

@ -309,7 +309,12 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
} else {
//https或者http,上级是代理,proxy需要转发
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
//直连目标或上级非代理,清理HTTP头部的代理头信息
if !useProxy || *s.cfg.ParentType == "ssh" {
_, err = outConn.Write(utils.RemoveProxyHeaders(req.HeadBuf))
} else {
_, err = outConn.Write(req.HeadBuf)
}
outConn.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("write to %s , err:%s", *s.cfg.Parent, err)

View File

@ -27,7 +27,8 @@ type SPS struct {
serverChannels []*utils.ServerChannel
userConns utils.ConcurrentMap
log *logger.Logger
cipher *ss.Cipher
localCipher *ss.Cipher
parentCipher *ss.Cipher
}
func NewSPS() Service {
@ -48,6 +49,10 @@ func (s *SPS) CheckArgs() (err error) {
err = fmt.Errorf("parent type unkown,use -T <tls|tcp|kcp>")
return
}
if *s.cfg.ParentType == "ss" && (*s.cfg.ParentSSKey == "" || *s.cfg.ParentSSMethod == "") {
err = fmt.Errorf("ss parent need a ss key, set it by : -J <sskey>")
return
}
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.LocalType == TYPE_TLS {
s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
if err != nil {
@ -70,7 +75,14 @@ func (s *SPS) InitService() (err error) {
}
err = s.InitBasicAuth()
if *s.cfg.SSMethod != "" && *s.cfg.SSKey != "" {
s.cipher, err = ss.NewCipher(*s.cfg.SSMethod, *s.cfg.SSKey)
s.localCipher, err = ss.NewCipher(*s.cfg.SSMethod, *s.cfg.SSKey)
if err != nil {
s.log.Printf("error generating cipher : %s", err)
return
}
}
if *s.cfg.ParentServiceType == "ss" {
s.parentCipher, err = ss.NewCipher(*s.cfg.ParentSSMethod, *s.cfg.ParentSSKey)
if err != nil {
s.log.Printf("error generating cipher : %s", err)
return
@ -252,7 +264,7 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
//s.log.Printf("https reply: %s", request.Host)
} else {
//forwardBytes = bytes.TrimRight(request.HeadBuf,"\r\n")
forwardBytes = bytes.TrimRight(request.HeadBuf,"\r\n")
forwardBytes = request.HeadBuf
}
address = request.Host
var userpass string
@ -273,7 +285,7 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
return
}
(*inConn).SetDeadline(time.Now().Add(time.Second * 5))
ssConn := ss.NewConn(*inConn, s.cipher.Copy())
ssConn := ss.NewConn(*inConn, s.localCipher.Copy())
address, err = ss.GetRequest(ssConn)
(*inConn).SetDeadline(time.Time{})
if err != nil {
@ -309,20 +321,23 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
Password: *s.cfg.ParentKey,
})
}
if *s.cfg.ParentAuth != "" || *s.cfg.ParentSSKey != "" || s.IsBasicAuth() {
forwardBytes = utils.RemoveProxyHeaders(forwardBytes)
}
//ask parent for connect to target address
if *s.cfg.ParentServiceType == "http" {
//http parent
isHTTPS := false
pb := new(bytes.Buffer)
if len(forwardBytes) == 0 {
isHTTPS = true
pb.Write([]byte(fmt.Sprintf("CONNECT %s HTTP/1.1", address)))
pb.Write([]byte(fmt.Sprintf("CONNECT %s HTTP/1.1\r\n", address)))
}
pb.WriteString("\r\nProxy-Connection: Keep-Alive\r\n")
pb.WriteString("Proxy-Connection: Keep-Alive\r\n")
s.log.Printf("before bytes:%s",string(forwardBytes))
//Proxy-Authorization:\r\n
u := ""
if *s.cfg.ParentAuth != "" {
a := strings.Split(*s.cfg.ParentAuth, ":")
@ -343,15 +358,9 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
if isHTTPS {
pb.Write([]byte("\r\n"))
} else {
//do remove some headers with forwardBytes
//forwardBytes
forwardBytes=bytes.Replace(forwardBytes,[]byte("\r\n"),[]byte(pb.Bytes()),1)
forwardBytes = utils.InsertProxyHeaders(forwardBytes, string(pb.Bytes()))
pb.Reset()
pb.Write(forwardBytes)
if !bytes.Contains(forwardBytes,[]byte("\r\n\r\n\r\n")){
pb.Write([]byte("\r\n\r\n"))
}
forwardBytes = nil
}
@ -365,9 +374,6 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
return
}
s.log.Printf("ishttps:%v",isHTTPS)
s.log.Printf("bytes:%s",string(pb.Bytes()))
if isHTTPS {
reply := make([]byte, 1024)
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
@ -381,7 +387,7 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
}
//s.log.Printf("reply: %s", string(reply[:n]))
}
} else {
} else if *s.cfg.ParentServiceType == "socks" {
s.log.Printf("connect %s", address)
//socks client
var clientConn *socks.ClientConn
@ -402,11 +408,25 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
if err = clientConn.Handshake(); err != nil {
return
}
} else if *s.cfg.ParentServiceType == "ss" {
ra, e := ss.RawAddr(address)
if e != nil {
err = fmt.Errorf("build ss raw addr fail, err: %s", e)
return
}
outConn, err = ss.DialWithRawAddr(&outConn, ra, "", s.parentCipher.Copy())
if err != nil {
err = fmt.Errorf("dial ss parent fail, err : %s", err)
return
}
}
//forward client data to target,if necessary.
if len(forwardBytes) > 0 {
outConn.Write(forwardBytes)
}
//bind
inAddr := (*inConn).RemoteAddr().String()
outAddr := outConn.RemoteAddr().String()

View File

@ -621,6 +621,33 @@ func IsSocks5(head []byte) bool {
}
return false
}
func RemoveProxyHeaders(head []byte) []byte {
newLines := [][]byte{}
var keys = map[string]bool{}
lines := bytes.Split(head, []byte("\r\n"))
IsBody := false
for _, line := range lines {
if len(line) == 0 || IsBody {
newLines = append(newLines, line)
IsBody = true
} else {
hline := bytes.SplitN(line, []byte(":"), 2)
if len(hline) != 2 {
continue
}
k := strings.ToUpper(string(hline[0]))
if _, ok := keys[k]; ok || strings.HasPrefix(k, "PROXY-") {
continue
}
keys[k] = true
newLines = append(newLines, line)
}
}
return bytes.Join(newLines, []byte("\r\n"))
}
func InsertProxyHeaders(head []byte, headers string) []byte {
return bytes.Replace(head, []byte("\r\n"), []byte("\r\n"+headers), 1)
}
// type sockaddr struct {
// family uint16

View File

@ -58,12 +58,19 @@ func RawAddr(addr string) (buf []byte, err error) {
// This is intended for use by users implementing a local socks proxy.
// rawaddr shoud contain part of the data in socks request, starting from the
// ATYP field. (Refer to rfc1928 for more information.)
func DialWithRawAddr(rawaddr []byte, server string, cipher *Cipher) (c *Conn, err error) {
conn, err := net.Dial("tcp", server)
func DialWithRawAddr(rawConn *net.Conn, rawaddr []byte, server string, cipher *Cipher) (c *Conn, err error) {
var conn net.Conn
if rawConn == nil {
conn, err = net.Dial("tcp", server)
}
if err != nil {
return
}
if rawConn != nil {
c = NewConn(*rawConn, cipher)
} else {
c = NewConn(conn, cipher)
}
if cipher.ota {
if c.enc == nil {
if _, err = c.initEncrypt(); err != nil {
@ -88,7 +95,7 @@ func Dial(addr, server string, cipher *Cipher) (c *Conn, err error) {
if err != nil {
return
}
return DialWithRawAddr(ra, server, cipher)
return DialWithRawAddr(nil, ra, server, cipher)
}
func (c *Conn) GetIv() (iv []byte) {

View File

@ -486,10 +486,10 @@ func (req *HTTPRequest) getHeader(key string) (val string) {
lines := strings.Split(string(req.HeadBuf), "\r\n")
//log.Println(lines)
for _, line := range lines {
line := strings.SplitN(strings.Trim(line, "\r\n "), ":", 2)
if len(line) == 2 {
k := strings.ToUpper(strings.Trim(line[0], " "))
v := strings.Trim(line[1], " ")
hline := strings.SplitN(strings.Trim(line, "\r\n "), ":", 2)
if len(hline) == 2 {
k := strings.ToUpper(strings.Trim(hline[0], " "))
v := strings.Trim(hline[1], " ")
if key == k {
val = v
return