mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-05-25 21:13:57 +00:00
Merge branch 'ratelimit' into main
This commit is contained in:
commit
85ac9783f0
8 changed files with 145 additions and 2 deletions
|
@ -166,6 +166,10 @@ fn post_admin_login(
|
|||
) -> Result<Redirect, Flash<Redirect>> {
|
||||
let data = data.into_inner();
|
||||
|
||||
if crate::ratelimit::check_limit_admin(&ip.ip).is_err() {
|
||||
return Err(Flash::error(Redirect::to(admin_url(referer)), "Too many requests, try again later."));
|
||||
}
|
||||
|
||||
// If the token is invalid, redirect to login page
|
||||
if !_validate_token(&data.token) {
|
||||
error!("Invalid admin token. IP: {}", ip.ip);
|
||||
|
|
|
@ -84,6 +84,9 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
|
|||
err!("Scope not supported")
|
||||
}
|
||||
|
||||
// Ratelimit the login
|
||||
crate::ratelimit::check_limit_login(&ip.ip)?;
|
||||
|
||||
// Get the user
|
||||
let username = data.username.as_ref().unwrap();
|
||||
let user = match User::find_by_mail(username, &conn) {
|
||||
|
|
|
@ -511,6 +511,16 @@ make_config! {
|
|||
|
||||
/// Allowed iframe ancestors (Know the risks!) |> Allows other domains to embed the web vault into an iframe, useful for embedding into secure intranets
|
||||
allowed_iframe_ancestors: String, true, def, String::new();
|
||||
|
||||
/// Seconds between login requests |> Number of seconds, on average, between login and 2FA requests from the same IP address before rate limiting kicks in
|
||||
login_ratelimit_seconds: u64, false, def, 60;
|
||||
/// Max burst size for login requests |> Allow a burst of requests of up to this size, while maintaining the average indicated by `login_ratelimit_seconds`. Note that this applies to both the login and the 2FA, so it's recommended to allow a burst size of at least 2
|
||||
login_ratelimit_max_burst: u32, false, def, 10;
|
||||
|
||||
/// Seconds between admin requests |> Number of seconds, on average, between admin requests from the same IP address before rate limiting kicks in
|
||||
admin_ratelimit_seconds: u64, false, def, 300;
|
||||
/// Max burst size for login requests |> Allow a burst of requests of up to this size, while maintaining the average indicated by `admin_ratelimit_seconds`
|
||||
admin_ratelimit_max_burst: u32, false, def, 3;
|
||||
},
|
||||
|
||||
/// Yubikey settings
|
||||
|
|
|
@ -32,6 +32,7 @@ mod crypto;
|
|||
#[macro_use]
|
||||
mod db;
|
||||
mod mail;
|
||||
mod ratelimit;
|
||||
mod util;
|
||||
|
||||
pub use config::CONFIG;
|
||||
|
|
38
src/ratelimit.rs
Normal file
38
src/ratelimit.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
use once_cell::sync::Lazy;
|
||||
use std::{net::IpAddr, num::NonZeroU32, time::Duration};
|
||||
|
||||
use governor::{clock::DefaultClock, state::keyed::DashMapStateStore, Quota, RateLimiter};
|
||||
|
||||
use crate::{Error, CONFIG};
|
||||
|
||||
type Limiter<T = IpAddr> = RateLimiter<T, DashMapStateStore<T>, DefaultClock>;
|
||||
|
||||
static LIMITER_LOGIN: Lazy<Limiter> = Lazy::new(|| {
|
||||
let seconds = Duration::from_secs(CONFIG.login_ratelimit_seconds());
|
||||
let burst = NonZeroU32::new(CONFIG.login_ratelimit_max_burst()).expect("Non-zero login ratelimit burst");
|
||||
RateLimiter::keyed(Quota::with_period(seconds).expect("Non-zero login ratelimit seconds").allow_burst(burst))
|
||||
});
|
||||
|
||||
static LIMITER_ADMIN: Lazy<Limiter> = Lazy::new(|| {
|
||||
let seconds = Duration::from_secs(CONFIG.admin_ratelimit_seconds());
|
||||
let burst = NonZeroU32::new(CONFIG.admin_ratelimit_max_burst()).expect("Non-zero admin ratelimit burst");
|
||||
RateLimiter::keyed(Quota::with_period(seconds).expect("Non-zero admin ratelimit seconds").allow_burst(burst))
|
||||
});
|
||||
|
||||
pub fn check_limit_login(ip: &IpAddr) -> Result<(), Error> {
|
||||
match LIMITER_LOGIN.check_key(ip) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_e) => {
|
||||
err_code!("Too many login requests", 429);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_limit_admin(ip: &IpAddr) -> Result<(), Error> {
|
||||
match LIMITER_ADMIN.check_key(ip) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_e) => {
|
||||
err_code!("Too many admin requests", 429);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue