From fad7cf0206e54ee701bfa58fb06130d4aab9a775 Mon Sep 17 00:00:00 2001 From: mritd Date: Fri, 7 Aug 2020 18:11:44 +0800 Subject: [PATCH] feat(client): support https proxy support https proxy use the built-in basic auth extension ref tobyxdd/hysteria#14 tobyxdd/hysteria#15 Signed-off-by: mritd --- cmd/proxy_client.go | 45 ++++++++++----------------------------------- cmd/proxy_config.go | 2 ++ go.mod | 1 + pkg/http/server.go | 11 ++++++++--- 4 files changed, 21 insertions(+), 38 deletions(-) diff --git a/cmd/proxy_client.go b/cmd/proxy_client.go index 6753a31..770c72c 100644 --- a/cmd/proxy_client.go +++ b/cmd/proxy_client.go @@ -3,14 +3,11 @@ package main import ( "crypto/tls" "crypto/x509" - "encoding/base64" "io/ioutil" "net" "net/http" - "strings" "time" - "github.com/elazarl/goproxy" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/congestion" "github.com/sirupsen/logrus" @@ -157,44 +154,22 @@ func proxyClient(args []string) { "dst": reqAddr, }).Debug("New HTTP request") }, - func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + func(user, password string) bool { if config.HTTPUser == "" || config.HTTPPassword == "" { - return req, nil + return true } - - resp := goproxy.NewResponse(req, goproxy.ContentTypeText, http.StatusUnauthorized, "401 - Forbidden: Unauthorized") - // RFC7617 section 2.1 - pa := req.Header.Get("Proxy-Authorization") - if pa == "" { - return req, resp - } - authStr := strings.Fields(pa) - if len(authStr) != 2 || authStr[0] != "Basic" { - return req, resp - } - decodeBytes, err := base64.StdEncoding.DecodeString(authStr[1]) - if err != nil { - logrus.WithFields(logrus.Fields{ - "error": err, - "cred": authStr[1], - }).Debug("Failed to decode base64 auth string") - return req, resp - } - userAndPassword := strings.Split(string(decodeBytes), ":") - if len(userAndPassword) != 2 { - return req, resp - } - if userAndPassword[0] != config.HTTPUser || userAndPassword[1] != config.HTTPPassword { - return req, resp - } - - return req, nil + return config.HTTPUser == user && config.HTTPPassword == password }) if err != nil { logrus.WithField("error", err).Fatal("HTTP server initialization failed") } - logrus.WithField("addr", config.HTTPAddr).Info("HTTP server up and running") - errChan <- http.ListenAndServe(config.HTTPAddr, proxy) + if config.HTTPSCert != "" && config.HTTPSKey != "" { + logrus.WithField("addr", config.HTTPAddr).Info("HTTPS server up and running") + errChan <- http.ListenAndServeTLS(config.HTTPAddr, config.HTTPSCert, config.HTTPSKey, proxy) + } else { + logrus.WithField("addr", config.HTTPAddr).Info("HTTP server up and running") + errChan <- http.ListenAndServe(config.HTTPAddr, proxy) + } }() } diff --git a/cmd/proxy_config.go b/cmd/proxy_config.go index 7793ae3..552fb36 100644 --- a/cmd/proxy_config.go +++ b/cmd/proxy_config.go @@ -15,6 +15,8 @@ type proxyClientConfig struct { HTTPTimeout int `json:"http_timeout" desc:"HTTP connection timeout in seconds"` HTTPUser string `json:"http_user" desc:"HTTP basic auth username"` HTTPPassword string `json:"http_password" desc:"HTTP basic auth password"` + HTTPSCert string `json:"https_cert" desc:"HTTPS cert file"` + HTTPSKey string `json:"https_key" desc:"HTTPS cert key"` ACLFile string `json:"acl" desc:"Access control list"` ServerAddr string `json:"server" desc:"Server address"` Username string `json:"username" desc:"Authentication username"` diff --git a/go.mod b/go.mod index 1705adf..6fa4044 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.14 require ( github.com/elazarl/goproxy v0.0.0-20200426045556-49ad98f6dac1 + github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 github.com/golang/protobuf v1.4.0 github.com/hashicorp/golang-lru v0.5.4 github.com/lucas-clemente/quic-go v0.16.1 diff --git a/pkg/http/server.go b/pkg/http/server.go index fbc3666..dc64a04 100644 --- a/pkg/http/server.go +++ b/pkg/http/server.go @@ -1,12 +1,15 @@ package http import ( + "context" "errors" "fmt" "net" "net/http" "time" + "github.com/elazarl/goproxy/ext/auth" + "github.com/elazarl/goproxy" "github.com/tobyxdd/hysteria/pkg/acl" "github.com/tobyxdd/hysteria/pkg/core" @@ -14,13 +17,12 @@ import ( func NewProxyHTTPServer(hyClient core.Client, idleTimeout time.Duration, aclEngine *acl.Engine, newDialFunc func(reqAddr string, action acl.Action, arg string), - basicAuthFunc func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response)) (*goproxy.ProxyHttpServer, error) { + basicAuthFunc func(user, password string) bool) (*goproxy.ProxyHttpServer, error) { proxy := goproxy.NewProxyHttpServer() proxy.Logger = &nopLogger{} proxy.NonproxyHandler = http.NotFoundHandler() - proxy.OnRequest().DoFunc(basicAuthFunc) proxy.Tr = &http.Transport{ - Dial: func(network, addr string) (net.Conn, error) { + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { // Parse addr string host, port, err := net.SplitHostPort(addr) if err != nil { @@ -51,8 +53,11 @@ func NewProxyHTTPServer(hyClient core.Client, idleTimeout time.Duration, aclEngi } }, IdleConnTimeout: idleTimeout, + // TODO: Disable HTTP2 support? ref: https://github.com/elazarl/goproxy/issues/361 + //TLSNextProto: make(map[string]func(authority string, c *tls.Conn) http.RoundTripper), } proxy.ConnectDial = nil + auth.ProxyBasic(proxy, "hysteria client", basicAuthFunc) return proxy, nil }