diff --git a/nodebuilder/p2p/addrs.go b/nodebuilder/p2p/addrs.go index 28f6e44407..1a928e3be2 100644 --- a/nodebuilder/p2p/addrs.go +++ b/nodebuilder/p2p/addrs.go @@ -9,10 +9,10 @@ import ( ) // Listen returns invoke function that starts listening for inbound connections with libp2p.Host. -func Listen(listen []string) func(h hst.Host) (err error) { +func Listen(cfg *Config) func(h hst.Host) (err error) { return func(h hst.Host) (err error) { - maListen := make([]ma.Multiaddr, len(listen)) - for i, addr := range listen { + maListen := make([]ma.Multiaddr, len(cfg.ListenAddresses)) + for i, addr := range cfg.ListenAddresses { maListen[i], err = ma.NewMultiaddr(addr) if err != nil { return fmt.Errorf("failure to parse config.P2P.ListenAddresses: %w", err) diff --git a/nodebuilder/p2p/config.go b/nodebuilder/p2p/config.go index c95f5a98cf..23ac3df49f 100644 --- a/nodebuilder/p2p/config.go +++ b/nodebuilder/p2p/config.go @@ -86,3 +86,18 @@ func (cfg *Config) Validate() error { } return nil } + +// Upgrade updates the `ListenAddresses` and `NoAnnounceAddresses` to +// include support for websocket connections. +func (cfg *Config) Upgrade() { + cfg.ListenAddresses = append( + cfg.ListenAddresses, + "/ip4/0.0.0.0/tcp/2122/wss", + "/ip6/::/tcp/2122/wss", + ) + cfg.NoAnnounceAddresses = append( + cfg.NoAnnounceAddresses, + "/ip4/127.0.0.1/tcp/2122/wss", + "/ip6/::/tcp/2122/wss", + ) +} diff --git a/nodebuilder/p2p/host.go b/nodebuilder/p2p/host.go index b54db37d25..5a7b210024 100644 --- a/nodebuilder/p2p/host.go +++ b/nodebuilder/p2p/host.go @@ -17,6 +17,9 @@ import ( "github.com/libp2p/go-libp2p/core/routing" routedhost "github.com/libp2p/go-libp2p/p2p/host/routed" "github.com/libp2p/go-libp2p/p2p/net/conngater" + quic "github.com/libp2p/go-libp2p/p2p/transport/quic" + "github.com/libp2p/go-libp2p/p2p/transport/tcp" + webtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport" "github.com/prometheus/client_golang/prometheus" "go.uber.org/fx" @@ -67,6 +70,15 @@ func (ua *UserAgent) String() string { func host(params hostParams) (HostBase, error) { ua := newUserAgent().WithNetwork(params.Net).WithNodeType(params.Tp) + tlsCfg, isEnabled, err := tlsEnabled() + if err != nil { + return nil, err + } + + if isEnabled { + params.Cfg.Upgrade() + } + opts := []libp2p.Option{ libp2p.NoListenAddrs, // do not listen automatically libp2p.AddrsFactory(params.AddrF), @@ -79,9 +91,14 @@ func host(params hostParams) (HostBase, error) { libp2p.DisableRelay(), libp2p.BandwidthReporter(params.Bandwidth), libp2p.ResourceManager(params.ResourceManager), + libp2p.ChainOptions( + libp2p.Transport(tcp.NewTCPTransport), + libp2p.Transport(quic.NewTransport), + libp2p.Transport(webtransport.New), + wsTransport(tlsCfg), + ), // to clearly define what defaults we rely upon libp2p.DefaultSecurity, - libp2p.DefaultTransports, libp2p.DefaultMuxers, } @@ -113,6 +130,7 @@ type HostBase hst.Host type hostParams struct { fx.In + Cfg *Config Net Network Lc fx.Lifecycle ID peer.ID diff --git a/nodebuilder/p2p/misc.go b/nodebuilder/p2p/misc.go index df02e0e623..bcbc82152f 100644 --- a/nodebuilder/p2p/misc.go +++ b/nodebuilder/p2p/misc.go @@ -44,7 +44,7 @@ func defaultConnManagerConfig(tp node.Type) connManagerConfig { } // connectionManager provides a constructor for ConnectionManager. -func connectionManager(cfg Config, bpeers Bootstrappers) (connmgri.ConnManager, error) { +func connectionManager(cfg *Config, bpeers Bootstrappers) (connmgri.ConnManager, error) { fpeers, err := cfg.mutualPeers() if err != nil { return nil, err diff --git a/nodebuilder/p2p/module.go b/nodebuilder/p2p/module.go index 1ddff02173..59d9fa5054 100644 --- a/nodebuilder/p2p/module.go +++ b/nodebuilder/p2p/module.go @@ -15,10 +15,9 @@ var log = logging.Logger("module/p2p") func ConstructModule(tp node.Type, cfg *Config) fx.Option { // sanitize config values before constructing module cfgErr := cfg.Validate() - baseComponents := fx.Options( - fx.Supply(*cfg), fx.Error(cfgErr), + fx.Supply(cfg), fx.Provide(Key), fx.Provide(id), fx.Provide(peerStore), @@ -34,7 +33,7 @@ func ConstructModule(tp node.Type, cfg *Config) fx.Option { fx.Provide(addrsFactory(cfg.AnnounceAddresses, cfg.NoAnnounceAddresses)), fx.Provide(metrics.NewBandwidthCounter), fx.Provide(newModule), - fx.Invoke(Listen(cfg.ListenAddresses)), + fx.Invoke(Listen(cfg)), fx.Provide(resourceManager), fx.Provide(resourceManagerOpt(allowList)), ) diff --git a/nodebuilder/p2p/pubsub.go b/nodebuilder/p2p/pubsub.go index 13d812e3ce..214592aafd 100644 --- a/nodebuilder/p2p/pubsub.go +++ b/nodebuilder/p2p/pubsub.go @@ -40,7 +40,7 @@ func init() { } // pubSub provides a constructor for PubSub protocol with GossipSub routing. -func pubSub(cfg Config, params pubSubParams) (*pubsub.PubSub, error) { +func pubSub(cfg *Config, params pubSubParams) (*pubsub.PubSub, error) { fpeers, err := cfg.mutualPeers() if err != nil { return nil, err @@ -122,7 +122,7 @@ func topicScoreParams(params pubSubParams) map[string]*pubsub.TopicScoreParams { return mp } -func peerScoreParams(bootstrappers Bootstrappers, cfg Config) (*pubsub.PeerScoreParams, error) { +func peerScoreParams(bootstrappers Bootstrappers, cfg *Config) (*pubsub.PeerScoreParams, error) { bootstrapperSet := map[peer.ID]struct{}{} for _, b := range bootstrappers { bootstrapperSet[b.ID] = struct{}{} diff --git a/nodebuilder/p2p/resources.go b/nodebuilder/p2p/resources.go index 6e24e1e542..86154557fe 100644 --- a/nodebuilder/p2p/resources.go +++ b/nodebuilder/p2p/resources.go @@ -25,7 +25,7 @@ func autoscaleResources() rcmgr.ConcreteLimitConfig { return limits.AutoScale() } -func allowList(ctx context.Context, cfg Config, bootstrappers Bootstrappers) (rcmgr.Option, error) { +func allowList(ctx context.Context, cfg *Config, bootstrappers Bootstrappers) (rcmgr.Option, error) { mutual, err := cfg.mutualPeers() if err != nil { return nil, err diff --git a/nodebuilder/p2p/routing.go b/nodebuilder/p2p/routing.go index 4006ec03a8..edbf624b08 100644 --- a/nodebuilder/p2p/routing.go +++ b/nodebuilder/p2p/routing.go @@ -21,7 +21,7 @@ func contentRouting(r routing.PeerRouting) routing.ContentRouting { // peerRouting provides constructor for PeerRouting over DHT. // Basically, this provides a way to discover peer addresses by respecting public keys. -func peerRouting(cfg Config, tp node.Type, params routingParams) (routing.PeerRouting, error) { +func peerRouting(cfg *Config, tp node.Type, params routingParams) (routing.PeerRouting, error) { opts := []dht.Option{ dht.BootstrapPeers(params.Peers...), dht.ProtocolPrefix(protocol.ID(fmt.Sprintf("/celestia/%s", params.Net))), diff --git a/nodebuilder/p2p/tls.go b/nodebuilder/p2p/tls.go new file mode 100644 index 0000000000..24d2e5f1e6 --- /dev/null +++ b/nodebuilder/p2p/tls.go @@ -0,0 +1,60 @@ +package p2p + +import ( + "crypto/tls" + "os" + "path/filepath" + + "github.com/libp2p/go-libp2p" + ws "github.com/libp2p/go-libp2p/p2p/transport/websocket" + + "github.com/celestiaorg/celestia-node/libs/utils" +) + +const ( + cert = "cert.pem" + key = "key.pem" +) + +var tlsPath = "CELESTIA_TLS_PATH" + +// tlsEnabled checks whether `tlsPath` is not empty and creates a certificate. +// it returns the cfg itself, the bool flag that specifies whether the config was created +// and an error. +func tlsEnabled() (*tls.Config, bool, error) { + path := os.Getenv(tlsPath) + if path == "" { + log.Debug("the CELESTIA_TLS_PATH was not set") + return nil, false, nil + } + + certPath := filepath.Join(path, cert) + keyPath := filepath.Join(path, key) + exist := utils.Exists(certPath) && utils.Exists(keyPath) + if !exist { + return nil, false, nil + } + + cert, err := tls.LoadX509KeyPair(certPath, keyPath) + if err != nil { + return nil, false, err + } + + return &tls.Config{ + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{cert}, + }, true, nil +} + +// wsTransport enables a support for the secure websocket connection +// using the passed tls config. The connection will be insecure in case +// config is empty. +func wsTransport(config *tls.Config) libp2p.Option { + if config == nil { + log.Info("using a default ws transport") + return libp2p.Transport(ws.New) + } + + log.Info("using a wss transport with tlsConfig") + return libp2p.Transport(ws.New, ws.WithTLSConfig(config)) +}