1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-08-07 17:03:01 +03:00

Rate-limit password-based login attempts

This commit is contained in:
Quentin Gliech
2024-07-25 16:59:09 +02:00
parent f5b4caf520
commit e25c170403
13 changed files with 525 additions and 15 deletions

View File

@@ -22,7 +22,7 @@ use ipnetwork::IpNetwork;
use mas_data_model::SiteConfig;
use mas_handlers::{
passwords::PasswordManager, ActivityTracker, BoundActivityTracker, CookieManager, ErrorWrapper,
GraphQLSchema, HttpClientFactory, MetadataCache,
GraphQLSchema, HttpClientFactory, Limiter, MetadataCache, RequesterFingerprint,
};
use mas_i18n::Translator;
use mas_keystore::{Encrypter, Keystore};
@@ -57,6 +57,7 @@ pub struct AppState {
pub site_config: SiteConfig,
pub activity_tracker: ActivityTracker,
pub trusted_proxies: Vec<IpNetwork>,
pub limiter: Limiter,
pub conn_acquisition_histogram: Option<Histogram<u64>>,
}
@@ -210,6 +211,12 @@ impl FromRef<AppState> for SiteConfig {
}
}
impl FromRef<AppState> for Limiter {
fn from_ref(input: &AppState) -> Self {
input.limiter.clone()
}
}
impl FromRef<AppState> for BoxHomeserverConnection {
fn from_ref(input: &AppState) -> Self {
Box::new(input.homeserver_connection.clone())
@@ -326,12 +333,35 @@ impl FromRequestParts<AppState> for BoundActivityTracker {
parts: &mut axum::http::request::Parts,
state: &AppState,
) -> Result<Self, Self::Rejection> {
// TODO: we may infer the IP twice, for the activity tracker and the limiter
let ip = infer_client_ip(parts, &state.trusted_proxies);
tracing::debug!(ip = ?ip, "Inferred client IP address");
Ok(state.activity_tracker.clone().bind(ip))
}
}
#[async_trait]
impl FromRequestParts<AppState> for RequesterFingerprint {
type Rejection = Infallible;
async fn from_request_parts(
parts: &mut axum::http::request::Parts,
state: &AppState,
) -> Result<Self, Self::Rejection> {
// TODO: we may infer the IP twice, for the activity tracker and the limiter
let ip = infer_client_ip(parts, &state.trusted_proxies);
if let Some(ip) = ip {
Ok(RequesterFingerprint::new(ip))
} else {
// If we can't infer the IP address, we'll just use an empty fingerprint and
// warn about it
tracing::warn!("Could not infer client IP address for an operation which rate-limits based on IP addresses");
Ok(RequesterFingerprint::EMPTY)
}
}
}
#[async_trait]
impl FromRequestParts<AppState> for BoxRepository {
type Rejection = ErrorWrapper<mas_storage_pg::DatabaseError>;

View File

@@ -19,7 +19,7 @@ use clap::Parser;
use figment::Figment;
use itertools::Itertools;
use mas_config::{AppConfig, ClientsConfig, ConfigurationSection, UpstreamOAuth2Config};
use mas_handlers::{ActivityTracker, CookieManager, HttpClientFactory, MetadataCache};
use mas_handlers::{ActivityTracker, CookieManager, HttpClientFactory, Limiter, MetadataCache};
use mas_listener::{server::Server, shutdown::ShutdownStream};
use mas_matrix_synapse::SynapseConnection;
use mas_router::UrlBuilder;
@@ -200,6 +200,8 @@ impl Options {
// Listen for SIGHUP
register_sighup(&templates, &activity_tracker)?;
let limiter = Limiter::default();
let graphql_schema = mas_handlers::graphql_schema(
&pool,
&policy_factory,
@@ -213,7 +215,6 @@ impl Options {
pool,
templates,
key_store,
metadata_cache,
cookie_manager,
encrypter,
url_builder,
@@ -222,9 +223,11 @@ impl Options {
graphql_schema,
http_client_factory,
password_manager,
metadata_cache,
site_config,
activity_tracker,
trusted_proxies,
limiter,
conn_acquisition_histogram: None,
};
s.init_metrics()?;