ChainSafe / ChainSafe/lodestar

Enable discv5 Inbound Rate Limiting in Lodestar

Open
#9,930 0 comments 0 reactions 0 assignees View on GitHub
meta-bug
Dominant language
TypeScript
Stars
1.4k
Forks
483
Avg merge
1d 16h
Merged PRs (30d)
150

Description

### Describe the bug

## Summary

The discv5 implementation supports an optional inbound rate limiter (two-layer
GCRA: global + per-IP), but the beacon node never turns it on. In the default
configuration, a Lodestar node's discv5 UDP port accepts an unbounded inbound
packet rate.

To pre-empt a possible confusion: Lodestar *does* rate-limit ReqResp methods
(`beacon-node/src/network/reqresp/rateLimit.ts`, per-RPC quotas) — that is a different layer
and unaffected. This issue is only about discv5 UDP.

## Evidence (verified at the analyzed revision `lodestar@98e4089`)

**1. The limiter is conditional in the receive path**
(`@chainsafe/discv5`, `src/transport/udp.ts`, `handleIncoming`):

```ts
private handleIncoming = (data, rinfo) => {
if (this.rateLimiter && !this.rateLimiter.allowEncodedPacket(rinfo.address)) {
return;
}
...decodePacket(this.srcId, data)...
}
```

**2. The limiter is only constructed when the option is provided**
(`@chainsafe/discv5`, `src/service/service.ts`):

```ts
const rateLimiter = opts.rateLimiterOpts && new RateLimiter(opts.rateLimiterOpts, ...);
```

**3. The beacon node never passes it — and the worker type cannot carry it**
(`packages/beacon-node/src/network/discv5/worker.ts`):

```ts
const discv5 = Discv5.create({
enr, privateKey, bindAddrs, config, metricsRegistry,
// no rateLimiterOpts
})
```

Moreover, `Discv5WorkerData` (`packages/beacon-node/src/network/discv5/types.ts`)
has **no `rateLimiterOpts` field at all**, so enabling the limiter requires
adding the field to the worker data type and plumbing it through the network
config — it is an integration gap, not a one-liner.

## Cross client comparison

How the other consensus clients handle discv5 receive-side rate limiting by
default:

| Client | Default receive-side limiting on the discv5 UDP port |
|---|---|
| Lighthouse | Token bucket via `discv5::RateLimiterBuilder`: total 10 pkt/s, per-IP 9/s, per-node 8/s, 1 h IP ban on abuse (`lighthouse_network/src/config.rs`) |
| Grandine | Same as Lighthouse (inherited through its `eth2_libp2p` fork) |
| Teku | Netty `ChannelTrafficShapingHandler`, 250,000 bytes/s read limit on the discovery socket |
| Prysm | None (reported separately) |
| **Lodestar** | **Limiter implemented in the discv5 library but not enabled by the node** |

So in the current default landscape Lodestar is one of two clients with no
active discv5 rate limiter, and the only one whose limiter exists but is
unwired.

## Impact

With no bound on the inbound packet rate, CPU and bandwidth spent on discv5
packet parsing is attacker-controllable. Because discv5 runs over UDP and
source addresses can be spoofed, a per-IP-only limiter would not be sufficient
anyway — the global layer is what carries the defense, and the library already
implements the global+per-IP combination. The gap is integration, not missing
functionality.

### Expected behavior

Lodestar should enable the discv5 library's inbound rate limiter by default
with conservative global and per-IP limits, while allowing operators to tune
the limits when needed. Abuse-related IP bans should also expire after a
bounded period rather than remain permanent.

### Steps to reproduce

_No response_

### Additional context

_No response_

### Operating system

Linux

### Lodestar version or commit hash

v1.39.0

Contributor guide

Open the contributing guide

Research direction

Start by reading packages/beacon-node/src/network/discv5/worker.ts and types.ts, then trace how network configuration reaches Discv5.create and the @chainsafe/discv5 rateLimiterOpts option. Done means the limiter is enabled by default with configurable global and per-IP limits, bounded abuse-ban expiry, and coverage for the worker-data plumbing.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
networking, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.