add path option for hop

This commit is contained in:
ginuerzh 2023-11-09 20:34:59 +08:00
parent 3eca21104a
commit 6bface4581
10 changed files with 143 additions and 147 deletions

View File

@ -327,6 +327,7 @@ type ForwardNodeConfig struct {
Host string `yaml:",omitempty" json:"host,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"`
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
Path string `yaml:",omitempty" json:"path,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
@ -342,6 +343,7 @@ type HTTPNodeConfig struct {
type TLSNodeConfig struct {
ServerName string `yaml:"serverName,omitempty" json:"serverName,omitempty"`
Secure bool `yaml:",omitempty" json:"secure,omitempty"`
Options *TLSOptions `yaml:",omitempty" json:"options,omitempty"`
}
type DialerConfig struct {
@ -419,6 +421,7 @@ type NodeConfig struct {
Host string `yaml:",omitempty" json:"host,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"`
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
Path string `yaml:",omitempty" json:"path,omitempty"`
Interface string `yaml:",omitempty" json:"interface,omitempty"`
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`

View File

@ -164,6 +164,7 @@ func ParseNode(hop string, cfg *config.NodeConfig) (*chain.Node, error) {
chain.MetadataNodeOption(nm),
chain.HostNodeOption(host),
chain.ProtocolNodeOption(cfg.Protocol),
chain.PathNodeOption(cfg.Path),
chain.NetworkNodeOption(cfg.Network),
}
if cfg.HTTP != nil {
@ -173,10 +174,16 @@ func ParseNode(hop string, cfg *config.NodeConfig) (*chain.Node, error) {
}))
}
if cfg.TLS != nil {
opts = append(opts, chain.TLSNodeOption(&chain.TLSNodeSettings{
tlsCfg := &chain.TLSNodeSettings{
ServerName: cfg.TLS.ServerName,
Secure: cfg.TLS.Secure,
}))
}
if o := cfg.TLS.Options; o != nil {
tlsCfg.Options.MinVersion = o.MinVersion
tlsCfg.Options.MaxVersion = o.MaxVersion
tlsCfg.Options.CipherSuites = o.CipherSuites
}
opts = append(opts, chain.TLSNodeOption(tlsCfg))
}
if cfg.Auth != nil {
opts = append(opts, chain.AutherNodeOption(

View File

@ -265,6 +265,7 @@ func parseForwarder(cfg *config.ForwarderConfig) (hop.Hop, error) {
Host: node.Host,
Network: node.Network,
Protocol: node.Protocol,
Path: node.Path,
Bypass: node.Bypass,
Bypasses: node.Bypasses,
HTTP: node.HTTP,

4
go.mod
View File

@ -7,10 +7,10 @@ require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.9.1
github.com/go-gost/core v0.0.0-20231107150907-7f581cb668b1
github.com/go-gost/core v0.0.0-20231109123312-8e4fc06cf1b7
github.com/go-gost/gosocks4 v0.0.1
github.com/go-gost/gosocks5 v0.4.0
github.com/go-gost/plugin v0.0.0-20231102125124-a1cc7a13e066
github.com/go-gost/plugin v0.0.0-20231109123346-0ae4157b9d25
github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451
github.com/go-redis/redis/v8 v8.11.5

4
go.sum
View File

@ -93,12 +93,16 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
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-20231107150907-7f581cb668b1 h1:hzxZgut10J3Rm0meINWB5yal3gIV9IkThKLbshsd/Mk=
github.com/go-gost/core v0.0.0-20231107150907-7f581cb668b1/go.mod h1:ndkgWVYRLwupVaFFWv8ML1Nr8tD3xhHK245PLpUDg4E=
github.com/go-gost/core v0.0.0-20231109123312-8e4fc06cf1b7 h1:sDsPtmP51qf8zN/RbZZj/3vNLCoH0sdvpIRwV6TfzvY=
github.com/go-gost/core v0.0.0-20231109123312-8e4fc06cf1b7/go.mod h1:ndkgWVYRLwupVaFFWv8ML1Nr8tD3xhHK245PLpUDg4E=
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.4.0 h1:EIrOEkpJez4gwHrMa33frA+hHXJyevjp47thpMQsJzI=
github.com/go-gost/gosocks5 v0.4.0/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
github.com/go-gost/plugin v0.0.0-20231102125124-a1cc7a13e066 h1:/pDM9JP9ESSRuAr237yAXB6WiDdjEeulDkaLa9Gw0ss=
github.com/go-gost/plugin v0.0.0-20231102125124-a1cc7a13e066/go.mod h1:qXr2Zm9Ex2ATqnWuNUzVZqySPMnuIihvblYZt4MlZLw=
github.com/go-gost/plugin v0.0.0-20231109123346-0ae4157b9d25 h1:sOarC0xAJij4VtEhkJRng5okZW23KlXprxhb5XFZ+pw=
github.com/go-gost/plugin v0.0.0-20231109123346-0ae4157b9d25/go.mod h1:qXr2Zm9Ex2ATqnWuNUzVZqySPMnuIihvblYZt4MlZLw=
github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7 h1:qAG1OyjvdA5h221CfFSS3J359V3d2E7dJWyP29QoDSI=
github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451 h1:xj8gUZGYO3nb5+6Bjw9+tsFkA9sYynrOvDvvC4uDV2I=

View File

@ -20,10 +20,12 @@ import (
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
"github.com/go-gost/x/config"
xio "github.com/go-gost/x/internal/io"
xnet "github.com/go-gost/x/internal/net"
auth_util "github.com/go-gost/x/internal/util/auth"
"github.com/go-gost/x/internal/util/forward"
tls_util "github.com/go-gost/x/internal/util/tls"
"github.com/go-gost/x/registry"
)
@ -232,6 +234,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, remot
target = h.hop.Select(ctx,
hop.HostSelectOption(req.Host),
hop.ProtocolSelectOption(forward.ProtoHTTP),
hop.PathSelectOption(req.URL.Path),
)
}
if target == nil {
@ -284,10 +287,16 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, remot
log.Debugf("connection to node %s(%s)", target.Name, target.Addr)
if tlsSettings := target.Options().TLS; tlsSettings != nil {
cc = tls.Client(cc, &tls.Config{
cfg := &tls.Config{
ServerName: tlsSettings.ServerName,
InsecureSkipVerify: !tlsSettings.Secure,
}
tls_util.SetTLSOptions(cfg, &config.TLSOptions{
MinVersion: tlsSettings.Options.MinVersion,
MaxVersion: tlsSettings.Options.MaxVersion,
CipherSuites: tlsSettings.Options.CipherSuites,
})
cc = tls.Client(cc, cfg)
}
if err := req.Write(cc); err != nil {

View File

@ -21,11 +21,13 @@ import (
"github.com/go-gost/core/logger"
mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util"
"github.com/go-gost/x/config"
xio "github.com/go-gost/x/internal/io"
xnet "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/internal/net/proxyproto"
auth_util "github.com/go-gost/x/internal/util/auth"
"github.com/go-gost/x/internal/util/forward"
tls_util "github.com/go-gost/x/internal/util/tls"
"github.com/go-gost/x/registry"
)
@ -233,6 +235,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, remot
target = h.hop.Select(ctx,
hop.HostSelectOption(req.Host),
hop.ProtocolSelectOption(forward.ProtoHTTP),
hop.PathSelectOption(req.URL.Path),
)
}
if target == nil {
@ -285,10 +288,16 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, remot
log.Debugf("new connection to node %s(%s)", target.Name, target.Addr)
if tlsSettings := target.Options().TLS; tlsSettings != nil {
cc = tls.Client(cc, &tls.Config{
cfg := &tls.Config{
ServerName: tlsSettings.ServerName,
InsecureSkipVerify: !tlsSettings.Secure,
}
tls_util.SetTLSOptions(cfg, &config.TLSOptions{
MinVersion: tlsSettings.Options.MinVersion,
MaxVersion: tlsSettings.Options.MaxVersion,
CipherSuites: tlsSettings.Options.CipherSuites,
})
cc = tls.Client(cc, cfg)
}
cc = proxyproto.WrapClientConn(h.md.proxyProtocol, remoteAddr, localAddr, cc)

View File

@ -6,6 +6,7 @@ import (
"encoding/json"
"io"
"net"
"sort"
"strings"
"sync"
"time"
@ -179,6 +180,26 @@ func (p *chainHop) Select(ctx context.Context, opts ...hop.SelectOption) *chain.
}
}
// filter by path
if path := options.Path; path != "" {
p.options.logger.Debugf("filter by path: %s", path)
sort.SliceStable(filters, func(i, j int) bool {
return len(filters[i].Options().Path) > len(filters[j].Options().Path)
})
var nodes []*chain.Node
for _, node := range filters {
if node.Options().Path == "" {
nodes = append(nodes, node)
continue
}
if strings.HasPrefix(path, node.Options().Path) {
nodes = append(nodes, node)
break
}
}
filters = nodes
}
var nodes []*chain.Node
for _, node := range filters {
if node == nil {

View File

@ -67,6 +67,7 @@ func (p *grpcPlugin) Select(ctx context.Context, opts ...hop.SelectOption) *chai
Network: options.Network,
Addr: options.Addr,
Host: options.Host,
Path: options.Path,
Client: string(auth_util.IDFromContext(ctx)),
})
if err != nil {
@ -103,6 +104,7 @@ type httpPluginRequest struct {
Network string `json:"network"`
Addr string `json:"addr"`
Host string `json:"host"`
Path string `json:"path"`
Client string `json:"client"`
}
@ -151,6 +153,7 @@ func (p *httpPlugin) Select(ctx context.Context, opts ...hop.SelectOption) *chai
Network: options.Network,
Addr: options.Addr,
Host: options.Host,
Path: options.Path,
Client: string(auth_util.IDFromContext(ctx)),
}
v, err := json.Marshal(&rb)

View File

@ -88,76 +88,7 @@ func LoadServerConfig(config *config.TLSConfig) (*tls.Config, error) {
cfg.ClientAuth = tls.RequireAndVerifyClientCert
}
if opts := config.Options; opts != nil {
switch strings.ToLower(opts.MinVersion) {
case strings.ToLower(VersionTLS10):
cfg.MinVersion = tls.VersionTLS10
case strings.ToLower(VersionTLS11):
cfg.MinVersion = tls.VersionTLS11
case strings.ToLower(VersionTLS12):
cfg.MinVersion = tls.VersionTLS12
case strings.ToLower(VersionTLS13):
cfg.MinVersion = tls.VersionTLS13
}
switch strings.ToLower(opts.MaxVersion) {
case strings.ToLower(VersionTLS10):
cfg.MaxVersion = tls.VersionTLS10
case strings.ToLower(VersionTLS11):
cfg.MaxVersion = tls.VersionTLS11
case strings.ToLower(VersionTLS12):
cfg.MaxVersion = tls.VersionTLS12
case strings.ToLower(VersionTLS13):
cfg.MaxVersion = tls.VersionTLS13
}
for _, v := range opts.CipherSuites {
switch strings.ToLower(v) {
case strings.ToLower(TLS_RSA_WITH_RC4_128_SHA):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_RSA_WITH_RC4_128_SHA)
case strings.ToLower(TLS_RSA_WITH_3DES_EDE_CBC_SHA):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
case strings.ToLower(TLS_RSA_WITH_AES_128_CBC_SHA):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_RSA_WITH_AES_128_CBC_SHA)
case strings.ToLower(TLS_RSA_WITH_AES_256_CBC_SHA):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_RSA_WITH_AES_256_CBC_SHA)
case strings.ToLower(TLS_RSA_WITH_AES_128_CBC_SHA256):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_RSA_WITH_AES_128_CBC_SHA256)
case strings.ToLower(TLS_RSA_WITH_AES_128_GCM_SHA256):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_RSA_WITH_AES_128_GCM_SHA256)
case strings.ToLower(TLS_RSA_WITH_AES_256_GCM_SHA384):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_RSA_WITH_AES_256_GCM_SHA384)
case strings.ToLower(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA)
case strings.ToLower(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
case strings.ToLower(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
case strings.ToLower(TLS_ECDHE_RSA_WITH_RC4_128_SHA):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA)
case strings.ToLower(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA)
case strings.ToLower(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
case strings.ToLower(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
case strings.ToLower(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256)
case strings.ToLower(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256)
case strings.ToLower(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
case strings.ToLower(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
case strings.ToLower(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
case strings.ToLower(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
case strings.ToLower(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
case strings.ToLower(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256):
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)
}
}
}
SetTLSOptions(cfg, config.Options)
return cfg, nil
}
@ -188,7 +119,41 @@ func LoadClientConfig(config *config.TLSConfig) (*tls.Config, error) {
cfg.ServerName = config.ServerName
cfg.InsecureSkipVerify = !config.Secure
if opts := config.Options; opts != nil {
if config.Options != nil {
SetTLSOptions(cfg, config.Options)
}
// If the root ca is given, but skip verify, we verify the certificate manually.
if cfg.RootCAs != nil && !config.Secure {
cfg.VerifyConnection = func(state tls.ConnectionState) error {
opts := x509.VerifyOptions{
Roots: cfg.RootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := state.PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err := certs[0].Verify(opts)
return err
}
}
return cfg, nil
}
func SetTLSOptions(cfg *tls.Config, opts *config.TLSOptions) {
if cfg == nil || opts == nil {
return
}
switch strings.ToLower(opts.MinVersion) {
case strings.ToLower(VersionTLS10):
cfg.MinVersion = tls.VersionTLS10
@ -259,32 +224,6 @@ func LoadClientConfig(config *config.TLSConfig) (*tls.Config, error) {
}
}
// If the root ca is given, but skip verify, we verify the certificate manually.
if cfg.RootCAs != nil && !config.Secure {
cfg.VerifyConnection = func(state tls.ConnectionState) error {
opts := x509.VerifyOptions{
Roots: cfg.RootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := state.PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err := certs[0].Verify(opts)
return err
}
}
return cfg, nil
}
func loadCA(caFile string) (cp *x509.CertPool, err error) {
if caFile == "" {
return