From 60981ef7878ce973020cc78004aa694158dc3395 Mon Sep 17 00:00:00 2001 From: Timshel Date: Fri, 18 Sep 2026 18:48:41 +0200 Subject: [PATCH] Allow to lower KDF iterations for tests --- playwright/compose/warden/build.sh | 2 +- playwright/docker-compose.yml | 2 ++ playwright/test.env | 2 ++ src/api/core/accounts.rs | 6 +++--- src/config.rs | 24 ++++++++++++++++++++---- src/db/models/user.rs | 3 +-- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/playwright/compose/warden/build.sh b/playwright/compose/warden/build.sh index ee8b47fe..0a2d4694 100755 --- a/playwright/compose/warden/build.sh +++ b/playwright/compose/warden/build.sh @@ -24,7 +24,7 @@ if [[ ! -z "$REPO_URL" ]] && [[ ! -z "$COMMIT_HASH" ]] ; then fi # Lower the KDF iterations default for faster tests. -sed -i 's/(6e5,2e6,6e5)/(1e5,2e6,1e5)/' /web-vault/app/main.*.js +sed -i 's/(6e5,2e6,6e5)/(6e3,2e6,6e3)/' /web-vault/app/main.*.js # Generate a self signed cert mkdir -p /data/ssl; cd /data/ssl diff --git a/playwright/docker-compose.yml b/playwright/docker-compose.yml index 5bfc47a5..97f6faf5 100644 --- a/playwright/docker-compose.yml +++ b/playwright/docker-compose.yml @@ -23,12 +23,14 @@ services: env_file: ${DC_ENV_FILE:-.env} environment: - ADMIN_TOKEN + - CLIENT_KDF_ITER - DATABASE_URL - CLIENT_SUPPRESS_ONBOARDING - EMAIL_2FA_AUTO_FALLBACK - I_REALLY_WANT_VOLATILE_STORAGE - LOG_LEVEL - LOGIN_RATELIMIT_MAX_BURST + - PASSWORD_ITERATIONS - SMTP_HOST - SMTP_FROM - SMTP_DEBUG diff --git a/playwright/test.env b/playwright/test.env index 2260f860..c2fa354e 100644 --- a/playwright/test.env +++ b/playwright/test.env @@ -58,6 +58,8 @@ LOG_LEVEL=info,oidcwarden::sso=debug LOGIN_RATELIMIT_MAX_BURST=100 ADMIN_TOKEN=admin CLIENT_SUPPRESS_ONBOARDING=true +PASSWORD_ITERATIONS=6000 +CLIENT_KDF_ITER=6000 SMTP_SECURITY=off SMTP_PORT=${MAILDEV_SMTP_PORT} diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 8cc5e55b..a3ecc3bf 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -665,8 +665,8 @@ async fn post_password(data: Json, headers: Headers, conn: DbCon } fn set_kdf_data(user: &mut User, data: &KDFData) -> EmptyResult { - if data.kdf == UserKdfType::Pbkdf2 as i32 && data.kdf_iterations < 100_000 { - err!("PBKDF2 KDF iterations must be at least 100000.") + if data.kdf == UserKdfType::Pbkdf2 as i32 && data.kdf_iterations < CONFIG.client_kdf_iter() { + err!(format!("PBKDF2 KDF iterations must be at least {}.", CONFIG.client_kdf_iter())) } if data.kdf == UserKdfType::Argon2id as i32 { @@ -1370,7 +1370,7 @@ pub async fn prelogin(data: Json, ip: ClientIp, conn: DbConn) -> J let (kdf_type, kdf_iter, kdf_mem, kdf_para) = match User::find_by_mail(&data.email, &conn).await { Some(user) => (user.client_kdf_type, user.client_kdf_iter, user.client_kdf_memory, user.client_kdf_parallelism), - None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT, None, None), + None => (User::CLIENT_KDF_TYPE_DEFAULT, CONFIG.client_kdf_iter(), None, None), }; Ok(Json(json!({ diff --git a/src/config.rs b/src/config.rs index 0bb22f7f..dcd5a313 100644 --- a/src/config.rs +++ b/src/config.rs @@ -661,7 +661,9 @@ make_config! { client { /// Control whether clients onboarding interstitials are suppressed |> post-login welcome dialogs, extension install prompts, setup extension redirects, and premium upsell modals - client_suppress_onboarding: bool, true, def, false; + client_suppress_onboarding: bool, true, def, false; + /// In most cases this value is set by the client |> Will block clients from setting too low of a value in `accounts::post_kdf` + client_kdf_iter: i32, false, def, 600_000; }, /// Advanced settings @@ -938,6 +940,21 @@ make_config! { }, } +fn check_iterations(cfg: &ConfigItems, iterations: i32, key: &str) -> Result<(), Error> { + if iterations < 100_000 { + if let Ok(url) = Url::parse(&cfg.domain) + && url.domain().is_none() + { + // Warn only if it's probably not exposed externnaly + println!("[WARNING] {key} should be at least 100000 or higher. The default is 600000!"); + } else { + err!(format!("{key} should be at least 100000 or higher. The default is 600000!")); + } + } + + Ok(()) +} + fn validate_config(cfg: &ConfigItems, on_update: bool) -> Result<(), Error> { // Validate connection URL is valid and DB feature is enabled #[cfg(sqlite)] @@ -972,9 +989,8 @@ fn validate_config(cfg: &ConfigItems, on_update: bool) -> Result<(), Error> { } } - if cfg.password_iterations < 100_000 { - err!("PASSWORD_ITERATIONS should be at least 100000 or higher. The default is 600000!"); - } + check_iterations(cfg, cfg.password_iterations, "PASSWORD_ITERATIONS")?; + check_iterations(cfg, cfg.client_kdf_iter, "CLIENT_KDF_ITER")?; let limit = 256; if cfg.database_max_conns < 1 || cfg.database_max_conns > limit { diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 3412b142..aa377834 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -109,7 +109,6 @@ pub struct UserStampException { /// Local methods impl User { pub const CLIENT_KDF_TYPE_DEFAULT: i32 = UserKdfType::Pbkdf2 as i32; - pub const CLIENT_KDF_ITER_DEFAULT: i32 = 600_000; pub fn new(email: &str, name: Option) -> Self { let now = Utc::now().naive_utc(); @@ -147,7 +146,7 @@ impl User { excluded_globals: "[]".to_owned(), client_kdf_type: Self::CLIENT_KDF_TYPE_DEFAULT, - client_kdf_iter: Self::CLIENT_KDF_ITER_DEFAULT, + client_kdf_iter: CONFIG.client_kdf_iter(), client_kdf_memory: None, client_kdf_parallelism: None,