veza/veza-chat-server/src/core/advanced_rate_limiter.rs

829 lines
28 KiB
Rust
Raw Normal View History

2025-12-03 19:33:26 +00:00
use std::collections::{HashMap, VecDeque};
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::net::IpAddr;
use serde::{Deserialize, Serialize};
use dashmap::DashMap;
use tokio::sync::RwLock;
use parking_lot::Mutex;
use crate::error::{ChatError, Result};
/// Service de rate limiting avancé anti-DDoS
#[derive(Debug)]
pub struct AdvancedRateLimiter {
/// Limiteurs par IP
ip_limiters: Arc<DashMap<IpAddr, IpRateLimiter>>,
/// Limiteurs par utilisateur
user_limiters: Arc<DashMap<i64, UserRateLimiter>>,
/// Limiteurs par canal
channel_limiters: Arc<DashMap<String, ChannelRateLimiter>>,
/// Patterns d'attaque détectés
attack_patterns: Arc<DashMap<String, AttackPattern>>,
/// Liste noire temporaire
blacklist: Arc<DashMap<IpAddr, BlacklistEntry>>,
/// Configuration globale
config: Arc<RwLock<RateLimitConfig>>,
/// Métriques de performance
metrics: Arc<RateLimitMetrics>,
}
/// Limiteur par IP avec détection de patterns
#[derive(Debug)]
pub struct IpRateLimiter {
pub ip: IpAddr,
pub buckets: HashMap<LimitType, TokenBucket>,
pub last_activity: Instant,
pub violation_count: u32,
pub trust_score: f32,
pub request_patterns: VecDeque<RequestEvent>,
pub status: IpStatus,
}
/// Limiteur par utilisateur
#[derive(Debug)]
pub struct UserRateLimiter {
pub user_id: i64,
pub buckets: HashMap<LimitType, TokenBucket>,
pub last_activity: Instant,
pub violation_count: u32,
pub reputation: UserReputation,
pub daily_limits: DailyLimits,
}
/// Limiteur par canal
#[derive(Debug)]
pub struct ChannelRateLimiter {
pub channel_id: String,
pub message_bucket: TokenBucket,
pub concurrent_users: u32,
pub last_activity: Instant,
pub spam_threshold: f32,
pub moderation_level: ModerationLevel,
}
/// Implémentation du Token Bucket
#[derive(Debug, Clone)]
pub struct TokenBucket {
pub capacity: u32,
pub tokens: u32,
pub refill_rate: f32, // tokens per second
pub last_refill: Instant,
pub burst_allowance: u32,
}
/// Types de limitations
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum LimitType {
/// Messages par minute
MessagesPerMinute,
/// Connexions par heure
ConnectionsPerHour,
/// Tentatives d'authentification
AuthAttempts,
/// Requêtes API
ApiRequests,
/// Upload de fichiers
FileUploads,
/// Création de channels
ChannelCreation,
/// Invitations envoyées
Invitations,
/// Réactions ajoutées
Reactions,
}
/// Statut d'une IP
#[derive(Debug, Clone, PartialEq)]
pub enum IpStatus {
/// IP normale
Normal,
/// IP suspecte (surveillance accrue)
Suspicious,
/// IP en liste noire temporaire
Blacklisted,
/// IP bloquée définitivement
Banned,
/// IP de confiance (VPN/Proxy autorisé)
Trusted,
}
/// Réputation d'un utilisateur
#[derive(Debug, Clone)]
pub struct UserReputation {
pub score: f32, // 0.0 - 1.0
pub level: ReputationLevel,
pub violations_today: u32,
pub positive_actions: u32,
pub last_violation: Option<Instant>,
}
/// Niveau de réputation
#[derive(Debug, Clone, PartialEq)]
pub enum ReputationLevel {
NewUser, // Nouvels utilisateurs (restrictions strictes)
Normal, // Utilisateurs normaux
Trusted, // Utilisateurs de confiance
VIP, // Utilisateurs VIP (modérateurs, abonnés)
System, // Comptes système (bots officiels)
}
/// Limites quotidiennes
#[derive(Debug, Clone)]
pub struct DailyLimits {
pub messages_sent: u32,
pub max_messages: u32,
pub files_uploaded: u32,
pub max_files: u32,
pub reset_time: Instant,
}
/// Niveau de modération d'un canal
#[derive(Debug, Clone, PartialEq)]
pub enum ModerationLevel {
Low, // Modération allégée
Normal, // Modération standard
High, // Modération stricte
Lockdown, // Canal en verrouillage
}
/// Pattern d'attaque détecté
#[derive(Debug, Clone)]
pub struct AttackPattern {
pub pattern_id: String,
pub pattern_type: AttackType,
pub source_ips: Vec<IpAddr>,
pub detection_time: Instant,
pub severity: f32,
pub requests_count: u32,
pub geographic_spread: f32,
pub user_agents: Vec<String>,
}
/// Types d'attaques détectées
#[derive(Debug, Clone, PartialEq)]
pub enum AttackType {
/// Attaque DDoS classique
DDoS,
/// Spam de messages
MessageSpam,
/// Brute force sur l'authentification
BruteForce,
/// Scraping de données
Scraping,
/// Attaque par déni de service applicatif
SlowLoris,
/// Comportement suspect automatisé
BotActivity,
}
/// Entrée de liste noire
#[derive(Debug, Clone)]
pub struct BlacklistEntry {
pub ip: IpAddr,
pub reason: String,
pub blocked_at: Instant,
pub expires_at: Option<Instant>,
pub violation_count: u32,
pub auto_generated: bool,
}
/// Événement de requête pour la détection de patterns
#[derive(Debug, Clone)]
pub struct RequestEvent {
pub timestamp: Instant,
pub request_type: String,
pub path: String,
pub user_agent: Option<String>,
pub response_time: Duration,
pub status_code: u16,
}
/// Configuration du rate limiting
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimitConfig {
/// Messages par minute par utilisateur
pub messages_per_minute: u32,
/// Connexions par heure par IP
pub connections_per_hour: u32,
/// Tentatives d'auth par IP
pub auth_attempts_per_minute: u32,
/// Taille maximale des buckets
pub max_bucket_capacity: u32,
/// Seuil de détection d'attaque
pub attack_detection_threshold: f32,
/// Durée de blacklist automatique
pub auto_blacklist_duration: Duration,
/// Activar la géolocalisation
pub enable_geolocation: bool,
/// IPs de confiance (CDN, proxies autorisés)
pub trusted_ips: Vec<IpAddr>,
}
/// Métriques du rate limiting
#[derive(Debug, Default)]
pub struct RateLimitMetrics {
pub requests_processed: Arc<std::sync::atomic::AtomicU64>,
pub requests_blocked: Arc<std::sync::atomic::AtomicU64>,
pub attacks_detected: Arc<std::sync::atomic::AtomicU32>,
pub false_positives: Arc<std::sync::atomic::AtomicU32>,
pub avg_response_time: Arc<Mutex<Duration>>,
}
/// Résultat d'une vérification de rate limit
#[derive(Debug, Clone)]
pub struct RateLimitResult {
pub allowed: bool,
pub reason: Option<String>,
pub retry_after: Option<Duration>,
pub remaining_tokens: u32,
pub burst_remaining: u32,
pub reputation_impact: f32,
}
use std::collections::VecDeque;
impl AdvancedRateLimiter {
/// Crée un nouveau rate limiter avancé
pub fn new(config: RateLimitConfig) -> Self {
Self {
ip_limiters: Arc::new(DashMap::new()),
user_limiters: Arc::new(DashMap::new()),
channel_limiters: Arc::new(DashMap::new()),
attack_patterns: Arc::new(DashMap::new()),
blacklist: Arc::new(DashMap::new()),
config: Arc::new(RwLock::new(config)),
metrics: Arc::new(RateLimitMetrics::default()),
}
}
/// Vérifie si une requête est autorisée (point d'entrée principal)
pub async fn check_rate_limit(
&self,
ip: IpAddr,
user_id: Option<i64>,
channel_id: Option<String>,
limit_type: LimitType,
request_info: RequestInfo,
) -> Result<RateLimitResult> {
let start_time = Instant::now();
// Incrémenter les métriques
self.metrics.requests_processed.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
// 1. Vérifier la liste noire d'abord
if let Some(blacklist_entry) = self.blacklist.get(&ip) {
if blacklist_entry.expires_at.map_or(true, |exp| exp > Instant::now()) {
self.metrics.requests_blocked.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
return Ok(RateLimitResult {
allowed: false,
reason: Some(format!("IP blacklisted: {}", blacklist_entry.reason)),
retry_after: blacklist_entry.expires_at.map(|exp| exp.duration_since(Instant::now())),
remaining_tokens: 0,
burst_remaining: 0,
reputation_impact: -0.1,
});
} else {
// Entrée expirée, la supprimer
self.blacklist.remove(&ip);
}
}
// 2. Vérifier le rate limiting par IP
let ip_result = self.check_ip_rate_limit(ip, &limit_type, &request_info).await?;
if !ip_result.allowed {
return Ok(ip_result);
}
// 3. Vérifier le rate limiting par utilisateur si applicable
if let Some(uid) = user_id {
let user_result = self.check_user_rate_limit(uid, &limit_type).await?;
if !user_result.allowed {
return Ok(user_result);
}
}
// 4. Vérifier le rate limiting par canal si applicable
if let Some(cid) = channel_id {
let channel_result = self.check_channel_rate_limit(&cid, &limit_type).await?;
if !channel_result.allowed {
return Ok(channel_result);
}
}
// 5. Analyser les patterns d'attaque
self.analyze_request_pattern(ip, &request_info).await?;
// 6. Mettre à jour les métriques de performance
let elapsed = start_time.elapsed();
*self.metrics.avg_response_time.lock() = elapsed;
Ok(RateLimitResult {
allowed: true,
reason: None,
retry_after: None,
remaining_tokens: ip_result.remaining_tokens,
burst_remaining: ip_result.burst_remaining,
reputation_impact: 0.0,
})
}
/// Vérifie le rate limiting par IP
async fn check_ip_rate_limit(
&self,
ip: IpAddr,
limit_type: &LimitType,
request_info: &RequestInfo,
) -> Result<RateLimitResult> {
let config = self.config.read().await;
// Récupérer ou créer le limiteur IP
let mut ip_limiter = self.ip_limiters.entry(ip).or_insert_with(|| {
IpRateLimiter::new(ip, &config)
});
// Vérifier le statut de l'IP
match ip_limiter.status {
IpStatus::Banned => {
return Ok(RateLimitResult {
allowed: false,
reason: Some("IP permanently banned".to_string()),
retry_after: None,
remaining_tokens: 0,
burst_remaining: 0,
reputation_impact: -0.2,
});
}
IpStatus::Blacklisted => {
return Ok(RateLimitResult {
allowed: false,
reason: Some("IP temporarily blacklisted".to_string()),
retry_after: Some(Duration::from_secs(300)), // 5 minutes
remaining_tokens: 0,
burst_remaining: 0,
reputation_impact: -0.1,
});
}
_ => {}
}
// Appliquer le rate limiting avec token bucket
let remaining_tokens = {
P0: stabilisation backend/chat/stream + nouvelle base migrations v1 Backend Go: - Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN. - Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError). - Sécurisation de config.go, CORS, statuts de santé et monitoring. - Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles). - Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés. - Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*. Chat server (Rust): - Refonte du pipeline JWT + sécurité, audit et rate limiting avancé. - Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing). - Nettoyage des panics, gestion d’erreurs robuste, logs structurés. - Migrations chat alignées sur le schéma UUID et nouvelles features. Stream server (Rust): - Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core. - Transactions P0 pour les jobs et segments, garanties d’atomicité. - Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION). Documentation & audits: - TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services. - Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3). - Scripts de reset et de cleanup pour la lab DB et la V1. Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
let bucket = ip_limiter.buckets.get_mut(limit_type)
.ok_or_else(|| ChatError::internal_error(format!(
"Rate limit bucket not initialized for limit type: {:?}",
limit_type
)))?;
2025-12-03 19:33:26 +00:00
bucket.refill();
if bucket.tokens > 0 {
bucket.tokens -= 1;
bucket.tokens
} else {
0
}
};
if remaining_tokens > 0 {
ip_limiter.last_activity = Instant::now();
// Enregistrer l'événement pour l'analyse de patterns
ip_limiter.request_patterns.push_back(RequestEvent {
timestamp: Instant::now(),
request_type: format!("{:?}", limit_type),
path: request_info.path.clone(),
user_agent: request_info.user_agent.clone(),
response_time: Duration::from_millis(0),
status_code: 200,
});
// Garder seulement les 100 derniers événements
if ip_limiter.request_patterns.len() > 100 {
ip_limiter.request_patterns.pop_front();
}
Ok(RateLimitResult {
allowed: true,
reason: None,
retry_after: None,
remaining_tokens,
burst_remaining: remaining_tokens,
reputation_impact: 0.0,
})
} else {
// Rate limit dépassé
ip_limiter.violation_count += 1;
// Escalade automatique si trop de violations
if ip_limiter.violation_count >= 5 {
ip_limiter.status = IpStatus::Suspicious;
}
if ip_limiter.violation_count >= 10 {
self.auto_blacklist_ip(ip, "Too many violations".to_string()).await?;
}
Ok(RateLimitResult {
allowed: false,
reason: Some(format!("Rate limit exceeded for {:?}", limit_type)),
retry_after: Some(Duration::from_secs(60)),
remaining_tokens: 0,
burst_remaining: 0,
reputation_impact: 0.0,
})
}
}
/// Vérifie le rate limiting par utilisateur
async fn check_user_rate_limit(&self, user_id: i64, limit_type: &LimitType) -> Result<RateLimitResult> {
let config = self.config.read().await;
let mut user_limiter = self.user_limiters.entry(user_id).or_insert_with(|| {
UserRateLimiter::new(user_id, &config)
});
// Vérifier la réputation d'abord
let capacity_multiplier = match user_limiter.reputation.level {
ReputationLevel::NewUser => 0.5,
ReputationLevel::Normal => 1.0,
ReputationLevel::Trusted => 1.5,
ReputationLevel::VIP => 2.0,
ReputationLevel::System => 5.0,
};
// Puis accéder au bucket avec la capacité ajustée
let remaining_tokens = {
P0: stabilisation backend/chat/stream + nouvelle base migrations v1 Backend Go: - Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN. - Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError). - Sécurisation de config.go, CORS, statuts de santé et monitoring. - Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles). - Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés. - Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*. Chat server (Rust): - Refonte du pipeline JWT + sécurité, audit et rate limiting avancé. - Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing). - Nettoyage des panics, gestion d’erreurs robuste, logs structurés. - Migrations chat alignées sur le schéma UUID et nouvelles features. Stream server (Rust): - Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core. - Transactions P0 pour les jobs et segments, garanties d’atomicité. - Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION). Documentation & audits: - TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services. - Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3). - Scripts de reset et de cleanup pour la lab DB et la V1. Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
let bucket = user_limiter.buckets.get_mut(limit_type)
.ok_or_else(|| ChatError::internal_error(format!(
"Rate limit bucket not initialized for limit type: {:?}",
limit_type
)))?;
2025-12-03 19:33:26 +00:00
bucket.capacity = (bucket.capacity as f32 * capacity_multiplier) as u32;
bucket.refill();
if bucket.tokens > 0 {
bucket.tokens -= 1;
bucket.tokens
} else {
0
}
};
if remaining_tokens > 0 {
user_limiter.last_activity = Instant::now();
Ok(RateLimitResult {
allowed: true,
reason: None,
retry_after: None,
remaining_tokens,
burst_remaining: remaining_tokens,
reputation_impact: 0.0,
})
} else {
user_limiter.violation_count += 1;
user_limiter.reputation.violations_today += 1;
user_limiter.reputation.score = (user_limiter.reputation.score - 0.05).max(0.0);
Ok(RateLimitResult {
allowed: false,
reason: Some(format!("User rate limit exceeded for {:?}", limit_type)),
retry_after: Some(Duration::from_secs(30)),
remaining_tokens: 0,
burst_remaining: 0,
reputation_impact: 0.0,
})
}
}
/// Vérifie le rate limiting par canal
async fn check_channel_rate_limit(&self, channel_id: &str, limit_type: &LimitType) -> Result<RateLimitResult> {
let config = self.config.read().await;
let mut channel_limiter = self.channel_limiters.entry(channel_id.to_string()).or_insert_with(|| {
ChannelRateLimiter::new(channel_id.to_string(), &config)
});
// Appliquer la modération selon le niveau du canal
let rate_multiplier = match channel_limiter.moderation_level {
ModerationLevel::Low => 1.5,
ModerationLevel::Normal => 1.0,
ModerationLevel::High => 0.5,
ModerationLevel::Lockdown => 0.1,
};
channel_limiter.message_bucket.refill();
let tokens_needed = (1.0 / rate_multiplier) as u32;
if channel_limiter.message_bucket.tokens >= tokens_needed {
channel_limiter.message_bucket.tokens -= tokens_needed;
channel_limiter.last_activity = Instant::now();
Ok(RateLimitResult {
allowed: true,
reason: None,
retry_after: None,
remaining_tokens: channel_limiter.message_bucket.tokens,
burst_remaining: channel_limiter.message_bucket.burst_allowance,
reputation_impact: 0.0,
})
} else {
Ok(RateLimitResult {
allowed: false,
reason: Some(format!("Channel rate limit exceeded (moderation: {:?})", channel_limiter.moderation_level)),
retry_after: Some(Duration::from_secs(10)),
remaining_tokens: 0,
burst_remaining: 0,
reputation_impact: 0.0,
})
}
}
/// Analyse les patterns de requêtes pour détecter les attaques
async fn analyze_request_pattern(&self, ip: IpAddr, request_info: &RequestInfo) -> Result<()> {
// Récupérer l'historique des requêtes pour cette IP
if let Some(ip_limiter) = self.ip_limiters.get(&ip) {
let recent_requests: Vec<_> = ip_limiter.request_patterns.iter()
.filter(|event| event.timestamp.elapsed() < Duration::from_secs(60))
.collect();
// Détecter différents types d'attaques
// 1. DDoS - Trop de requêtes dans un court laps de temps
if recent_requests.len() > 100 {
self.detect_ddos_attack(ip, &recent_requests).await?;
}
// 2. Brute Force - Tentatives répétées sur l'authentification
if request_info.path.contains("/auth") || request_info.path.contains("/login") {
let auth_attempts = recent_requests.iter()
.filter(|event| event.path.contains("/auth"))
.count();
if auth_attempts > 10 {
self.detect_brute_force_attack(ip).await?;
}
}
// 3. Bot Activity - Patterns suspects (même User-Agent, timing régulier)
if let Some(user_agent) = &request_info.user_agent {
let same_ua_count = recent_requests.iter()
.filter(|event| event.user_agent.as_ref() == Some(user_agent))
.count();
if same_ua_count > 50 && self.is_suspicious_user_agent(user_agent) {
self.detect_bot_activity(ip, user_agent.clone()).await?;
}
}
}
Ok(())
}
/// Détecte une attaque DDoS
async fn detect_ddos_attack(&self, ip: IpAddr, recent_requests: &[&RequestEvent]) -> Result<()> {
let pattern = AttackPattern {
pattern_id: format!("ddos_{}_{}",ip, Instant::now().elapsed().as_secs()),
pattern_type: AttackType::DDoS,
source_ips: vec![ip],
detection_time: Instant::now(),
severity: 0.9,
requests_count: recent_requests.len() as u32,
geographic_spread: 0.0, // Single IP
user_agents: recent_requests.iter()
.filter_map(|event| event.user_agent.clone())
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect(),
};
self.attack_patterns.insert(pattern.pattern_id.clone(), pattern);
self.auto_blacklist_ip(ip, "DDoS attack detected".to_string()).await?;
self.metrics.attacks_detected.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
tracing::warn!("DDoS attack detected from IP: {}", ip);
Ok(())
}
/// Détecte une attaque de brute force
async fn detect_brute_force_attack(&self, ip: IpAddr) -> Result<()> {
let pattern = AttackPattern {
pattern_id: format!("bruteforce_{}_{}", ip, Instant::now().elapsed().as_secs()),
pattern_type: AttackType::BruteForce,
source_ips: vec![ip],
detection_time: Instant::now(),
severity: 0.8,
requests_count: 10,
geographic_spread: 0.0,
user_agents: vec![],
};
self.attack_patterns.insert(pattern.pattern_id.clone(), pattern);
self.auto_blacklist_ip(ip, "Brute force attack detected".to_string()).await?;
tracing::warn!("Brute force attack detected from IP: {}", ip);
Ok(())
}
/// Détecte une activité de bot
async fn detect_bot_activity(&self, ip: IpAddr, user_agent: String) -> Result<()> {
let pattern = AttackPattern {
pattern_id: format!("bot_{}_{}", ip, Instant::now().elapsed().as_secs()),
pattern_type: AttackType::BotActivity,
source_ips: vec![ip],
detection_time: Instant::now(),
severity: 0.6,
requests_count: 50,
geographic_spread: 0.0,
user_agents: vec![user_agent],
};
self.attack_patterns.insert(pattern.pattern_id.clone(), pattern);
// Marquer l'IP comme suspecte plutôt que de la blacklister immédiatement
if let Some(mut ip_limiter) = self.ip_limiters.get_mut(&ip) {
ip_limiter.status = IpStatus::Suspicious;
ip_limiter.trust_score = (ip_limiter.trust_score - 0.3).max(0.0);
}
tracing::info!("Bot activity detected from IP: {}", ip);
Ok(())
}
/// Vérifie si un User-Agent est suspect
fn is_suspicious_user_agent(&self, user_agent: &str) -> bool {
let suspicious_patterns = [
"bot", "crawler", "spider", "scraper", "curl", "wget", "python", "java",
"headless", "selenium", "phantom", "automated"
];
let ua_lower = user_agent.to_lowercase();
suspicious_patterns.iter().any(|pattern| ua_lower.contains(pattern))
}
/// Ajoute automatiquement une IP à la liste noire
async fn auto_blacklist_ip(&self, ip: IpAddr, reason: String) -> Result<()> {
let reason_clone = reason.clone();
let config = self.config.read().await;
let blacklist_entry = BlacklistEntry {
ip,
reason,
blocked_at: Instant::now(),
expires_at: Some(Instant::now() + config.auto_blacklist_duration),
violation_count: 1,
auto_generated: true,
};
self.blacklist.insert(ip, blacklist_entry);
self.metrics.attacks_detected.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
tracing::warn!("IP {} automatically blacklisted: {}", ip, reason_clone);
Ok(())
}
/// Nettoie les entrées expirées
pub async fn cleanup_expired_entries(&self) {
let now = Instant::now();
// Nettoyer les blacklists expirées
self.blacklist.retain(|_, entry| {
entry.expires_at.map_or(true, |exp| exp > now)
});
// Nettoyer les limiteurs inactifs (plus de 1 heure)
self.ip_limiters.retain(|_, limiter| {
now.duration_since(limiter.last_activity) < Duration::from_secs(3600)
});
self.user_limiters.retain(|_, limiter| {
now.duration_since(limiter.last_activity) < Duration::from_secs(3600)
});
self.channel_limiters.retain(|_, limiter| {
now.duration_since(limiter.last_activity) < Duration::from_secs(3600)
});
}
}
/// Informations sur une requête
#[derive(Debug, Clone)]
pub struct RequestInfo {
pub path: String,
pub user_agent: Option<String>,
pub method: String,
pub content_length: Option<usize>,
}
impl TokenBucket {
pub fn new(capacity: u32, refill_rate: f32, burst_allowance: u32) -> Self {
Self {
capacity,
tokens: capacity,
refill_rate,
last_refill: Instant::now(),
burst_allowance,
}
}
pub fn refill(&mut self) {
let now = Instant::now();
let elapsed = now.duration_since(self.last_refill).as_secs_f32();
let tokens_to_add = (elapsed * self.refill_rate) as u32;
if tokens_to_add > 0 {
self.tokens = (self.tokens + tokens_to_add).min(self.capacity);
self.last_refill = now;
}
}
}
impl IpRateLimiter {
pub fn new(ip: IpAddr, config: &RateLimitConfig) -> Self {
let mut buckets = HashMap::new();
// Créer les buckets pour différents types de limitations
buckets.insert(LimitType::MessagesPerMinute, TokenBucket::new(config.messages_per_minute, config.messages_per_minute as f32 / 60.0, 10));
buckets.insert(LimitType::ConnectionsPerHour, TokenBucket::new(config.connections_per_hour, config.connections_per_hour as f32 / 3600.0, 5));
buckets.insert(LimitType::AuthAttempts, TokenBucket::new(config.auth_attempts_per_minute, config.auth_attempts_per_minute as f32 / 60.0, 2));
buckets.insert(LimitType::ApiRequests, TokenBucket::new(1000, 16.67, 50)); // 1000/min
buckets.insert(LimitType::FileUploads, TokenBucket::new(10, 0.17, 2)); // 10/min
Self {
ip,
buckets,
last_activity: Instant::now(),
violation_count: 0,
trust_score: 0.5, // Score neutre initial
request_patterns: VecDeque::new(),
status: IpStatus::Normal,
}
}
}
impl UserRateLimiter {
pub fn new(user_id: i64, config: &RateLimitConfig) -> Self {
let mut buckets = HashMap::new();
buckets.insert(LimitType::MessagesPerMinute, TokenBucket::new(config.messages_per_minute, config.messages_per_minute as f32 / 60.0, 5));
buckets.insert(LimitType::FileUploads, TokenBucket::new(20, 0.33, 3)); // 20/min
buckets.insert(LimitType::ChannelCreation, TokenBucket::new(5, 0.083, 1)); // 5/min
buckets.insert(LimitType::Invitations, TokenBucket::new(10, 0.17, 2)); // 10/min
buckets.insert(LimitType::Reactions, TokenBucket::new(60, 1.0, 10)); // 60/min
Self {
user_id,
buckets,
last_activity: Instant::now(),
violation_count: 0,
reputation: UserReputation {
score: 0.5,
level: ReputationLevel::NewUser,
violations_today: 0,
positive_actions: 0,
last_violation: None,
},
daily_limits: DailyLimits {
messages_sent: 0,
max_messages: 1000,
files_uploaded: 0,
max_files: 50,
reset_time: Instant::now() + Duration::from_secs(86400), // 24h
},
}
}
}
impl ChannelRateLimiter {
pub fn new(channel_id: String, config: &RateLimitConfig) -> Self {
Self {
channel_id,
message_bucket: TokenBucket::new(config.messages_per_minute * 10, (config.messages_per_minute * 10) as f32 / 60.0, 20),
concurrent_users: 0,
last_activity: Instant::now(),
spam_threshold: 0.7,
moderation_level: ModerationLevel::Normal,
}
}
}
impl Default for RateLimitConfig {
fn default() -> Self {
Self {
messages_per_minute: 30,
connections_per_hour: 100,
auth_attempts_per_minute: 5,
max_bucket_capacity: 1000,
attack_detection_threshold: 0.8,
auto_blacklist_duration: Duration::from_secs(900), // 15 minutes
enable_geolocation: true,
trusted_ips: vec![],
}
}
}