diff --git a/src/cli/client.rs b/src/cli/client.rs index f04d0385..90fcbe98 100644 --- a/src/cli/client.rs +++ b/src/cli/client.rs @@ -22,7 +22,7 @@ use crate::constants::KRILL_CLI_API_ENV; use crate::daemon::config::Config; #[cfg(feature = "multi-user")] -use crate::constants::{ PW_HASH_LOG_N, PW_HASH_P, PW_HASH_R }; +use crate::constants::{PW_HASH_LOG_N, PW_HASH_P, PW_HASH_R}; fn resolve_uri(server: &uri::Https, path: &str) -> String { format!("{}{}", server, path) @@ -431,12 +431,13 @@ impl KrillClient { } #[cfg(feature = "multi-user")] + #[allow(clippy::clippy::unnecessary_wraps)] fn user(&self, details: KrillUserDetails) -> Result { let (password_hash, salt) = { use scrypt::scrypt; let password = rpassword::read_password_from_tty(Some("Enter the password to hash: ")).unwrap(); - + // The scrypt-js NPM documentation (https://www.npmjs.com/package/scrypt-js) says: // "TL;DR - either only allow ASCII characters in passwords, or use // String.prototype.normalize('NFKC') on any password" @@ -448,7 +449,7 @@ impl KrillClient { let params = scrypt::Params::new(PW_HASH_LOG_N, PW_HASH_R, PW_HASH_P).unwrap(); // hash twice with two different salts - // hash first with a salt the client browser knows how to construct based on the users id and a site + // hash first with a salt the client browser knows how to construct based on the users id and a site // specific string. let weak_salt = format!("krill-lagosta-{}", user_id); diff --git a/src/daemon/auth/providers/config_file/provider.rs b/src/daemon/auth/providers/config_file/provider.rs index 1c102476..558ab63b 100644 --- a/src/daemon/auth/providers/config_file/provider.rs +++ b/src/daemon/auth/providers/config_file/provider.rs @@ -12,7 +12,7 @@ use crate::daemon::auth::{Auth, AuthProvider, LoggedInUser}; use crate::daemon::config::Config; use crate::daemon::http::HttpResponse; -use crate::constants::{ PW_HASH_LOG_N, PW_HASH_P, PW_HASH_R }; +use crate::constants::{PW_HASH_LOG_N, PW_HASH_P, PW_HASH_R}; // This is NOT an actual relative path to redirect to. Instead it is the path // string of an entry in the Vue router routes table to "route" to (in the @@ -43,7 +43,7 @@ fn get_checked_config_user(id: &str, user: &ConfigUserDetails) -> KrillResult (user.password_hash.to_string(), user.salt.clone()), None => (self.fake_password_hash.clone(), self.fake_salt.clone()), @@ -148,14 +148,22 @@ impl AuthProvider for ConfigFileAuthProvider { let password_hash_bytes = hex::decode(password_hash.as_ref()).unwrap(); let strong_salt = hex::decode(&user_salt).unwrap(); let mut hashed_hash: [u8; 32] = [0; 32]; - scrypt(password_hash_bytes.as_slice(), strong_salt.as_slice(), ¶ms, &mut hashed_hash).unwrap(); + scrypt( + password_hash_bytes.as_slice(), + strong_salt.as_slice(), + ¶ms, + &mut hashed_hash, + ) + .unwrap(); if hex::encode(hashed_hash) == user_password_hash.as_ref() { // And now finally check the user, so that both known and unknown user code paths do the same work // and don't result in an obvious timing difference between the two scenarios which could potentially // be used to discover user names. if let Some(user) = self.users.get(&id) { - let api_token = self.session_cache.encode(&id, &user.attributes, HashMap::new(), &self.key, None)?; + let api_token = + self.session_cache + .encode(&id, &user.attributes, HashMap::new(), &self.key, None)?; Ok(LoggedInUser { token: api_token, diff --git a/src/upgrades/v0_9_0/ca_objects_migration.rs b/src/upgrades/v0_9_0/ca_objects_migration.rs index 6845fe9a..0dd54f2b 100644 --- a/src/upgrades/v0_9_0/ca_objects_migration.rs +++ b/src/upgrades/v0_9_0/ca_objects_migration.rs @@ -251,6 +251,12 @@ impl UpgradeStore for CasStoreMigration { let mut total_migrated = 0; for cmd_key in cmd_keys { + // Do the migration counter first, so that we can just call continue when we need to skip commands + total_migrated += 1; + if total_migrated % 100 == 0 { + info!(" migrated {} commands", total_migrated); + } + debug!(" command: {}", cmd_key); let mut old_cmd: OldStoredCaCommand = self.get(&cmd_key)?; @@ -298,11 +304,6 @@ impl UpgradeStore for CasStoreMigration { let key = KeyStoreKey::scoped(scope.clone(), format!("{}.json", cmd_key)); self.store.store(&key, &migrated_cmd)?; - - total_migrated += 1; - if total_migrated % 100 == 0 { - info!(" migrated {} commands", total_migrated); - } } info!("Finished migrating commands for CA {}", scope); diff --git a/src/upgrades/v0_9_0/pubd_objects_migration.rs b/src/upgrades/v0_9_0/pubd_objects_migration.rs index 4e30fa30..baab9b9e 100644 --- a/src/upgrades/v0_9_0/pubd_objects_migration.rs +++ b/src/upgrades/v0_9_0/pubd_objects_migration.rs @@ -6,7 +6,8 @@ use std::{ sync::Arc, }; -use rpki::{crypto::KeyIdentifier, uri}; +use chrono::Duration; +use rpki::{crypto::KeyIdentifier, uri, x509::Time}; use crate::{ commons::{ @@ -110,8 +111,16 @@ impl UpgradeStore for PubdStoreMigration { let scope = "0"; let handle = Handle::from_str(scope).unwrap(); - let info_key = KeyStoreKey::scoped(scope.to_string(), "info.json".to_string()); - let mut info: StoredValueInfo = match self.store.get(&info_key) { + // Archive all keys in the scope, then we can write new keys as needed without + // overwriting anything when we renumber. + for key in self.store.keys(Some(scope.to_string()), "")? { + self.archive_to_migration_scope(&key)?; + } + + let migration_scope = format!("{}/{}", scope, MIGRATION_SCOPE); + + let migration_info_key = KeyStoreKey::scoped(migration_scope.clone(), "info.json".to_string()); + let mut info: StoredValueInfo = match self.store.get(&migration_info_key) { Ok(Some(info)) => info, _ => StoredValueInfo::default(), }; @@ -121,10 +130,11 @@ impl UpgradeStore for PubdStoreMigration { info.last_command = 1; // migrate init - let init_key = Self::event_key(&scope, 0); + let old_init_key = Self::event_key(&migration_scope, 0); + let init_key = Self::event_key(scope, 0); let old_init: OldPubdInit = self .store - .get(&init_key)? + .get(&old_init_key)? .ok_or_else(|| UpgradeError::custom("Cannot read pubd init event"))?; let (_, _, old_init) = old_init.unpack(); @@ -133,29 +143,51 @@ impl UpgradeStore for PubdStoreMigration { self.store.store(&init_key, &init)?; // migrate commands and events - let cmd_keys = self.command_keys(scope)?; - - info!("Will migrate {} commands for publication server", cmd_keys.len()); + let old_cmd_keys = self.command_keys(&migration_scope)?; let mut total_migrated = 0; + let time_started = Time::now(); + let total_commands = old_cmd_keys.len(); - for cmd_key in cmd_keys { - let mut old_cmd: OldStoredRepositoryCommand = self.get(&cmd_key)?; - self.archive_to_migration_scope(&cmd_key)?; + info!("Will migrate {} commands for publication server", total_commands); + + for old_cmd_key in old_cmd_keys { + // Do the migration counter first, so that we can just call continue when we need to skip commands + total_migrated += 1; + if total_migrated % 100 == 0 { + // ETA: + // - (total_migrated / (now - started)) * total + let mut time_passed = (Time::now().timestamp() - time_started.timestamp()) as usize; + if time_passed == 0 { + time_passed = 1; // avoid divide by zero.. we are doing approximate estimates here + } + let migrated_per_second = total_migrated / time_passed; + let expected_seconds = (total_commands / migrated_per_second) as i64; + let eta = time_started + Duration::seconds(expected_seconds); + info!( + " migrated {} commands, expect to finish: {}", + total_migrated, + eta.to_rfc3339() + ); + } + + if old_cmd_key.name().contains("pubd-publish.json") { + continue; // There is no migration needed for these commands. + } + + let mut old_cmd: OldStoredRepositoryCommand = self.get(&old_cmd_key)?; if let Some(evt_versions) = old_cmd.effect.events() { - debug!(" command: {}", cmd_key); + debug!(" command: {}", old_cmd_key); let mut events = vec![]; for v in evt_versions { - let event_key = Self::event_key(scope, *v); - debug!(" +- event: {}", event_key); + let old_event_key = Self::event_key(&migration_scope, *v); + debug!(" +- event: {}", old_event_key); let old_evt: OldPubdEvt = self .store - .get(&event_key)? - .ok_or_else(|| UpgradeError::Custom(format!("Cannot parse old event: {}", event_key)))?; - - self.archive_to_migration_scope(&event_key)?; + .get(&old_event_key)? + .ok_or_else(|| UpgradeError::Custom(format!("Cannot parse old event: {}", old_event_key)))?; if old_evt.needs_migration() { info.last_event += 1; @@ -185,11 +217,6 @@ impl UpgradeStore for PubdStoreMigration { let key = KeyStoreKey::scoped(scope.to_string(), format!("{}.json", cmd_key)); self.store.store(&key, &migrated_cmd)?; - - total_migrated += 1; - if total_migrated % 100 == 0 { - info!(" migrated {} commands", total_migrated); - } } info!("Finished migrating publication server commands"); @@ -202,6 +229,7 @@ impl UpgradeStore for PubdStoreMigration { // update the info file info.snapshot_version = 0; info.last_command -= 1; + let info_key = KeyStoreKey::scoped(scope.to_string(), "info.json".to_string()); self.store.store(&info_key, &info)?; // verify that we can now rebuild the 0.9 publication server based on