Rate limit subscribeRepos

This endpoint was lacking rate limits, even though it can be a very costly operation. I set it to a 5 per 60s per ip rate limit, which should support normal behaviors and mitigate any misbehaving relays.
This commit is contained in:
Johanna Larsson
2026-10-18 11:06:23 +00:00
committed by Tangled
parent 01a5e8d2a1
commit 8fd032f158
4 changed files with 16 additions and 0 deletions
@@ -115,6 +115,11 @@ impl RateLimitPolicy for HandleVerificationLimit {
const KIND: RateLimitKind = RateLimitKind::HandleVerification;
}
pub struct SubscribeReposLimit;
impl RateLimitPolicy for SubscribeReposLimit {
const KIND: RateLimitKind = RateLimitKind::SubscribeRepos;
}
pub trait RateLimitRejection: IntoResponse + Send + 'static {
fn new() -> Self;
}
@@ -39,6 +39,7 @@ pub struct RateLimiters {
pub sso_unlink: Arc<KeyedRateLimiter>,
pub oauth_register_complete: Arc<KeyedRateLimiter>,
pub handle_verification: Arc<KeyedRateLimiter>,
pub subscribe_repos: Arc<KeyedRateLimiter>,
}
impl Default for RateLimiters {
@@ -70,6 +71,7 @@ impl RateLimiters {
sso_unlink: keyed_limiter(RateLimitKind::SsoUnlink),
oauth_register_complete: keyed_limiter(RateLimitKind::OAuthRegisterComplete),
handle_verification: keyed_limiter(RateLimitKind::HandleVerification),
subscribe_repos: keyed_limiter(RateLimitKind::SubscribeRepos),
}
}
+7
View File
@@ -99,6 +99,7 @@ pub enum RateLimitKind {
SsoUnlink,
OAuthRegisterComplete,
HandleVerification,
SubscribeRepos,
}
impl RateLimitKind {
@@ -124,6 +125,7 @@ impl RateLimitKind {
Self::SsoUnlink => "sso_unlink",
Self::OAuthRegisterComplete => "oauth_register_complete",
Self::HandleVerification => "handle_verification",
Self::SubscribeRepos => "subscribe_repos",
}
}
@@ -209,6 +211,10 @@ impl RateLimitKind {
limit: 10,
window_ms: 60_000,
},
Self::SubscribeRepos => RateLimitParams {
limit: 5,
window_ms: 60_000,
},
}
}
}
@@ -489,6 +495,7 @@ impl AppState {
RateLimitKind::SsoUnlink => &self.rate_limiters.sso_unlink,
RateLimitKind::OAuthRegisterComplete => &self.rate_limiters.oauth_register_complete,
RateLimitKind::HandleVerification => &self.rate_limiters.handle_verification,
RateLimitKind::SubscribeRepos => &self.rate_limiters.subscribe_repos,
};
if limiter.check_key(&client_ip.to_string()).is_err() {
@@ -8,6 +8,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use tokio::sync::broadcast::error::RecvError;
use tracing::{error, info, warn};
use tranquil_db_traits::SequenceNumber;
use tranquil_pds::rate_limit::{RateLimited, SubscribeReposLimit};
use tranquil_pds::state::AppState;
use tranquil_pds::sync::firehose::SequencedEvent;
use tranquil_pds::sync::frame::{ErrorFrameName, InfoFrameName};
@@ -30,6 +31,7 @@ pub async fn subscribe_repos(
ws: WebSocketUpgrade,
State(state): State<AppState>,
Query(params): Query<SubscribeReposParams>,
_rate_limit: RateLimited<SubscribeReposLimit>,
) -> Response {
ws.on_upgrade(move |socket| handle_socket(socket, state, params))
}