From e261e422c11d7560923b87f0384932ffcce6f6ab Mon Sep 17 00:00:00 2001 From: dd dd Date: Thu, 25 Jul 2024 23:25:56 +0200 Subject: [PATCH 1/6] Implement reverse connection handling in SOCKS5 server --- exit/exit.go | 29 +++++++++++++++++++++ netstr/dial.go | 5 ++-- protocol/message.go | 5 ++-- socks5/request.go | 15 ++++++++--- socks5/socks5.go | 15 +++++++---- socks5/tcp.go | 63 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 socks5/tcp.go diff --git a/exit/exit.go b/exit/exit.go index a0e4c50..4936512 100644 --- a/exit/exit.go +++ b/exit/exit.go @@ -183,6 +183,8 @@ func (e *Exit) processMessage(ctx context.Context, msg nostr.IncomingEvent) { switch protocolMessage.Type { case protocol.MessageConnect: e.handleConnect(ctx, msg, protocolMessage, false) + case protocol.MessageConnectReverse: + e.handleConnectReverse(ctx, protocolMessage, false) case protocol.MessageTypeSocks5: e.handleSocks5ProxyMessage(msg, protocolMessage) } @@ -226,6 +228,33 @@ func (e *Exit) handleConnect(ctx context.Context, msg nostr.IncomingEvent, proto go socks5.Proxy(connection, dst, nil) } +func (e *Exit) handleConnectReverse(ctx context.Context, protocolMessage *protocol.Message, isTLS bool) { + e.mutexMap.Lock(protocolMessage.Key.String()) + defer e.mutexMap.Unlock(protocolMessage.Key.String()) + connection, err := net.Dial("tcp", ":1234") + if err != nil { + return + } + var dst net.Conn + if isTLS { + conf := tls.Config{InsecureSkipVerify: true} + dst, err = tls.Dial("tcp", e.config.BackendHost, &conf) + } else { + dst, err = net.Dial("tcp", e.config.BackendHost) + } + if err != nil { + slog.Error("could not connect to backend", "error", err) + return + } + + _, err = connection.Write([]byte(protocolMessage.Key.String())) + if err != nil { + return + } + go socks5.Proxy(dst, connection, nil) + go socks5.Proxy(connection, dst, nil) +} + // handleSocks5ProxyMessage handles the SOCKS5 proxy message by writing it to the destination connection. // If the destination connection does not exist, the function returns without doing anything. // diff --git a/netstr/dial.go b/netstr/dial.go index b7ffd58..706e8e9 100644 --- a/netstr/dial.go +++ b/netstr/dial.go @@ -17,10 +17,9 @@ import ( // It creates a signed event using the private key, public key, and destination address. // It ensures that the relays are available in the pool and publishes the signed event to each relay. // Finally, it returns the Connection and nil error. If there are any errors, nil connection and the error are returned. -func DialSocks(pool *nostr.SimplePool) func(ctx context.Context, net_, addr string) (net.Conn, error) { +func DialSocks(pool *nostr.SimplePool, connectionID uuid.UUID) func(ctx context.Context, net_, addr string) (net.Conn, error) { return func(ctx context.Context, net_, addr string) (net.Conn, error) { addr = strings.ReplaceAll(addr, ".", "") - connectionID := uuid.New() key := nostr.GeneratePrivateKey() connection := NewConnection(ctx, WithPrivateKey(key), @@ -39,7 +38,7 @@ func DialSocks(pool *nostr.SimplePool) func(ctx context.Context, net_, addr stri return nil, err } opts := []protocol.MessageOption{ - protocol.WithType(protocol.MessageConnect), + protocol.WithType(protocol.MessageConnectReverse), protocol.WithUUID(connectionID), protocol.WithDestination(addr), } diff --git a/protocol/message.go b/protocol/message.go index ddacdf5..7ecf161 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -8,8 +8,9 @@ import ( type MessageType string var ( - MessageTypeSocks5 = MessageType("SOCKS5") - MessageConnect = MessageType("CONNECT") + MessageTypeSocks5 = MessageType("SOCKS5") + MessageConnect = MessageType("CONNECT") + MessageConnectReverse = MessageType("CONNECTR") ) type Message struct { diff --git a/socks5/request.go b/socks5/request.go index b18dafc..f9cf735 100644 --- a/socks5/request.go +++ b/socks5/request.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/asmogo/nws/netstr" + "github.com/google/uuid" "io" "net" "strconv" @@ -167,8 +168,11 @@ func (s *Server) handleConnect(ctx context.Context, conn net.Conn, req *Request) // Attempt to connect dial := s.config.Dial + ch := make(chan net.Conn) if dial == nil { - dial = netstr.DialSocks(s.pool) + connectionID := uuid.New() + s.tcpListener.AddConnectChannel(connectionID, ch) + dial = netstr.DialSocks(s.pool, connectionID) } target, err := dial(ctx, "tcp", req.realDestAddr.FQDN) if err != nil { @@ -192,11 +196,16 @@ func (s *Server) handleConnect(ctx context.Context, conn net.Conn, req *Request) if err := SendReply(conn, successReply, &bind); err != nil { return fmt.Errorf("failed to send reply: %v", err) } + // read + + // wait for the connection + connR := <-ch + defer connR.Close() // Start proxying errCh := make(chan error, 2) - go Proxy(target, conn, errCh) - go Proxy(conn, target, errCh) + go Proxy(connR, conn, errCh) + go Proxy(conn, connR, errCh) // Wait for i := 0; i < 2; i++ { diff --git a/socks5/socks5.go b/socks5/socks5.go index 1bbb4f4..6d6a8d2 100644 --- a/socks5/socks5.go +++ b/socks5/socks5.go @@ -59,6 +59,7 @@ type Server struct { config *Config authMethods map[uint8]Authenticator pool *nostr.SimplePool + tcpListener *TCPListener } // New creates a new Server and potentially returns an error @@ -86,12 +87,16 @@ func New(conf *Config, pool *nostr.SimplePool) (*Server, error) { if conf.Logger == nil { conf.Logger = log.New(os.Stdout, "", log.LstdFlags) } - - server := &Server{ - config: conf, - pool: pool, + listener, err := NewTCPListener() + if err != nil { + return nil, err + } + go listener.Start() + server := &Server{ + config: conf, + pool: pool, + tcpListener: listener, } - server.authMethods = make(map[uint8]Authenticator) for _, a := range conf.AuthMethods { diff --git a/socks5/tcp.go b/socks5/tcp.go new file mode 100644 index 0000000..9edef36 --- /dev/null +++ b/socks5/tcp.go @@ -0,0 +1,63 @@ +package socks5 + +import ( + "github.com/google/uuid" + "github.com/puzpuzpuz/xsync/v3" + "log/slog" + "net" +) + +type TCPListener struct { + listener net.Listener + connectChannels *xsync.MapOf[string, chan net.Conn] // todo -- use [16]byte for uuid instead of string +} + +func NewTCPListener() (*TCPListener, error) { + l, err := net.Listen("tcp", ":1234") + if err != nil { + return nil, err + } + return &TCPListener{ + listener: l, + connectChannels: xsync.NewMapOf[string, chan net.Conn](), + }, nil +} + +func (l *TCPListener) AddConnectChannel(uuid uuid.UUID, ch chan net.Conn) { + l.connectChannels.Store(uuid.String(), ch) +} + +// Start starts the listener +func (l *TCPListener) Start() { + for { + conn, err := l.listener.Accept() + if err != nil { + return + } + go l.handleConnection(conn) + } +} + +// handleConnection handles the connection +func (l *TCPListener) handleConnection(conn net.Conn) { + //defer conn.Close() + for { + // read uuid from the connection + readbuffer := make([]byte, 36) + + _, err := conn.Read(readbuffer) + if err != nil { + return + } + + // check if uuid is in the map + ch, ok := l.connectChannels.Load(string(readbuffer)) + if !ok { + slog.Error("uuid not found in map") + return + } + // send the connection to the channel + ch <- conn + return + } +} From 9cfbd2329d6865238de40963202daeb245e2ea22 Mon Sep 17 00:00:00 2001 From: dd dd Date: Fri, 26 Jul 2024 12:37:11 +0200 Subject: [PATCH 2/6] Added new MessageType for ConnectReverse. Added PublicAddress configuration to entry node. --- cmd/entry/proxy.go | 2 +- config/config.go | 5 +++-- exit/exit.go | 2 +- netstr/dial.go | 23 +++++++++++++++++------ proxy/proxy.go | 6 +++--- socks5/request.go | 30 ++++++++++++++++++++++-------- socks5/socks5.go | 25 +++++++++++++++++-------- 7 files changed, 64 insertions(+), 29 deletions(-) diff --git a/cmd/entry/proxy.go b/cmd/entry/proxy.go index 6915f63..29bd6ec 100644 --- a/cmd/entry/proxy.go +++ b/cmd/entry/proxy.go @@ -9,7 +9,7 @@ import ( func main() { // load the configuration // from the environment - cfg, err := config.LoadConfig[config.ProxyConfig]() + cfg, err := config.LoadConfig[config.EntryConfig]() if err != nil { panic(err) } diff --git a/config/config.go b/config/config.go index 161c76e..d9caf3d 100644 --- a/config/config.go +++ b/config/config.go @@ -9,8 +9,9 @@ import ( "github.com/joho/godotenv" ) -type ProxyConfig struct { - NostrRelays []string `env:"NOSTR_RELAYS" envSeparator:";"` +type EntryConfig struct { + NostrRelays []string `env:"NOSTR_RELAYS" envSeparator:";"` + PublicAddress string `env:"PUBLIC_ADDRESS"` } type ExitConfig struct { diff --git a/exit/exit.go b/exit/exit.go index 4936512..62d9b84 100644 --- a/exit/exit.go +++ b/exit/exit.go @@ -231,7 +231,7 @@ func (e *Exit) handleConnect(ctx context.Context, msg nostr.IncomingEvent, proto func (e *Exit) handleConnectReverse(ctx context.Context, protocolMessage *protocol.Message, isTLS bool) { e.mutexMap.Lock(protocolMessage.Key.String()) defer e.mutexMap.Unlock(protocolMessage.Key.String()) - connection, err := net.Dial("tcp", ":1234") + connection, err := net.Dial("tcp", protocolMessage.Destination) if err != nil { return } diff --git a/netstr/dial.go b/netstr/dial.go index 706e8e9..103b2c1 100644 --- a/netstr/dial.go +++ b/netstr/dial.go @@ -11,13 +11,20 @@ import ( "strings" ) +type DialOptions struct { + Pool *nostr.SimplePool + PublicAddress string + ConnectionID uuid.UUID + MessageType protocol.MessageType +} + // DialSocks connects to a destination using the provided SimplePool and returns a Dialer function. // It creates a new Connection using the specified context, private key, destination address, subscription flag, and connectionID. // It parses the destination address to get the public key and relays. // It creates a signed event using the private key, public key, and destination address. // It ensures that the relays are available in the pool and publishes the signed event to each relay. // Finally, it returns the Connection and nil error. If there are any errors, nil connection and the error are returned. -func DialSocks(pool *nostr.SimplePool, connectionID uuid.UUID) func(ctx context.Context, net_, addr string) (net.Conn, error) { +func DialSocks(options DialOptions) func(ctx context.Context, net_, addr string) (net.Conn, error) { return func(ctx context.Context, net_, addr string) (net.Conn, error) { addr = strings.ReplaceAll(addr, ".", "") key := nostr.GeneratePrivateKey() @@ -25,7 +32,7 @@ func DialSocks(pool *nostr.SimplePool, connectionID uuid.UUID) func(ctx context. WithPrivateKey(key), WithDst(addr), WithSub(), - WithUUID(connectionID)) + WithUUID(options.ConnectionID)) publicKey, relays, err := ParseDestination(addr) if err != nil { @@ -38,16 +45,20 @@ func DialSocks(pool *nostr.SimplePool, connectionID uuid.UUID) func(ctx context. return nil, err } opts := []protocol.MessageOption{ - protocol.WithType(protocol.MessageConnectReverse), - protocol.WithUUID(connectionID), - protocol.WithDestination(addr), + protocol.WithType(options.MessageType), + protocol.WithUUID(options.ConnectionID), + } + if options.PublicAddress != "" { + opts = append(opts, protocol.WithDestination(options.PublicAddress)) + } else { + opts = append(opts, protocol.WithDestination(addr)) // todo -- use public key instead } ev, err := signer.CreateSignedEvent(publicKey, protocol.KindEphemeralEvent, nostr.Tags{nostr.Tag{"p", publicKey}}, opts...) for _, relayUrl := range relays { - relay, err := pool.EnsureRelay(relayUrl) + relay, err := options.Pool.EnsureRelay(relayUrl) if err != nil { slog.Error("error creating relay", err) continue diff --git a/proxy/proxy.go b/proxy/proxy.go index e1695d8..00b4a51 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -11,14 +11,14 @@ import ( ) type Proxy struct { - config *config.ProxyConfig // the configuration for the gateway + config *config.EntryConfig // the configuration for the gateway // a list of nostr relays to publish events to relays []*nostr.Relay // deprecated -- should be used for default relay configuration pool *nostr.SimplePool socksServer *socks5.Server } -func New(ctx context.Context, config *config.ProxyConfig) *Proxy { +func New(ctx context.Context, config *config.EntryConfig) *Proxy { s := &Proxy{ config: config, pool: nostr.NewSimplePool(ctx), @@ -32,7 +32,7 @@ func New(ctx context.Context, config *config.ProxyConfig) *Proxy { BindIP: net.IP{0, 0, 0, 0}, Logger: nil, Dial: nil, - }, s.pool) + }, s.pool, config) if err != nil { panic(err) } diff --git a/socks5/request.go b/socks5/request.go index f9cf735..8c95308 100644 --- a/socks5/request.go +++ b/socks5/request.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/asmogo/nws/netstr" + "github.com/asmogo/nws/protocol" "github.com/google/uuid" "io" "net" @@ -169,10 +170,20 @@ func (s *Server) handleConnect(ctx context.Context, conn net.Conn, req *Request) // Attempt to connect dial := s.config.Dial ch := make(chan net.Conn) + connectionID := uuid.New() + options := netstr.DialOptions{ + Pool: s.pool, + PublicAddress: s.config.entryConfig.PublicAddress, + ConnectionID: connectionID, + } if dial == nil { - connectionID := uuid.New() - s.tcpListener.AddConnectChannel(connectionID, ch) - dial = netstr.DialSocks(s.pool, connectionID) + if s.tcpListener != nil { + s.tcpListener.AddConnectChannel(connectionID, ch) + options.MessageType = protocol.MessageConnectReverse + } else { + options.MessageType = protocol.MessageConnect + } + dial = netstr.DialSocks(options) } target, err := dial(ctx, "tcp", req.realDestAddr.FQDN) if err != nil { @@ -197,11 +208,14 @@ func (s *Server) handleConnect(ctx context.Context, conn net.Conn, req *Request) return fmt.Errorf("failed to send reply: %v", err) } // read - - // wait for the connection - connR := <-ch - defer connR.Close() - + var connR net.Conn + if options.MessageType == protocol.MessageConnectReverse { + // wait for the connection + connR = <-ch + defer connR.Close() + } else { + connR = target + } // Start proxying errCh := make(chan error, 2) go Proxy(connR, conn, errCh) diff --git a/socks5/socks5.go b/socks5/socks5.go index 6d6a8d2..475ecf6 100644 --- a/socks5/socks5.go +++ b/socks5/socks5.go @@ -3,6 +3,7 @@ package socks5 import ( "bufio" "fmt" + "github.com/asmogo/nws/config" "github.com/nbd-wtf/go-nostr" "log" "net" @@ -49,6 +50,8 @@ type Config struct { // Optional function for dialing out Dial func(ctx context.Context, network, addr string) (net.Conn, error) + + entryConfig *config.EntryConfig } var ErrorNoServerAvailable = fmt.Errorf("no socks server available") @@ -63,7 +66,7 @@ type Server struct { } // New creates a new Server and potentially returns an error -func New(conf *Config, pool *nostr.SimplePool) (*Server, error) { +func New(conf *Config, pool *nostr.SimplePool, config *config.EntryConfig) (*Server, error) { // Ensure we have at least one authentication method enabled if len(conf.AuthMethods) == 0 { if conf.Credentials != nil { @@ -87,15 +90,21 @@ func New(conf *Config, pool *nostr.SimplePool) (*Server, error) { if conf.Logger == nil { conf.Logger = log.New(os.Stdout, "", log.LstdFlags) } - listener, err := NewTCPListener() - if err != nil { - return nil, err + if conf.entryConfig == nil { + conf.entryConfig = config } - go listener.Start() + server := &Server{ - config: conf, - pool: pool, - tcpListener: listener, + config: conf, + pool: pool, + } + if conf.entryConfig.PublicAddress != "" { + listener, err := NewTCPListener() + if err != nil { + return nil, err + } + go listener.Start() + server.tcpListener = listener } server.authMethods = make(map[uint8]Authenticator) From 36cc5eca25968fd582cb30b7af5b1b20889ab3d0 Mon Sep 17 00:00:00 2001 From: dd dd Date: Fri, 26 Jul 2024 12:39:10 +0200 Subject: [PATCH 3/6] removed unwanted connection object. added comment. --- socks5/request.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/socks5/request.go b/socks5/request.go index 8c95308..b1d74af 100644 --- a/socks5/request.go +++ b/socks5/request.go @@ -208,18 +208,16 @@ func (s *Server) handleConnect(ctx context.Context, conn net.Conn, req *Request) return fmt.Errorf("failed to send reply: %v", err) } // read - var connR net.Conn if options.MessageType == protocol.MessageConnectReverse { // wait for the connection - connR = <-ch - defer connR.Close() - } else { - connR = target + // in this case, our target needs to be the reversed tcp connection + target = <-ch + defer target.Close() } // Start proxying errCh := make(chan error, 2) - go Proxy(connR, conn, errCh) - go Proxy(conn, connR, errCh) + go Proxy(target, conn, errCh) + go Proxy(conn, target, errCh) // Wait for i := 0; i < 2; i++ { From 4f2f788c114b5d796b714037cd2e3cc995c3fc47 Mon Sep 17 00:00:00 2001 From: dd dd Date: Fri, 26 Jul 2024 19:37:34 +0200 Subject: [PATCH 4/6] Add EntryConfig to New function and update dependencies --- go.mod | 5 ++++- go.sum | 14 +++++++++++--- socks5/auth_test.go | 9 +++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index cc628cf..7896e92 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/samber/lo v1.45.0 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.23.0 + golang.org/x/net v0.27.0 ) require ( @@ -26,6 +26,8 @@ require ( github.com/gobwas/ws v1.2.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect + github.com/kr/pretty v0.1.0 // indirect + github.com/kr/text v0.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -35,5 +37,6 @@ require ( golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b9fae80..a1f34df 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,7 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/caarlos0/env/v11 v11.0.0 h1:ZIlkOjuL3xoZS0kmUJlF74j2Qj8GMOq3CDLX/Viak8Q= github.com/caarlos0/env/v11 v11.0.0/go.mod h1:2RC3HQu8BQqtEK3V4iHPxj0jOdWdbPpWJ6pOueeU1xM= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -69,6 +70,12 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/nbd-wtf/go-nostr v0.30.2 h1:dG/2X52/XDg+7phZH+BClcvA5D+S6dXvxJKkBaySEzI= @@ -114,8 +121,8 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -143,8 +150,9 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/socks5/auth_test.go b/socks5/auth_test.go index 962afbe..bf526e9 100644 --- a/socks5/auth_test.go +++ b/socks5/auth_test.go @@ -2,6 +2,7 @@ package socks5 import ( "bytes" + "github.com/asmogo/nws/config" "github.com/nbd-wtf/go-nostr" "testing" ) @@ -11,7 +12,7 @@ func TestNoAuth(t *testing.T) { req.Write([]byte{1, NoAuth}) var resp bytes.Buffer - s, _ := New(&Config{}, &nostr.SimplePool{}) + s, _ := New(&Config{}, &nostr.SimplePool{}, &config.EntryConfig{}) ctx, err := s.authenticate(&resp, req) if err != nil { t.Fatalf("err: %v", err) @@ -39,7 +40,7 @@ func TestPasswordAuth_Valid(t *testing.T) { cator := UserPassAuthenticator{Credentials: cred} - s, _ := New(&Config{AuthMethods: []Authenticator{cator}}, &nostr.SimplePool{}) + s, _ := New(&Config{AuthMethods: []Authenticator{cator}}, &nostr.SimplePool{}, &config.EntryConfig{}) ctx, err := s.authenticate(&resp, req) if err != nil { @@ -75,7 +76,7 @@ func TestPasswordAuth_Invalid(t *testing.T) { "foo": "bar", } cator := UserPassAuthenticator{Credentials: cred} - s, _ := New(&Config{AuthMethods: []Authenticator{cator}}, &nostr.SimplePool{}) + s, _ := New(&Config{AuthMethods: []Authenticator{cator}}, &nostr.SimplePool{}, &config.EntryConfig{}) ctx, err := s.authenticate(&resp, req) if err != UserAuthFailed { @@ -102,7 +103,7 @@ func TestNoSupportedAuth(t *testing.T) { } cator := UserPassAuthenticator{Credentials: cred} - s, _ := New(&Config{AuthMethods: []Authenticator{cator}}, &nostr.SimplePool{}) + s, _ := New(&Config{AuthMethods: []Authenticator{cator}}, &nostr.SimplePool{}, &config.EntryConfig{}) ctx, err := s.authenticate(&resp, req) if err != NoSupportedAuth { From 1dc506a1f732ce8f4eb870c9b3b122c98fc02805 Mon Sep 17 00:00:00 2001 From: dd dd Date: Fri, 26 Jul 2024 21:21:50 +0200 Subject: [PATCH 5/6] restructure stuff --- protocol/signer.go | 3 +-- socks5/request.go | 5 ++--- socks5/socks5.go | 2 +- socks5/tcp.go | 4 ++-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/protocol/signer.go b/protocol/signer.go index f696b97..68050a3 100644 --- a/protocol/signer.go +++ b/protocol/signer.go @@ -11,8 +11,7 @@ const KindEphemeralEvent int = 38333 // EventSigner represents a signer that can create and sign events. // -// EventSigner provides methods for creating unsigned events, creating signed events, -// and decrypting and writing events received from an exit node. +// EventSigner provides methods for creating unsigned events, creating signed events type EventSigner struct { PublicKey string privateKey string diff --git a/socks5/request.go b/socks5/request.go index b1d74af..194115f 100644 --- a/socks5/request.go +++ b/socks5/request.go @@ -166,16 +166,15 @@ func (s *Server) handleConnect(ctx context.Context, conn net.Conn, req *Request) } else { ctx = ctx_ } - - // Attempt to connect - dial := s.config.Dial ch := make(chan net.Conn) + // Attempt to connect connectionID := uuid.New() options := netstr.DialOptions{ Pool: s.pool, PublicAddress: s.config.entryConfig.PublicAddress, ConnectionID: connectionID, } + dial := s.config.Dial if dial == nil { if s.tcpListener != nil { s.tcpListener.AddConnectChannel(connectionID, ch) diff --git a/socks5/socks5.go b/socks5/socks5.go index 475ecf6..dca76a0 100644 --- a/socks5/socks5.go +++ b/socks5/socks5.go @@ -99,7 +99,7 @@ func New(conf *Config, pool *nostr.SimplePool, config *config.EntryConfig) (*Ser pool: pool, } if conf.entryConfig.PublicAddress != "" { - listener, err := NewTCPListener() + listener, err := NewTCPListener(conf.entryConfig.PublicAddress) if err != nil { return nil, err } diff --git a/socks5/tcp.go b/socks5/tcp.go index 9edef36..813dfe8 100644 --- a/socks5/tcp.go +++ b/socks5/tcp.go @@ -12,8 +12,8 @@ type TCPListener struct { connectChannels *xsync.MapOf[string, chan net.Conn] // todo -- use [16]byte for uuid instead of string } -func NewTCPListener() (*TCPListener, error) { - l, err := net.Listen("tcp", ":1234") +func NewTCPListener(address string) (*TCPListener, error) { + l, err := net.Listen("tcp", address) if err != nil { return nil, err } From f7d13a07a25d48429b520be28e9c5365c3dcd8e7 Mon Sep 17 00:00:00 2001 From: dd dd Date: Fri, 26 Jul 2024 21:44:47 +0200 Subject: [PATCH 6/6] updated readme --- README.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 76041e1..e09af3f 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,12 @@ To set up using Docker Compose, run the following command: docker compose up -d --build ``` -This will start an example environment, including the entry node, exit node, and a backend service. +This will start an example environment, including: +* Entry node +* Exit node +* Exit node with https reverse proxy +* [Cashu Nutshell](https://github.com/cashubtc/nutshell) (backend service) +* [nostr-relay](https://github.com/scsibug/nostr-rs-relay) You can run the following commands to receive your nprofiles: @@ -65,9 +70,9 @@ When using https, the entry node can be used as a service, since the operator wi ## Build from source -The exit node must be set up to make the services reachable via Nostr. +The exit node must be set up to make your services reachable via Nostr. -### Configuration +### Exit node Configuration Configuration should be completed using environment variables. Alternatively, you can create a `.env` file in the current working directory with the following content: @@ -77,7 +82,7 @@ NOSTR_PRIVATE_KEY = "EXITPRIVATEHEX" BACKEND_HOST = 'localhost:3338' ``` -- `NOSTR_RELAYS`: A list of nostr relays to publish events to. Will only be used if there was no nprofile in the +- `NOSTR_RELAYS`: A list of nostr relays to publish events to. Will only be used if there was no relay data in the request. - `NOSTR_PRIVATE_KEY`: The private key to sign the events - `BACKEND_HOST`: The host of the backend to forward requests to @@ -92,18 +97,18 @@ If your backend services support TLS, your service can now start using TLS encry --- +### Entry node Configuration + To run an entry node for accessing NWS services behind exit nodes, use the following command: ``` go run cmd/entry/main.go ``` - -#### Entry node Configuration - -If you used environment variables, no further configuration is needed. -For `.env` file configurations, do so in the current working directory with the following content: +If you don't want to use the `PUBLIC_ADDRESS` feature, no further configuration is needed. ``` -NOSTR_RELAYS = 'ws://localhost:6666;wss://relay.com' +PUBLIC_ADDRESS = ':' ``` -Here, NOSTR_RELAYS is a list of nostr relays to publish events to and will only be used if there was no nprofile in the request. +- `PUBLIC_ADDRESS`: This can be set if the entry node is publicly available. When set, the entry node will additionally bind to this address. Exit node discovery will still be done using Nostr. Once a connection is established, this public address will be used to transmit further data. +- `NOSTR_RELAYS`: A list of nostr relays to publish events to. Will only be used if there was no relay data in the + request.