diff --git a/README.md b/README.md index 1b8662b..a5c3223 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 - SSH中转,HTTP(S),SOCKS5代理支持SSH中转,上级Linux服务器不需要任何服务端,本地一个proxy即可开心上网. - [KCP](https://github.com/xtaci/kcp-go)协议支持,HTTP(S),SOCKS5代理支持KCP协议传输数据,降低延迟,提升浏览体验. - 集成外部API,HTTP(S),SOCKS5代理认证功能可以与外部HTTP API集成,可以方便的通过外部系统控制代理用户. - +- 透明HTTP(S)代理,支持直接把域名解析到proxy监听的ip,然后proxy就会帮你代理访问需要访问的HTTP(S)网站.如果配合iptables,在网关直接把出去的80,443方向的流量转发到proxy,就能实现无感知的智能路由器代理. + ### Why need these? - 当由于某某原因,我们不能访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道访问我们的服务.   - 微信接口本地开发,方便调试. @@ -69,7 +70,9 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 - [1.7.1 用户名和密码的方式](#171-ssh用户名和密码的方式) - [1.7.2 用户名和密钥的方式](#172-ssh用户名和密钥的方式) - [1.8 KCP协议传输](#18kcp协议传输) - - [1.9 查看帮助](#19查看帮助) + - [1.9 HTTP(S)透明代理(域名解析)](#19https透明代理域名解析) + - [1.10 HTTP(S)透明代理(iptables转发)](#110https透明代理iptables转发) + - [1.11 查看帮助](#111查看帮助) - [2. TCP代理](#2tcp代理) - [2.1 普通一级TCP代理](#21普通一级tcp代理) - [2.2 普通二级TCP代理](#22普通二级tcp代理) @@ -122,7 +125,7 @@ curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.s 下载地址:https://github.com/snail007/goproxy/releases ```shell cd /root/proxy/ -wget https://github.com/snail007/goproxy/releases/download/v4.1/proxy-linux-amd64.tar.gz +wget https://github.com/snail007/goproxy/releases/download/v4.2/proxy-linux-amd64.tar.gz ``` #### **2.下载自动安装脚本** ```shell @@ -266,6 +269,66 @@ KCP协议需要-B参数设置一个密码用于加密解密数据 `./proxy http -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" -B mypassword` 那么访问本地的8080端口就是访问VPS上面的代理端口38080,数据通过kcp协议传输. +#### **1.9 HTTP(S)透明代理(域名解析)** +proxy不仅支持在其他软件里面通过设置代理的方式,为其他软件提供代理服务,而且支持直接把请求的网站域名解析到proxy监听的ip上,然后proxy监听80和443端口,那么proxy就会自动为你代理访问需要访问的HTTP(S)网站. + +使用方式: +在"最后一级proxy代理"的机器上,因为proxy要伪装成所有网站,网站默认的端口HTTP是80,HTTPS是443,让proxy监听80和443端口即可.参数-p多个地址用逗号分割. +`./proxy http -t tcp -p :80,:443` + +这个命令就在机器上启动了一个proxy代理,同时监听80和443端口,既可以当作普通的代理使用,也可以直接把需要代理的域名解析到这个机器的IP上. + +如果有上级代理那么参照上面教程设置上级即可,使用方式完全一样. +`./proxy http -t tcp -p :80,:443 -T tls -P "2.2.2.2:33080" -C proxy.crt -K proxy.key` + +注意: +proxy所在的服务器的DNS解析结果不能受到自定义的解析影响,不然就死循环了. + +#### **1.10 HTTP(S)透明代理(iptables转发)** +该模式需要具有一定的网络基础,相关概念不懂的请自行搜索解决. +假设proxy现在在路由器上运行,启动命令如下: +`./proxy http -t tcp -p :33080 -T tls -P "2.2.2.2:33090" -C proxy.crt -K proxy.key` + +然后添加iptables规则,下面是参考规则: +```shell +#上级proxy服务端服务器IP地址: +proxy_server_ip=2.2.2.2 + +#路由器运行proxy监听的端口: +proxy_local_port=33080 + +#下面的就不用修改了 +#create a new chain named PROXY +iptables -t nat -N PROXY + +# Ignore your PROXY server's addresses +# It's very IMPORTANT, just be careful. + +iptables -t nat -A PROXY -d $proxy_server_ip -j RETURN + +# Ignore LANs IP address +iptables -t nat -A PROXY -d 0.0.0.0/8 -j RETURN +iptables -t nat -A PROXY -d 10.0.0.0/8 -j RETURN +iptables -t nat -A PROXY -d 127.0.0.0/8 -j RETURN +iptables -t nat -A PROXY -d 169.254.0.0/16 -j RETURN +iptables -t nat -A PROXY -d 172.16.0.0/12 -j RETURN +iptables -t nat -A PROXY -d 192.168.0.0/16 -j RETURN +iptables -t nat -A PROXY -d 224.0.0.0/4 -j RETURN +iptables -t nat -A PROXY -d 240.0.0.0/4 -j RETURN + +# Anything to port 80 443 should be redirected to PROXY's local port +iptables -t nat -A PROXY -p tcp --dport 80 -j REDIRECT --to-ports $proxy_local_port +iptables -t nat -A PROXY -p tcp --dport 443 -j REDIRECT --to-ports $proxy_local_port + +# Apply the rules to nat client +iptables -t nat -A PREROUTING -p tcp -j PROXY +# Apply the rules to localhost +iptables -t nat -A OUTPUT -p tcp -j PROXY +``` +- 清空整个链 iptables -F 链名比如iptables -t nat -F SHADOWSOCKS +- 删除指定的用户自定义链 iptables -X 链名 比如 iptables -t nat -X SHADOWSOCKS +- 从所选链中删除规则 iptables -D 链名 规则详情 比如 iptables -t nat -D SHADOWSOCKS -d 223.223.192.0/255.255.240.0 -j RETURN + #### **1.9.查看帮助** `./proxy help http` diff --git a/config.go b/config.go index 7b1c246..d28b7eb 100755 --- a/config.go +++ b/config.go @@ -64,7 +64,7 @@ func initConfig() (err error) { httpArgs.Auth = http.Flag("auth", "http basic auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings() httpArgs.PoolSize = http.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int() httpArgs.CheckParentInterval = http.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int() - httpArgs.Local = http.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() + httpArgs.Local = http.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() httpArgs.SSHUser = http.Flag("ssh-user", "user for ssh").Short('u').Default("").String() httpArgs.SSHKeyFile = http.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String() httpArgs.SSHKeyFileSalt = http.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String() diff --git a/install_auto.sh b/install_auto.sh index 26790e7..fb1e1d8 100755 --- a/install_auto.sh +++ b/install_auto.sh @@ -5,7 +5,7 @@ if [ -e /tmp/proxy ]; then fi mkdir /tmp/proxy cd /tmp/proxy -wget https://github.com/snail007/goproxy/releases/download/v4.1/proxy-linux-amd64.tar.gz +wget https://github.com/snail007/goproxy/releases/download/v4.2/proxy-linux-amd64.tar.gz # #install proxy tar zxvf proxy-linux-amd64.tar.gz diff --git a/main.go b/main.go index 77a063a..f25de23 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,7 @@ import ( "syscall" ) -const APP_VERSION = "4.1" +const APP_VERSION = "4.2" func main() { err := initConfig() diff --git a/release.sh b/release.sh index eaea9dd..1c1c3a0 100755 --- a/release.sh +++ b/release.sh @@ -1,5 +1,5 @@ #!/bin/bash -VER="4.1" +VER="4.2" RELEASE="release-${VER}" rm -rf .cert mkdir .cert diff --git a/services/http.go b/services/http.go index a9d1caa..91b971d 100644 --- a/services/http.go +++ b/services/http.go @@ -9,6 +9,7 @@ import ( "proxy/utils" "runtime/debug" "strconv" + "strings" "time" "golang.org/x/crypto/ssh" @@ -115,20 +116,24 @@ func (s *HTTP) Start(args interface{}) (err error) { s.InitOutConnPool() } s.InitService() - host, port, _ := net.SplitHostPort(*s.cfg.Local) - p, _ := strconv.Atoi(port) - sc := utils.NewServerChannel(host, p) - if *s.cfg.LocalType == TYPE_TCP { - err = sc.ListenTCP(s.callback) - } else if *s.cfg.LocalType == TYPE_TLS { - err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback) - } else if *s.cfg.LocalType == TYPE_KCP { - err = sc.ListenKCP(*s.cfg.KCPMethod, *s.cfg.KCPKey, s.callback) + for _, addr := range strings.Split(*s.cfg.Local, ",") { + if addr != "" { + host, port, _ := net.SplitHostPort(addr) + p, _ := strconv.Atoi(port) + sc := utils.NewServerChannel(host, p) + if *s.cfg.LocalType == TYPE_TCP { + err = sc.ListenTCP(s.callback) + } else if *s.cfg.LocalType == TYPE_TLS { + err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback) + } else if *s.cfg.LocalType == TYPE_KCP { + err = sc.ListenKCP(*s.cfg.KCPMethod, *s.cfg.KCPKey, s.callback) + } + if err != nil { + return + } + log.Printf("%s http(s) proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr()) + } } - if err != nil { - return - } - log.Printf("%s http(s) proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr()) return } diff --git a/utils/structs.go b/utils/structs.go index 4fefd76..907eba6 100644 --- a/utils/structs.go +++ b/utils/structs.go @@ -11,6 +11,7 @@ import ( "log" "net" "net/url" + "proxy/utils/sni" "strings" "sync" "time" @@ -306,13 +307,22 @@ func NewHTTPRequest(inConn *net.Conn, bufSize int, isBasicAuth bool, basicAuth * return } req.HeadBuf = buf[:len] - index := bytes.IndexByte(req.HeadBuf, '\n') - if index == -1 { - err = fmt.Errorf("http decoder data line err:%s", SubStr(string(req.HeadBuf), 0, 50)) - CloseConn(inConn) - return + //try sni + serverName, err0 := sni.ServerNameFromBytes(req.HeadBuf) + if err0 == nil { + //sni success + req.Method = "SNI" + req.hostOrURL = "https://" + serverName + ":443" + } else { + //sni fail , try http + index := bytes.IndexByte(req.HeadBuf, '\n') + if index == -1 { + err = fmt.Errorf("http decoder data line err:%s", SubStr(string(req.HeadBuf), 0, 50)) + CloseConn(inConn) + return + } + fmt.Sscanf(string(req.HeadBuf[:index]), "%s%s", &req.Method, &req.hostOrURL) } - fmt.Sscanf(string(req.HeadBuf[:index]), "%s%s", &req.Method, &req.hostOrURL) if req.Method == "" || req.hostOrURL == "" { err = fmt.Errorf("http decoder data err:%s", SubStr(string(req.HeadBuf), 0, 50)) CloseConn(inConn)