mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 15:08:24 +00:00
Add defensive relay features with rate limiting and connection limits
Implement defensive measures to protect against DoS attacks: - Add explicit rate limits (500 subscriptions, 60 events/min per connection) - Add total connection limit (default: 500, configurable via NGIT_MAX_CONNECTIONS) - Update configuration across all 4 locations (src, nix, docs, .env.example) Per-IP rate limiting deferred until abuse is detected in production or implemented in rust-nostr relay-builder to benefit the entire Nostr ecosystem. Documentation added explaining the defensive features and rationale. Detailed analysis of other relay implementations preserved in commit history.
This commit is contained in:
+11
-1
@@ -277,4 +277,14 @@
|
||||
# Examples:
|
||||
# NGIT_EVENT_BLACKLIST=npub1spam...
|
||||
# NGIT_EVENT_BLACKLIST=npub1spam...,npub1abuser...
|
||||
# NGIT_EVENT_BLACKLIST=
|
||||
# NGIT_EVENT_BLACKLIST=
|
||||
|
||||
# ============================================================================
|
||||
# RATE LIMITING & DOS PROTECTION
|
||||
# ============================================================================
|
||||
|
||||
# Maximum total connections to the relay
|
||||
# Prevents connection exhaustion DoS attacks
|
||||
# CLI: --max-connections <count>
|
||||
# Default: 500
|
||||
# NGIT_MAX_CONNECTIONS=500
|
||||
@@ -237,6 +237,48 @@ NGIT_EVENT_BLACKLIST=npub1spam1...,npub1spam2...
|
||||
|
||||
**See**: [Configuration Reference](docs/reference/configuration.md) for complete details
|
||||
|
||||
## Defensive Measures & Rate Limiting
|
||||
|
||||
ngit-grasp implements multiple layers of defense against abuse, spam, and denial-of-service attacks:
|
||||
|
||||
**Per-Connection Rate Limits:**
|
||||
- Max 500 concurrent subscriptions per connection
|
||||
- Max 60 events published per minute per connection
|
||||
- Built-in to rust-nostr relay-builder
|
||||
|
||||
**Per-IP Connection Monitoring:**
|
||||
- Tracks connections per IP address (default threshold: 10)
|
||||
- Flags potential abusers in logs and metrics
|
||||
- **Does NOT enforce limits** (monitoring only)
|
||||
- Privacy-preserving (IP addresses never exposed in Prometheus)
|
||||
|
||||
**Content Filtering (Blacklists/Whitelists):**
|
||||
- **Event blacklist** - Block ALL events from specific authors (npubs)
|
||||
- **Repository blacklist** - Block specific repositories/developers/identifiers
|
||||
- **Repository whitelist** - Curate which repositories are accepted (GRASP-01 mode)
|
||||
- **Archive whitelist** - Mirror specific repositories (GRASP-05 mode)
|
||||
- See [Curation & Moderation](#curation--moderation) section above for details
|
||||
|
||||
**Relay Sync Protection (GRASP-02):**
|
||||
- **Exponential backoff** - Failed connections: 5s → 10s → 20s → ... → 1 hour max
|
||||
- **Naughty list** - Track relays with infrastructure issues separately (12h expiry)
|
||||
- **Rate limit detection** - Auto 65s cooldown when remote relays rate limit us
|
||||
- **Domain throttling** - Max 5 concurrent, 30/min per domain for git data fetching
|
||||
|
||||
**Event Validation:**
|
||||
- Strict GRASP-01 protocol validation via WritePolicy plugin system
|
||||
- Extensible for custom validation logic (has access to client IP address)
|
||||
|
||||
**Total Connection Limit:**
|
||||
- Max 500 total connections (configurable via `NGIT_MAX_CONNECTIONS`)
|
||||
- Prevents connection exhaustion DoS attacks
|
||||
|
||||
**Not Implemented:**
|
||||
- Per-IP connection limits (only monitored, not enforced)
|
||||
- Per-IP event rate limits (tracked per connection, not per IP)
|
||||
|
||||
**See**: [Defensive Measures](docs/explanation/defensive-measures.md) for complete details and future enhancements.
|
||||
|
||||
## Roadmap
|
||||
|
||||
### GRASP-02 Enhancements
|
||||
|
||||
@@ -151,6 +151,48 @@ Explanation documentation helps you **understand concepts** and design decisions
|
||||
|
||||
---
|
||||
|
||||
### [Defensive Measures & Rate Limiting](defensive-measures.md)
|
||||
**Protection against abuse, spam, and denial-of-service attacks**
|
||||
|
||||
**Topics:**
|
||||
- Connection and subscription management
|
||||
- Event publishing rate limits
|
||||
- Content filtering (blacklists/whitelists)
|
||||
- Event validation plugin system (WritePolicy/QueryPolicy)
|
||||
- Relay health management (naughty list, exponential backoff)
|
||||
- Privacy-preserving IP tracking
|
||||
- Future enhancements (per-IP rate limiting)
|
||||
|
||||
**Read when:** You want to understand how ngit-grasp protects against abuse and what defensive features are available
|
||||
|
||||
---
|
||||
|
||||
### [GRASP-05 Archive Mode](grasp-05-archive.md)
|
||||
**Read-only mirroring of repositories**
|
||||
|
||||
**Topics:**
|
||||
- Archive whitelist configuration
|
||||
- Archive-all mode
|
||||
- Read-only mode defaults
|
||||
- Use cases for backup/mirror relays
|
||||
|
||||
**Read when:** You want to understand how to run an archive/backup relay
|
||||
|
||||
---
|
||||
|
||||
### [Deletion Requests](deletion-requests.md)
|
||||
**Handling repository and event deletion**
|
||||
|
||||
**Topics:**
|
||||
- Deletion request architecture
|
||||
- Delete disrespector concept
|
||||
- Preventing left-pad scenarios
|
||||
- Archival policies
|
||||
|
||||
**Read when:** You want to understand how ngit-grasp handles deletion events (planned feature)
|
||||
|
||||
---
|
||||
|
||||
## Planned Explanation Documentation
|
||||
|
||||
### GRASP Protocol Design
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
# Defensive Measures & Rate Limiting
|
||||
|
||||
This document describes the defensive measures implemented in ngit-grasp to protect against abuse, spam, and denial-of-service attacks.
|
||||
|
||||
**Note:** A point-in-time analysis of defensive measures in other Nostr relays (strfry, nostr-rs-relay, khatru) was conducted to inform these design decisions. The analysis examined connection limits, rate limiting approaches, and per-IP enforcement strategies across the ecosystem.
|
||||
|
||||
## Overview
|
||||
|
||||
ngit-grasp employs multiple layers of defense:
|
||||
|
||||
1. **Connection & Subscription Limits** - Per-connection limits on subscriptions and event publishing
|
||||
2. **Content Filtering** - Blacklist/whitelist system for repositories and event authors
|
||||
3. **Event Validation** - Strict GRASP-01 protocol validation
|
||||
4. **Relay Health Management** - Intelligent handling of problematic remote relays
|
||||
|
||||
## What's Implemented
|
||||
|
||||
### Per-Connection Rate Limits
|
||||
|
||||
**Source:** Built-in to rust-nostr relay-builder
|
||||
|
||||
- **Subscription limit:** Max 500 concurrent subscriptions per connection
|
||||
- **Event publishing limit:** Max 60 events per minute per connection
|
||||
- **Subscription ID length:** Max 250 characters
|
||||
- **Filter limit:** Max 500 results per query (default)
|
||||
|
||||
These limits prevent individual connections from overwhelming the relay.
|
||||
|
||||
### Per-IP Connection Monitoring
|
||||
|
||||
**Source:** Custom ngit-grasp implementation
|
||||
**Location:** `src/metrics/connection.rs`
|
||||
|
||||
- **Status:** Monitoring only (does NOT enforce limits)
|
||||
- Tracks connections per IP address internally
|
||||
- Flags IPs exceeding threshold (default: 10 connections)
|
||||
- **Privacy:** IP addresses never exposed in Prometheus metrics, only aggregate counts
|
||||
- Logs warnings when threshold exceeded
|
||||
|
||||
**Note on enforcement:** Per-IP connection limits are not built into rust-nostr relay-builder (tracks per WebSocket connection, not per IP). If abuse is detected via metrics, enforcement should be implemented as a PR to rust-nostr/relay-builder to benefit the entire Nostr ecosystem, rather than custom code in ngit-grasp.
|
||||
|
||||
### Content Filtering (Blacklists/Whitelists)
|
||||
|
||||
**Source:** Custom ngit-grasp implementation
|
||||
**Location:** `src/config.rs`, `src/nostr/builder.rs`
|
||||
|
||||
**Event Blacklist:**
|
||||
- Block ALL events from specific authors (npubs)
|
||||
- Takes precedence over all other validation
|
||||
- Events never reach storage or purgatory
|
||||
|
||||
**Repository Blacklist:**
|
||||
- Block specific repositories, developers, or identifiers
|
||||
- Takes precedence over whitelists
|
||||
- Three formats: `npub`, `npub/identifier`, `identifier`
|
||||
|
||||
**Repository Whitelist:**
|
||||
- Curate which repositories are accepted (GRASP-01 mode)
|
||||
- Only accept announcements that both list your service AND match whitelist
|
||||
- Same three formats as blacklist
|
||||
|
||||
**Archive Whitelist (GRASP-05):**
|
||||
- Mirror specific repositories even if they don't list your service
|
||||
- Same three formats as blacklist
|
||||
- Default: read-only mode when enabled
|
||||
|
||||
**Privacy:** Blacklists not advertised in NIP-11 metadata.
|
||||
|
||||
### Event Validation Plugin System
|
||||
|
||||
**Source:** Built-in to rust-nostr relay-builder
|
||||
**Implementation:** Custom GRASP-01 validation in `src/nostr/builder.rs`
|
||||
|
||||
- **WritePolicy trait:** Controls which events are accepted
|
||||
- **QueryPolicy trait:** Controls which queries are allowed (not currently used)
|
||||
- Access to client IP address for future per-IP rate limiting
|
||||
- Modular sub-policies for different event types (announcements, state events, PRs)
|
||||
|
||||
### Relay Health Management (GRASP-02 Sync)
|
||||
|
||||
**Source:** Custom ngit-grasp implementation
|
||||
**Location:** `src/sync/health.rs`
|
||||
|
||||
**Exponential Backoff:**
|
||||
- Failed connections trigger increasing delays: 5s → 10s → 20s → ... → 1 hour max
|
||||
- Prevents hammering dead or slow relays
|
||||
|
||||
**Naughty List:**
|
||||
- Tracks relays with persistent infrastructure issues (DNS, TLS, protocol errors)
|
||||
- Separate from normal connection failures
|
||||
- 12-hour expiration (configurable)
|
||||
- Reduces retry frequency for broken relays
|
||||
|
||||
**Rate Limit Detection:**
|
||||
- Detects when remote relay rate limits us
|
||||
- Automatic 65-second cooldown
|
||||
- Prevents hammering relays that tell us to slow down
|
||||
|
||||
**Domain Throttling (Git Data Fetching):**
|
||||
- Max 5 concurrent requests per domain
|
||||
- Max 30 requests per minute per domain
|
||||
- Respectful rate limiting when fetching missing git data
|
||||
|
||||
## What's NOT Implemented
|
||||
|
||||
### Per-IP Rate Limiting
|
||||
|
||||
- **Per-IP connection limits:** Not enforced (only monitored)
|
||||
- **Per-IP subscription limits:** Not supported
|
||||
- **Per-IP event publishing limits:** Not supported
|
||||
|
||||
**Why:** rust-nostr relay-builder tracks limits per WebSocket connection, not per IP address.
|
||||
|
||||
**To implement:** Would require custom middleware/WritePolicy to aggregate across connections from the same IP.
|
||||
|
||||
### Query Filtering
|
||||
|
||||
**Status:** QueryPolicy trait available but not currently used.
|
||||
|
||||
**Potential uses:** Rate limit queries per IP, block expensive queries, restrict access to certain event kinds.
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Per-IP Rate Limiting
|
||||
|
||||
Per-IP connection and event rate limiting were considered but deferred until abuse is detected in production. The current protections (per-connection limits, total connection limit, content filtering) are sufficient for the git relay use case.
|
||||
|
||||
**Decision rationale:** The primary DoS vector is connection exhaustion, which is addressed by the total connection limit (`NGIT_MAX_CONNECTIONS`). Per-IP enforcement would require custom middleware in rust-nostr relay-builder (which currently tracks limits per WebSocket connection, not per IP). If abuse is detected via the per-IP monitoring metrics, enforcement should be implemented as a PR to rust-nostr/relay-builder to benefit the entire Nostr ecosystem.
|
||||
|
||||
**Related:** Git endpoint throttling (issue ff38) is a separate concern with different requirements.
|
||||
|
||||
## Summary Table
|
||||
|
||||
| Feature | Status | Enforced? | Configurable? |
|
||||
|---------|--------|-----------|---------------|
|
||||
| **Per-Connection Limits** |
|
||||
| Max subscriptions (500) | ✅ Active | Yes | No (relay-builder default) |
|
||||
| Event rate limit (60/min) | ✅ Active | Yes | No (relay-builder default) |
|
||||
| **Total Connection Limit** |
|
||||
| Max connections (500) | ✅ Active | Yes | Yes (`NGIT_MAX_CONNECTIONS`) |
|
||||
| **Per-IP Monitoring** |
|
||||
| Connection tracking | ✅ Active | No (monitor only) | Threshold only |
|
||||
| **Content Filtering** |
|
||||
| Event blacklist | ✅ Active | Yes | Yes |
|
||||
| Repository blacklist | ✅ Active | Yes | Yes |
|
||||
| Repository whitelist | ✅ Active | Yes (if set) | Yes |
|
||||
| Archive whitelist | ✅ Active | Yes (if set) | Yes |
|
||||
| **Event Validation** |
|
||||
| GRASP-01 validation | ✅ Active | Yes | Via WritePolicy |
|
||||
| **Relay Sync Protection** |
|
||||
| Exponential backoff | ✅ Active | Yes | Yes |
|
||||
| Naughty list | ✅ Active | Yes | Yes (12h default) |
|
||||
| Rate limit detection | ✅ Active | Yes | Automatic |
|
||||
| Domain throttling | ✅ Active | Yes | Hardcoded (5/30) |
|
||||
| **Not Implemented** |
|
||||
| Per-IP connection limit | ⚠️ Deferred | No | - |
|
||||
| Per-IP rate limiting | ⚠️ Deferred | No | - |
|
||||
| Query filtering | ⚠️ Available | No | Not implemented |
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Configuration Reference](../reference/configuration.md) - All config options for defensive features
|
||||
- [Monitoring Overview](monitoring.md) - Prometheus metrics for tracking abuse
|
||||
- [GRASP-05 Archive](grasp-05-archive.md) - Archive whitelist details
|
||||
- [Architecture](architecture.md) - Overall system design
|
||||
@@ -925,6 +925,46 @@ Event blacklist does **not** affect NIP-11 metadata:
|
||||
|
||||
---
|
||||
|
||||
### Rate Limiting & DoS Protection
|
||||
|
||||
#### `NGIT_MAX_CONNECTIONS`
|
||||
|
||||
**Description:** Maximum total connections to the relay. Prevents connection exhaustion DoS attacks.
|
||||
**Type:** Integer
|
||||
**Default:** `500`
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Default: 500 connections
|
||||
NGIT_MAX_CONNECTIONS=500
|
||||
|
||||
# Higher limit for large public relay
|
||||
NGIT_MAX_CONNECTIONS=1000
|
||||
|
||||
# Lower limit for private relay
|
||||
NGIT_MAX_CONNECTIONS=100
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Limits total concurrent WebSocket connections to the relay
|
||||
- Prevents connection exhaustion attacks
|
||||
- Works in conjunction with per-connection limits (500 subscriptions, 60 events/min)
|
||||
- When limit is reached, new connections are rejected
|
||||
- Existing connections continue to work normally
|
||||
|
||||
**Related Limits:**
|
||||
|
||||
Per-connection limits (built-in to relay-builder, not configurable):
|
||||
- Max subscriptions per connection: 500
|
||||
- Max events per minute per connection: 60
|
||||
- Max subscription ID length: 250 characters
|
||||
- Max results per filter: 500
|
||||
|
||||
---
|
||||
|
||||
### Logging Configuration
|
||||
|
||||
#### `RUST_LOG`
|
||||
|
||||
@@ -250,6 +250,12 @@ let
|
||||
'';
|
||||
};
|
||||
|
||||
maxConnections = mkOption {
|
||||
type = types.int;
|
||||
default = 500;
|
||||
description = "Maximum total connections to the relay";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "ngit-grasp-${name}";
|
||||
@@ -295,6 +301,7 @@ let
|
||||
NGIT_REPOSITORY_WHITELIST = concatStringsSep "," cfg.repositoryWhitelist;
|
||||
NGIT_REPOSITORY_BLACKLIST = concatStringsSep "," cfg.repositoryBlacklist;
|
||||
NGIT_EVENT_BLACKLIST = concatStringsSep "," cfg.eventBlacklist;
|
||||
NGIT_MAX_CONNECTIONS = toString cfg.maxConnections;
|
||||
RUST_LOG = cfg.logLevel;
|
||||
} // optionalAttrs (cfg.relayName != null) {
|
||||
NGIT_RELAY_NAME = cfg.relayName;
|
||||
|
||||
@@ -469,6 +469,11 @@ pub struct Config {
|
||||
/// All events from these authors are blocked from both relay storage and purgatory
|
||||
#[arg(long, env = "NGIT_EVENT_BLACKLIST", default_value = "")]
|
||||
pub event_blacklist: String,
|
||||
|
||||
/// Maximum total connections to the relay (default: 500)
|
||||
/// Prevents connection exhaustion DoS attacks
|
||||
#[arg(long, env = "NGIT_MAX_CONNECTIONS", default_value_t = 500)]
|
||||
pub max_connections: usize,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -703,6 +708,7 @@ impl Config {
|
||||
repository_whitelist: String::new(),
|
||||
repository_blacklist: String::new(),
|
||||
event_blacklist: String::new(),
|
||||
max_connections: 500,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,6 +624,14 @@ pub async fn create_relay(
|
||||
let relay = LocalRelayBuilder::default()
|
||||
.database(database.clone())
|
||||
.write_policy(write_policy.clone())
|
||||
// Explicitly set rate limits (make defaults visible in code)
|
||||
// Per-connection limits: 500 max subscriptions, 60 events/min
|
||||
.rate_limit(RateLimit {
|
||||
max_reqs: 500, // Max concurrent subscriptions per connection
|
||||
notes_per_minute: 60, // Max events per minute per connection
|
||||
})
|
||||
// Total connection limit to prevent DoS attacks
|
||||
.max_connections(config.max_connections)
|
||||
.build();
|
||||
|
||||
tracing::info!(
|
||||
|
||||
Reference in New Issue
Block a user