From 2ae04628222a35a7dfb742bdec939d69e6a37dfa Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Fri, 21 Jun 2024 23:38:18 +0800 Subject: [PATCH] add support for linux network namespace --- config/config.go | 2 ++ config/parsing/hop/parse.go | 19 ++++++++++++- config/parsing/node/parse.go | 1 + config/parsing/service/parse.go | 27 +++++++++++++++++++ dialer/wg/dialer.go | 48 +++++++++++++++++++++++++++++++++ dialer/wg/metadata.go | 23 ++++++++++++++++ go.mod | 4 +-- go.sum | 7 ++--- internal/util/tls/tls.go | 4 +-- 9 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 dialer/wg/dialer.go create mode 100644 dialer/wg/metadata.go diff --git a/config/config.go b/config/config.go index c97dfa8..6bb586f 100644 --- a/config/config.go +++ b/config/config.go @@ -487,6 +487,7 @@ type HopConfig struct { Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"` HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"` Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"` + Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"` } type NodeConfig struct { @@ -500,6 +501,7 @@ type NodeConfig struct { Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"` Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"` Interface string `yaml:",omitempty" json:"interface,omitempty"` + Netns string `yaml:",omitempty" json:"netns,omitempty"` SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"` Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"` HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"` diff --git a/config/parsing/hop/parse.go b/config/parsing/hop/parse.go index 3fb9dc5..d435479 100644 --- a/config/parsing/hop/parse.go +++ b/config/parsing/hop/parse.go @@ -8,7 +8,9 @@ import ( "github.com/go-gost/core/chain" "github.com/go-gost/core/hop" "github.com/go-gost/core/logger" + mdutil "github.com/go-gost/core/metadata/util" "github.com/go-gost/x/config" + "github.com/go-gost/x/config/parsing" bypass_parser "github.com/go-gost/x/config/parsing/bypass" node_parser "github.com/go-gost/x/config/parsing/node" selector_parser "github.com/go-gost/x/config/parsing/selector" @@ -16,6 +18,7 @@ import ( hop_plugin "github.com/go-gost/x/hop/plugin" "github.com/go-gost/x/internal/loader" "github.com/go-gost/x/internal/plugin" + "github.com/go-gost/x/metadata" ) func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) { @@ -47,6 +50,16 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) { } } + ifce := cfg.Interface + var netns string + if cfg.Metadata != nil { + md := metadata.NewMetadata(cfg.Metadata) + if v := mdutil.GetString(md, parsing.MDKeyInterface); v != "" { + ifce = v + } + netns = mdutil.GetString(md, "netns") + } + var nodes []*chain.Node for _, v := range cfg.Nodes { if v == nil { @@ -60,8 +73,12 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) { v.Hosts = cfg.Hosts } if v.Interface == "" { - v.Interface = cfg.Interface + v.Interface = ifce } + if v.Netns == "" { + v.Netns = netns + } + if v.SockOpts == nil { v.SockOpts = cfg.SockOpts } diff --git a/config/parsing/node/parse.go b/config/parsing/node/parse.go index e91df94..52cb382 100644 --- a/config/parsing/node/parse.go +++ b/config/parsing/node/parse.go @@ -143,6 +143,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No tr := chain.NewTransport(d, cr, chain.AddrTransportOption(cfg.Addr), chain.InterfaceTransportOption(cfg.Interface), + chain.NetnsTransportOption(cfg.Netns), chain.SockOptsTransportOption(sockOpts), chain.TimeoutTransportOption(10*time.Second), ) diff --git a/config/parsing/service/parse.go b/config/parsing/service/parse.go index 3554256..8f4bd1c 100644 --- a/config/parsing/service/parse.go +++ b/config/parsing/service/parse.go @@ -2,6 +2,7 @@ package service import ( "fmt" + "runtime" "strings" "time" @@ -32,6 +33,7 @@ import ( "github.com/go-gost/x/registry" xservice "github.com/go-gost/x/service" "github.com/go-gost/x/stats" + "github.com/vishvananda/netns" ) func ParseService(cfg *config.ServiceConfig) (service.Service, error) { @@ -104,6 +106,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { var ignoreChain bool var pStats *stats.Stats var observePeriod time.Duration + var netnsIn, netnsOut string if cfg.Metadata != nil { md := metadata.NewMetadata(cfg.Metadata) ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol) @@ -125,6 +128,8 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { pStats = &stats.Stats{} } observePeriod = mdutil.GetDuration(md, "observePeriod") + netnsIn = mdutil.GetString(md, "netns") + netnsOut = mdutil.GetString(md, "netns.out") } listenOpts := []listener.Option{ @@ -146,6 +151,27 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { ) } + if netnsIn != "" { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + originNs, err := netns.Get() + if err != nil { + return nil, fmt.Errorf("netns.Get(): %v", err) + } + defer netns.Set(originNs) + + ns, err := netns.GetFromName(netnsIn) + if err != nil { + return nil, fmt.Errorf("netns.GetFromName(%s): %v", netnsIn, err) + } + defer ns.Close() + + if err := netns.Set(ns); err != nil { + return nil, fmt.Errorf("netns.Set(%s): %v", netnsIn, err) + } + } + var ln listener.Listener if rf := registry.ListenerRegistry().Get(cfg.Listener.Type); rf != nil { ln = rf(listenOpts...) @@ -209,6 +235,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { chain.RetriesRouterOption(cfg.Handler.Retries), // chain.TimeoutRouterOption(10*time.Second), chain.InterfaceRouterOption(ifce), + chain.NetnsRouterOption(netnsOut), chain.SockOptsRouterOption(sockOpts), chain.ResolverRouterOption(registry.ResolverRegistry().Get(cfg.Resolver)), chain.HostMapperRouterOption(registry.HostsRegistry().Get(cfg.Hosts)), diff --git a/dialer/wg/dialer.go b/dialer/wg/dialer.go new file mode 100644 index 0000000..33ac93e --- /dev/null +++ b/dialer/wg/dialer.go @@ -0,0 +1,48 @@ +package wg + +import ( + "context" + "net" + + "github.com/go-gost/core/dialer" + "github.com/go-gost/core/logger" + md "github.com/go-gost/core/metadata" + "github.com/go-gost/x/registry" +) + +func init() { + registry.DialerRegistry().Register("wg", NewDialer) +} + +type wgDialer struct { + md metadata + logger logger.Logger +} + +func NewDialer(opts ...dialer.Option) dialer.Dialer { + options := &dialer.Options{} + for _, opt := range opts { + opt(options) + } + + return &wgDialer{ + logger: options.Logger, + } +} + +func (d *wgDialer) Init(md md.Metadata) (err error) { + return d.parseMetadata(md) +} + +func (d *wgDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) { + var options dialer.DialOptions + for _, opt := range opts { + opt(&options) + } + + conn, err := options.NetDialer.Dial(ctx, "tcp", addr) + if err != nil { + d.logger.Error(err) + } + return conn, err +} diff --git a/dialer/wg/metadata.go b/dialer/wg/metadata.go new file mode 100644 index 0000000..db192db --- /dev/null +++ b/dialer/wg/metadata.go @@ -0,0 +1,23 @@ +package wg + +import ( + "time" + + md "github.com/go-gost/core/metadata" +) + +const ( + dialTimeout = "dialTimeout" +) + +const ( + defaultDialTimeout = 5 * time.Second +) + +type metadata struct { + dialTimeout time.Duration +} + +func (d *wgDialer) parseMetadata(md md.Metadata) (err error) { + return +} diff --git a/go.mod b/go.mod index 5752105..8a9122f 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/gin-contrib/cors v1.5.0 github.com/gin-gonic/gin v1.9.1 - github.com/go-gost/core v0.0.0-20240508132029-8d554ddcf77c + github.com/go-gost/core v0.0.0-20240621153412-5aede9a2b32f github.com/go-gost/gosocks4 v0.0.1 github.com/go-gost/gosocks5 v0.4.0 github.com/go-gost/plugin v0.0.0-20240103125338-9c84e29cb81a @@ -35,6 +35,7 @@ require ( github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/spf13/viper v1.18.2 github.com/vishvananda/netlink v1.1.0 + github.com/vishvananda/netns v0.0.4 github.com/xtaci/kcp-go/v5 v5.6.5 github.com/xtaci/smux v1.5.24 github.com/xtaci/tcpraw v1.2.25 @@ -106,7 +107,6 @@ require ( github.com/tjfoc/gmsm v1.4.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect - github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.6.0 // indirect diff --git a/go.sum b/go.sum index 23e313b..ae4f9b8 100644 --- a/go.sum +++ b/go.sum @@ -53,8 +53,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-gost/core v0.0.0-20240508132029-8d554ddcf77c h1:1ahtn+3bQB50at5ubWDOrA4yja8vWpWNrGSRaCztNWg= -github.com/go-gost/core v0.0.0-20240508132029-8d554ddcf77c/go.mod h1:j08tDHkFzk7dfOeLhl3RWsASdf9YWWRfWBUQqbQvx3A= +github.com/go-gost/core v0.0.0-20240621153412-5aede9a2b32f h1:deEX5HhpUDB03wAggTWn3/8l20cEGVq25bMsd9wqizo= +github.com/go-gost/core v0.0.0-20240621153412-5aede9a2b32f/go.mod h1:aTPFucvJyqc/o5h5/ZtyHJ0xgFIq5Ip+cMlhazm+TaI= 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= @@ -242,8 +242,9 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= +github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/xtaci/kcp-go/v5 v5.6.5 h1:oxGZNobj3OddrLzwdJYnR/waNgwrL98u02u0DWNHE3k= github.com/xtaci/kcp-go/v5 v5.6.5/go.mod h1:Qy3Zf2tWTdFdEs0E8JvhrX+39r5UDZoYac8anvud7/Q= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM= diff --git a/internal/util/tls/tls.go b/internal/util/tls/tls.go index d7301ec..eef4fc8 100644 --- a/internal/util/tls/tls.go +++ b/internal/util/tls/tls.go @@ -3,7 +3,7 @@ package tls import ( "crypto/tls" "crypto/x509" - "errors" + "fmt" "net" "os" "strings" @@ -234,7 +234,7 @@ func loadCA(caFile string) (cp *x509.CertPool, err error) { return nil, err } if !cp.AppendCertsFromPEM(data) { - return nil, errors.New("AppendCertsFromPEM failed") + return nil, fmt.Errorf("loadCA %s: AppendCertsFromPEM failed", caFile) } return }