mirror of
https://github.com/getnora-io/nora.git
synced 2026-04-12 18:30:32 +00:00
- Add enabled field to RateLimitConfig (default: true, env: NORA_RATE_LIMIT_ENABLED) - Skip rate limiter layers entirely when disabled - Replace PeerIpKeyExtractor with SmartIpKeyExtractor for upload/general routes to correctly identify clients behind reverse proxies and Docker bridge networks - Keep PeerIpKeyExtractor for auth routes (stricter brute-force protection) Root cause: PeerIpKeyExtractor saw all Docker bridge traffic as single IP (172.17.0.1), exhausting GCRA bucket for all clients simultaneously. With burst=1M, recovery time reached 84000+ seconds.
121 lines
3.5 KiB
Rust
121 lines
3.5 KiB
Rust
// Copyright (c) 2026 Volkov Pavel | DevITWay
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
//! Rate limiting configuration and middleware
|
|
//!
|
|
//! Provides rate limiting to protect against:
|
|
//! - Brute-force authentication attacks
|
|
//! - DoS attacks on upload endpoints
|
|
//! - General API abuse
|
|
|
|
use crate::config::RateLimitConfig;
|
|
use tower_governor::governor::GovernorConfigBuilder;
|
|
use tower_governor::key_extractor::SmartIpKeyExtractor;
|
|
|
|
/// Create rate limiter layer for auth endpoints (strict protection against brute-force)
|
|
pub fn auth_rate_limiter(
|
|
config: &RateLimitConfig,
|
|
) -> tower_governor::GovernorLayer<
|
|
tower_governor::key_extractor::PeerIpKeyExtractor,
|
|
governor::middleware::StateInformationMiddleware,
|
|
axum::body::Body,
|
|
> {
|
|
let gov_config = GovernorConfigBuilder::default()
|
|
.per_second(config.auth_rps)
|
|
.burst_size(config.auth_burst)
|
|
.use_headers()
|
|
.finish()
|
|
.expect("Failed to build auth rate limiter");
|
|
|
|
tower_governor::GovernorLayer::new(gov_config)
|
|
}
|
|
|
|
/// Create rate limiter layer for upload endpoints
|
|
///
|
|
/// High limits to accommodate Docker client's aggressive parallel layer uploads
|
|
pub fn upload_rate_limiter(
|
|
config: &RateLimitConfig,
|
|
) -> tower_governor::GovernorLayer<
|
|
SmartIpKeyExtractor,
|
|
governor::middleware::StateInformationMiddleware,
|
|
axum::body::Body,
|
|
> {
|
|
let gov_config = GovernorConfigBuilder::default()
|
|
.key_extractor(SmartIpKeyExtractor)
|
|
.per_second(config.upload_rps)
|
|
.burst_size(config.upload_burst)
|
|
.use_headers()
|
|
.finish()
|
|
.expect("Failed to build upload rate limiter");
|
|
|
|
tower_governor::GovernorLayer::new(gov_config)
|
|
}
|
|
|
|
/// Create rate limiter layer for general endpoints (lenient)
|
|
pub fn general_rate_limiter(
|
|
config: &RateLimitConfig,
|
|
) -> tower_governor::GovernorLayer<
|
|
SmartIpKeyExtractor,
|
|
governor::middleware::StateInformationMiddleware,
|
|
axum::body::Body,
|
|
> {
|
|
let gov_config = GovernorConfigBuilder::default()
|
|
.key_extractor(SmartIpKeyExtractor)
|
|
.per_second(config.general_rps)
|
|
.burst_size(config.general_burst)
|
|
.use_headers()
|
|
.finish()
|
|
.expect("Failed to build general rate limiter");
|
|
|
|
tower_governor::GovernorLayer::new(gov_config)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::config::RateLimitConfig;
|
|
|
|
#[test]
|
|
fn test_default_config() {
|
|
let config = RateLimitConfig::default();
|
|
assert_eq!(config.auth_rps, 1);
|
|
assert_eq!(config.auth_burst, 5);
|
|
assert_eq!(config.upload_rps, 200);
|
|
assert_eq!(config.general_rps, 100);
|
|
}
|
|
|
|
#[test]
|
|
fn test_auth_rate_limiter_creation() {
|
|
let config = RateLimitConfig::default();
|
|
let _limiter = auth_rate_limiter(&config);
|
|
}
|
|
|
|
#[test]
|
|
fn test_upload_rate_limiter_creation() {
|
|
let config = RateLimitConfig::default();
|
|
let _limiter = upload_rate_limiter(&config);
|
|
}
|
|
|
|
#[test]
|
|
fn test_general_rate_limiter_creation() {
|
|
let config = RateLimitConfig::default();
|
|
let _limiter = general_rate_limiter(&config);
|
|
}
|
|
|
|
#[test]
|
|
fn test_custom_config() {
|
|
let config = RateLimitConfig {
|
|
enabled: true,
|
|
auth_rps: 10,
|
|
auth_burst: 20,
|
|
upload_rps: 500,
|
|
upload_burst: 1000,
|
|
general_rps: 200,
|
|
general_burst: 400,
|
|
};
|
|
let _auth = auth_rate_limiter(&config);
|
|
let _upload = upload_rate_limiter(&config);
|
|
let _general = general_rate_limiter(&config);
|
|
}
|
|
}
|