diff --git a/auth/auth.go b/auth/auth.go index 0f9b16b..9209642 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -11,6 +11,7 @@ import ( "github.com/go-gost/core/auth" "github.com/go-gost/core/logger" "github.com/go-gost/x/internal/loader" + xlogger "github.com/go-gost/x/logger" ) type options struct { @@ -74,6 +75,9 @@ func NewAuthenticator(opts ...Option) auth.Authenticator { for _, opt := range opts { opt(&options) } + if options.logger == nil { + options.logger = xlogger.Nop() + } ctx, cancel := context.WithCancel(context.TODO()) p := &authenticator{ diff --git a/config/config.go b/config/config.go index f8f6e32..c86eda9 100644 --- a/config/config.go +++ b/config/config.go @@ -288,6 +288,7 @@ type ForwardNodeConfig struct { Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"` HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"` TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"` + Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"` } type HTTPNodeConfig struct { @@ -382,6 +383,7 @@ type NodeConfig struct { Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"` HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"` TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"` + Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"` } type Config struct { diff --git a/config/parsing/chain.go b/config/parsing/chain.go index 41f4a04..1dcea76 100644 --- a/config/parsing/chain.go +++ b/config/parsing/chain.go @@ -12,6 +12,7 @@ import ( "github.com/go-gost/core/logger" "github.com/go-gost/core/metadata" mdutil "github.com/go-gost/core/metadata/util" + auther "github.com/go-gost/x/auth" xchain "github.com/go-gost/x/chain" "github.com/go-gost/x/config" tls_util "github.com/go-gost/x/internal/util/tls" @@ -231,6 +232,19 @@ func ParseHop(cfg *config.HopConfig) (chain.Hop, error) { Secure: v.TLS.Secure, })) } + if v.Auth != nil { + opts = append(opts, chain.AutherNodeOption( + auther.NewAuthenticator( + auther.AuthsOption(map[string]string{v.Auth.Username: v.Auth.Password}), + auther.LoggerOption(logger.Default().WithFields(map[string]any{ + "kind": "node", + "node": v.Name, + "addr": v.Addr, + "host": v.Host, + "protocol": v.Protocol, + })), + ))) + } node := chain.NewNode(v.Name, v.Addr, opts...) nodes = append(nodes, node) } diff --git a/config/parsing/service.go b/config/parsing/service.go index 2e77f80..4852170 100644 --- a/config/parsing/service.go +++ b/config/parsing/service.go @@ -259,6 +259,7 @@ func parseForwarder(cfg *config.ForwarderConfig) (chain.Hop, error) { Bypasses: node.Bypasses, HTTP: node.HTTP, TLS: node.TLS, + Auth: node.Auth, }, ) } diff --git a/go.mod b/go.mod index fe8a165..c02bc3b 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/gin-contrib/cors v1.3.1 github.com/gin-gonic/gin v1.8.2 - github.com/go-gost/core v0.0.0-20230129124513-1a643651c025 + github.com/go-gost/core v0.0.0-20230131053758-ff3b77ac2899 github.com/go-gost/gosocks4 v0.0.1 github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 github.com/go-gost/relay v0.3.1 diff --git a/go.sum b/go.sum index b53aa77..bbde453 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8n github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gost/core v0.0.0-20230129124513-1a643651c025 h1:3UdtINVrlGIulMyTrygas1uYMc/az6VivtzosP8FTmg= -github.com/go-gost/core v0.0.0-20230129124513-1a643651c025/go.mod h1:R08B7BVdhWsYHX8s7wkEBpeKqc4+YFP6bLLFoao0J/A= +github.com/go-gost/core v0.0.0-20230131053758-ff3b77ac2899 h1:Ofa8D9NViX+biyS3oUPgIzAjYD6i71abRGalX/0Den4= +github.com/go-gost/core v0.0.0-20230131053758-ff3b77ac2899/go.mod h1:R08B7BVdhWsYHX8s7wkEBpeKqc4+YFP6bLLFoao0J/A= github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s= github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc= github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgOuJkQ7QLfG0Hs5peWIUSysCDNz4pfe04= diff --git a/handler/forward/local/handler.go b/handler/forward/local/handler.go index 690d8fb..9d5cf3f 100644 --- a/handler/forward/local/handler.go +++ b/handler/forward/local/handler.go @@ -123,7 +123,9 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand } log = log.WithFields(map[string]any{ - "dst": fmt.Sprintf("%s/%s", target.Addr, network), + "host": host, + "node": target.Name, + "dst": fmt.Sprintf("%s/%s", target.Addr, network), }) log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr) @@ -157,13 +159,13 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l br := bufio.NewReader(rw) var connPool sync.Map - resp := &http.Response{ - ProtoMajor: 1, - ProtoMinor: 1, - StatusCode: http.StatusServiceUnavailable, - } - for { + resp := &http.Response{ + ProtoMajor: 1, + ProtoMinor: 1, + StatusCode: http.StatusServiceUnavailable, + } + err = func() error { req, err := http.ReadRequest(br) if err != nil { @@ -183,11 +185,21 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l } log = log.WithFields(map[string]any{ - "dst": target.Addr, + "host": req.Host, + "node": target.Name, + "dst": target.Addr, }) log.Debugf("find node for host %s -> %s(%s)", req.Host, target.Name, target.Addr) - // log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr) + if auther := target.Options().Auther; auther != nil { + username, password, _ := req.BasicAuth() + if !auther.Authenticate(username, password) { + resp.StatusCode = http.StatusUnauthorized + resp.Header.Set("WWW-Authenticate", "Basic") + log.Warnf("node %s(%s) 401 unauthorized", target.Name, target.Addr) + return resp.Write(rw) + } + } var cc net.Conn if v, ok := connPool.Load(target); ok { @@ -202,7 +214,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l if marker := target.Marker(); marker != nil { marker.Mark() } - log.Warnf("connect to node %s(%s) failed", target.Name, target.Addr) + log.Warnf("connect to node %s(%s) failed: %v", target.Name, target.Addr, err) return resp.Write(rw) } if marker := target.Marker(); marker != nil { @@ -221,7 +233,8 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l go func() { defer cc.Close() - xnet.CopyBuffer(rw, cc, 8192) + err := xnet.CopyBuffer(rw, cc, 8192) + log.Debugf("close connection to node %s(%s), reason: %v", target.Name, target.Addr, err) connPool.Delete(target) }() } @@ -240,6 +253,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l log.Trace(string(dump)) } if err := req.Write(cc); err != nil { + log.Warnf("send request to node %s(%s) failed: %v", target.Name, target.Addr, err) return resp.Write(rw) } diff --git a/handler/forward/remote/handler.go b/handler/forward/remote/handler.go index 04f320b..a7563ef 100644 --- a/handler/forward/remote/handler.go +++ b/handler/forward/remote/handler.go @@ -122,7 +122,9 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand } log = log.WithFields(map[string]any{ - "dst": fmt.Sprintf("%s/%s", target.Addr, network), + "host": host, + "node": target.Name, + "dst": fmt.Sprintf("%s/%s", target.Addr, network), }) log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr) @@ -156,13 +158,14 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l br := bufio.NewReader(rw) var connPool sync.Map - resp := &http.Response{ - ProtoMajor: 1, - ProtoMinor: 1, - StatusCode: http.StatusServiceUnavailable, - } - for { + resp := &http.Response{ + ProtoMajor: 1, + ProtoMinor: 1, + Header: http.Header{}, + StatusCode: http.StatusServiceUnavailable, + } + err = func() error { req, err := http.ReadRequest(br) if err != nil { @@ -182,11 +185,21 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l } log = log.WithFields(map[string]any{ - "dst": target.Addr, + "host": req.Host, + "node": target.Name, + "dst": target.Addr, }) log.Debugf("find node for host %s -> %s(%s)", req.Host, target.Name, target.Addr) - // log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr) + if auther := target.Options().Auther; auther != nil { + username, password, _ := req.BasicAuth() + if !auther.Authenticate(username, password) { + resp.StatusCode = http.StatusUnauthorized + resp.Header.Set("WWW-Authenticate", "Basic") + log.Warnf("node %s(%s) 401 unauthorized", target.Name, target.Addr) + return resp.Write(rw) + } + } var cc net.Conn if v, ok := connPool.Load(target); ok { @@ -201,7 +214,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l if marker := target.Marker(); marker != nil { marker.Mark() } - log.Warnf("connect to node %s(%s) failed", target.Name, target.Addr) + log.Warnf("connect to node %s(%s) failed: %v", target.Name, target.Addr, err) return resp.Write(rw) } if marker := target.Marker(); marker != nil { @@ -220,7 +233,8 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l go func() { defer cc.Close() - xnet.CopyBuffer(rw, cc, 8192) + err := xnet.CopyBuffer(rw, cc, 8192) + log.Debugf("close connection to node %s(%s), reason: %v", target.Name, target.Addr, err) connPool.Delete(target) }() } @@ -239,6 +253,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l log.Trace(string(dump)) } if err := req.Write(cc); err != nil { + log.Warnf("send request to node %s(%s) failed: %v", target.Name, target.Addr, err) return resp.Write(rw) }