Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Replace persistent P2P connection gater with a no-op variant so stale blocklist entries can no longer prevent peers from connecting after a restart. The new `p2p.disable_connection_gater` flag (default `true`) can be set to `false` to re-enable peer filtering when experiencing P2P flooding [#3273](https://github.com/evstack/ev-node/pull/3273)
- Raft HA production hardening: leader fencing on SIGTERM, FSM data race, follower restart crash, log compaction config, and election timeout validation [#3230](https://github.com/evstack/ev-node/pull/3230)

### Changes
Expand Down
13 changes: 9 additions & 4 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ const (
FlagP2PBlockedPeers = FlagPrefixEvnode + "p2p.blocked_peers"
// FlagP2PAllowedPeers is a flag for specifying the P2P allowed peers
FlagP2PAllowedPeers = FlagPrefixEvnode + "p2p.allowed_peers"
// FlagP2PDisableConnectionGater disables the P2P connection gater (no-op mode).
// Enabled by default; set to false to activate peer filtering when experiencing P2P flooding.
FlagP2PDisableConnectionGater = FlagPrefixEvnode + "p2p.disable_connection_gater"

// Instrumentation configuration flags

Expand Down Expand Up @@ -313,10 +316,11 @@ type LogConfig struct {

// P2PConfig contains all peer-to-peer networking configuration parameters
type P2PConfig struct {
ListenAddress string `mapstructure:"listen_address" yaml:"listen_address" comment:"Address to listen for incoming connections (host:port)"`
Peers string `mapstructure:"peers" yaml:"peers" comment:"Comma-separated list of peers to connect to"`
BlockedPeers string `mapstructure:"blocked_peers" yaml:"blocked_peers" comment:"Comma-separated list of peer IDs to block from connecting"`
AllowedPeers string `mapstructure:"allowed_peers" yaml:"allowed_peers" comment:"Comma-separated list of peer IDs to allow connections from"`
ListenAddress string `mapstructure:"listen_address" yaml:"listen_address" comment:"Address to listen for incoming connections (host:port)"`
Peers string `mapstructure:"peers" yaml:"peers" comment:"Comma-separated list of peers to connect to"`
BlockedPeers string `mapstructure:"blocked_peers" yaml:"blocked_peers" comment:"Comma-separated list of peer IDs to block from connecting"`
AllowedPeers string `mapstructure:"allowed_peers" yaml:"allowed_peers" comment:"Comma-separated list of peer IDs to allow connections from"`
DisableConnectionGater bool `mapstructure:"disable_connection_gater" yaml:"disable_connection_gater" comment:"Disable the P2P connection gater (no-op mode). Set to false to enforce peer filtering when experiencing P2P flooding."`
}

// SignerConfig contains all signer configuration parameters
Expand Down Expand Up @@ -621,6 +625,7 @@ func AddFlags(cmd *cobra.Command) {
cmd.Flags().String(FlagP2PPeers, def.P2P.Peers, "Comma separated list of seed nodes to connect to")
cmd.Flags().String(FlagP2PBlockedPeers, def.P2P.BlockedPeers, "Comma separated list of nodes to ignore")
cmd.Flags().String(FlagP2PAllowedPeers, def.P2P.AllowedPeers, "Comma separated list of nodes to whitelist")
cmd.Flags().Bool(FlagP2PDisableConnectionGater, def.P2P.DisableConnectionGater, "Disable P2P connection gater (no-op mode); set to false to enforce peer filtering when experiencing P2P flooding")

// RPC configuration flags
cmd.Flags().String(FlagRPCAddress, def.RPC.Address, "RPC server address (host:port)")
Expand Down
3 changes: 2 additions & 1 deletion pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func TestAddFlags(t *testing.T) {
assertFlagValue(t, flags, FlagP2PPeers, DefaultConfig().P2P.Peers)
assertFlagValue(t, flags, FlagP2PBlockedPeers, DefaultConfig().P2P.BlockedPeers)
assertFlagValue(t, flags, FlagP2PAllowedPeers, DefaultConfig().P2P.AllowedPeers)
assertFlagValue(t, flags, FlagP2PDisableConnectionGater, DefaultConfig().P2P.DisableConnectionGater)

// Instrumentation flags
instrDef := DefaultInstrumentationConfig()
Expand Down Expand Up @@ -143,7 +144,7 @@ func TestAddFlags(t *testing.T) {
assertFlagValue(t, flags, FlagPruningInterval, DefaultConfig().Pruning.Interval.Duration)

// Count the number of flags we're explicitly checking
expectedFlagCount := 81 // Update this number if you add more flag checks above
expectedFlagCount := 82 // Update this number if you add more flag checks above

// Get the actual number of flags (both regular and persistent)
actualFlagCount := 0
Expand Down
5 changes: 3 additions & 2 deletions pkg/config/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ func DefaultConfig() Config {
RootDir: DefaultRootDir,
DBPath: "data",
P2P: P2PConfig{
ListenAddress: "/ip4/0.0.0.0/tcp/7676",
Peers: "",
ListenAddress: "/ip4/0.0.0.0/tcp/7676",
Peers: "",
DisableConnectionGater: true,
},
Node: NodeConfig{
Aggregator: false,
Expand Down
35 changes: 25 additions & 10 deletions pkg/p2p/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,17 @@ func NewClient(
metrics *Metrics,
) (*Client, error) {

gater, err := conngater.NewBasicConnectionGater(ds)
// When DisableConnectionGater is true (default) the gater is a no-op: it
// uses an ephemeral in-memory store, is not registered with the libp2p host,
// and never blocks any peer. The instance is kept only because go-header's
// Exchange requires a *conngater.BasicConnectionGater parameter.
// Set DisableConnectionGater=false to activate peer filtering (e.g. when
// experiencing P2P flooding).
var gaterDS datastore.Datastore
if !conf.DisableConnectionGater {
gaterDS = datastore.NewMapDatastore()
}
gater, err := conngater.NewBasicConnectionGater(gaterDS)
if err != nil {
return nil, fmt.Errorf("failed to create connection gater: %w", err)
}
Expand Down Expand Up @@ -156,14 +166,15 @@ func (c *Client) startWithHost(ctx context.Context, h host.Host) error {
c.logger.Info().Str("address", fmt.Sprintf("%s/p2p/%s", a, c.host.ID())).Msg("listening on address")
}

c.logger.Debug().Str("blacklist", c.conf.BlockedPeers).Msg("blocking blacklisted peers")
if err := c.setupBlockedPeers(c.parseAddrInfoList(c.conf.BlockedPeers)); err != nil {
return err
}

c.logger.Debug().Str("whitelist", c.conf.AllowedPeers).Msg("allowing whitelisted peers")
if err := c.setupAllowedPeers(c.parseAddrInfoList(c.conf.AllowedPeers)); err != nil {
return err
if !c.conf.DisableConnectionGater {
c.logger.Debug().Str("blacklist", c.conf.BlockedPeers).Msg("blocking blacklisted peers")
if err := c.setupBlockedPeers(c.parseAddrInfoList(c.conf.BlockedPeers)); err != nil {
return err
}
c.logger.Debug().Str("whitelist", c.conf.AllowedPeers).Msg("allowing whitelisted peers")
if err := c.setupAllowedPeers(c.parseAddrInfoList(c.conf.AllowedPeers)); err != nil {
return err
}
}
Comment on lines +169 to 178
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Warn when BlockedPeers/AllowedPeers are configured but ignored.

When DisableConnectionGater is true (the default), any values set in conf.BlockedPeers or conf.AllowedPeers are silently dropped — the flags remain wired in AddFlags and the struct fields still exist, so operators can easily set them without realizing they have no effect. Consider emitting a warning log here (or at startup) if either list is non-empty while the gater is disabled, so misconfiguration is surfaced instead of hidden.

Proposed fix
 	if !c.conf.DisableConnectionGater {
 		c.logger.Debug().Str("blacklist", c.conf.BlockedPeers).Msg("blocking blacklisted peers")
 		if err := c.setupBlockedPeers(c.parseAddrInfoList(c.conf.BlockedPeers)); err != nil {
 			return err
 		}
 		c.logger.Debug().Str("whitelist", c.conf.AllowedPeers).Msg("allowing whitelisted peers")
 		if err := c.setupAllowedPeers(c.parseAddrInfoList(c.conf.AllowedPeers)); err != nil {
 			return err
 		}
+	} else if c.conf.BlockedPeers != "" || c.conf.AllowedPeers != "" {
+		c.logger.Warn().
+			Str("blocked_peers", c.conf.BlockedPeers).
+			Str("allowed_peers", c.conf.AllowedPeers).
+			Msg("p2p.blocked_peers / p2p.allowed_peers are set but ignored because p2p.disable_connection_gater=true")
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if !c.conf.DisableConnectionGater {
c.logger.Debug().Str("blacklist", c.conf.BlockedPeers).Msg("blocking blacklisted peers")
if err := c.setupBlockedPeers(c.parseAddrInfoList(c.conf.BlockedPeers)); err != nil {
return err
}
c.logger.Debug().Str("whitelist", c.conf.AllowedPeers).Msg("allowing whitelisted peers")
if err := c.setupAllowedPeers(c.parseAddrInfoList(c.conf.AllowedPeers)); err != nil {
return err
}
}
if !c.conf.DisableConnectionGater {
c.logger.Debug().Str("blacklist", c.conf.BlockedPeers).Msg("blocking blacklisted peers")
if err := c.setupBlockedPeers(c.parseAddrInfoList(c.conf.BlockedPeers)); err != nil {
return err
}
c.logger.Debug().Str("whitelist", c.conf.AllowedPeers).Msg("allowing whitelisted peers")
if err := c.setupAllowedPeers(c.parseAddrInfoList(c.conf.AllowedPeers)); err != nil {
return err
}
} else if c.conf.BlockedPeers != "" || c.conf.AllowedPeers != "" {
c.logger.Warn().
Str("blocked_peers", c.conf.BlockedPeers).
Str("allowed_peers", c.conf.AllowedPeers).
Msg("p2p.blocked_peers / p2p.allowed_peers are set but ignored because p2p.disable_connection_gater=true")
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/p2p/client.go` around lines 169 - 178, If DisableConnectionGater is true
but conf.BlockedPeers or conf.AllowedPeers are non-empty, add a warning log so
operators know their settings are ignored; in the method containing the shown
block (the code that currently checks c.conf.DisableConnectionGater and calls
setupBlockedPeers/setupAllowedPeers), detect when c.conf.DisableConnectionGater
== true and (len(c.conf.BlockedPeers) > 0 || len(c.conf.AllowedPeers) > 0) and
emit a clear warning via c.logger.Warn() referencing the fields (e.g.,
"BlockedPeers configured but connection gater disabled") before
returning/continuing, rather than silently dropping the values.


c.logger.Debug().Msg("setting up gossiping")
Expand Down Expand Up @@ -340,7 +351,11 @@ func (c *Client) listen() (host.Host, error) {
return nil, err
}

return libp2p.New(libp2p.ListenAddrs(maddr), libp2p.Identity(c.privKey), libp2p.ConnectionGater(c.gater))
opts := []libp2p.Option{libp2p.ListenAddrs(maddr), libp2p.Identity(c.privKey)}
if !c.conf.DisableConnectionGater {
opts = append(opts, libp2p.ConnectionGater(c.gater))
}
return libp2p.New(opts...)
}

func (c *Client) setupDHT(ctx context.Context) error {
Expand Down
Loading