mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-11-04 12:18:20 +02:00 
			
		
		
		
	Merge pull request #3289 from BlackDex/admin-token-hash-support
Admin token Argon2 hashing support
This commit is contained in:
		@@ -259,9 +259,13 @@
 | 
				
			|||||||
## A comma-separated list means only those users can create orgs:
 | 
					## A comma-separated list means only those users can create orgs:
 | 
				
			||||||
# ORG_CREATION_USERS=admin1@example.com,admin2@example.com
 | 
					# ORG_CREATION_USERS=admin1@example.com,admin2@example.com
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Token for the admin interface, preferably use a long random string
 | 
					## Token for the admin interface, preferably an Argon2 PCH string
 | 
				
			||||||
## One option is to use 'openssl rand -base64 48'
 | 
					## Vaultwarden has a built-in generator by calling `vaultwarden hash`
 | 
				
			||||||
 | 
					## For details see: https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page#secure-the-admin_token
 | 
				
			||||||
## If not set, the admin panel is disabled
 | 
					## If not set, the admin panel is disabled
 | 
				
			||||||
 | 
					## New Argon2 PHC string
 | 
				
			||||||
 | 
					# ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$MmeKRnGK5RW5mJS7h3TOL89GrpLPXJPAtTK8FTqj9HM$DqsstvoSAETl9YhnsXbf43WeaUwJC6JhViIvuPoig78'
 | 
				
			||||||
 | 
					## Old plain text string (Will generate warnings in favor of Argon2)
 | 
				
			||||||
# ADMIN_TOKEN=Vy2VyYTTsKPv8W5aEOWUbB/Bt3DEKePbHmI4m9VcemUMS2rEviDowNAFqYi1xjmp
 | 
					# ADMIN_TOKEN=Vy2VyYTTsKPv8W5aEOWUbB/Bt3DEKePbHmI4m9VcemUMS2rEviDowNAFqYi1xjmp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Enable this to bypass the admin panel security. This option is only
 | 
					## Enable this to bypass the admin panel security. This option is only
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										60
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										60
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -85,6 +85,17 @@ dependencies = [
 | 
				
			|||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "argon2"
 | 
				
			||||||
 | 
					version = "0.5.0-pre.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "0efde6c15a373abaefe544ddae9fc024eac3073798ba0c40043fd655f3535eb8"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "base64ct",
 | 
				
			||||||
 | 
					 "blake2",
 | 
				
			||||||
 | 
					 "password-hash",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "async-channel"
 | 
					name = "async-channel"
 | 
				
			||||||
version = "1.8.0"
 | 
					version = "1.8.0"
 | 
				
			||||||
@@ -324,6 +335,12 @@ version = "0.21.0"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
 | 
					checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "base64ct"
 | 
				
			||||||
 | 
					version = "1.6.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "binascii"
 | 
					name = "binascii"
 | 
				
			||||||
version = "0.1.4"
 | 
					version = "0.1.4"
 | 
				
			||||||
@@ -336,6 +353,15 @@ version = "1.3.2"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 | 
					checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "blake2"
 | 
				
			||||||
 | 
					version = "0.10.6"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "digest",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "block-buffer"
 | 
					name = "block-buffer"
 | 
				
			||||||
version = "0.10.3"
 | 
					version = "0.10.3"
 | 
				
			||||||
@@ -2006,6 +2032,17 @@ dependencies = [
 | 
				
			|||||||
 "regex",
 | 
					 "regex",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "password-hash"
 | 
				
			||||||
 | 
					version = "0.5.0-pre.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "4d9d7f72dbf886af2c2a8d4a2ddfb4eea37e4d77ea3bde49f79af7c577e37908"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "base64ct",
 | 
				
			||||||
 | 
					 "rand_core",
 | 
				
			||||||
 | 
					 "subtle",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "paste"
 | 
					name = "paste"
 | 
				
			||||||
version = "1.0.11"
 | 
					version = "1.0.11"
 | 
				
			||||||
@@ -2585,6 +2622,27 @@ dependencies = [
 | 
				
			|||||||
 "uncased",
 | 
					 "uncased",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "rpassword"
 | 
				
			||||||
 | 
					version = "7.2.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "rtoolbox",
 | 
				
			||||||
 | 
					 "winapi",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "rtoolbox"
 | 
				
			||||||
 | 
					version = "0.0.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "winapi",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "rustc-demangle"
 | 
					name = "rustc-demangle"
 | 
				
			||||||
version = "0.1.21"
 | 
					version = "0.1.21"
 | 
				
			||||||
@@ -3425,6 +3483,7 @@ dependencies = [
 | 
				
			|||||||
name = "vaultwarden"
 | 
					name = "vaultwarden"
 | 
				
			||||||
version = "1.0.0"
 | 
					version = "1.0.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "argon2",
 | 
				
			||||||
 "backtrace",
 | 
					 "backtrace",
 | 
				
			||||||
 "bytes",
 | 
					 "bytes",
 | 
				
			||||||
 "cached",
 | 
					 "cached",
 | 
				
			||||||
@@ -3464,6 +3523,7 @@ dependencies = [
 | 
				
			|||||||
 "ring",
 | 
					 "ring",
 | 
				
			||||||
 "rmpv",
 | 
					 "rmpv",
 | 
				
			||||||
 "rocket",
 | 
					 "rocket",
 | 
				
			||||||
 | 
					 "rpassword",
 | 
				
			||||||
 "semver",
 | 
					 "semver",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "serde_json",
 | 
					 "serde_json",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								Cargo.toml
									
									
									
									
									
								
							@@ -157,8 +157,19 @@ semver = "1.0.16"
 | 
				
			|||||||
mimalloc = { version = "0.1.34", features = ["secure"], default-features = false, optional = true }
 | 
					mimalloc = { version = "0.1.34", features = ["secure"], default-features = false, optional = true }
 | 
				
			||||||
which = "4.4.0"
 | 
					which = "4.4.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Argon2 library with support for the PHC format
 | 
				
			||||||
 | 
					argon2 = "0.5.0-pre.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Reading a password from the cli for generating the Argon2id ADMIN_TOKEN
 | 
				
			||||||
 | 
					rpassword = "7.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Strip debuginfo from the release builds
 | 
					# Strip debuginfo from the release builds
 | 
				
			||||||
# Also enable thin LTO for some optimizations
 | 
					# Also enable thin LTO for some optimizations
 | 
				
			||||||
[profile.release]
 | 
					[profile.release]
 | 
				
			||||||
strip = "debuginfo"
 | 
					strip = "debuginfo"
 | 
				
			||||||
lto = "thin"
 | 
					lto = "thin"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Always build argon2 using opt-level 3
 | 
				
			||||||
 | 
					# This is a huge speed improvement during testing
 | 
				
			||||||
 | 
					[profile.dev.package.argon2]
 | 
				
			||||||
 | 
					opt-level = 3
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -201,6 +201,19 @@ fn post_admin_login(data: Form<LoginForm>, cookies: &CookieJar<'_>, ip: ClientIp
 | 
				
			|||||||
fn _validate_token(token: &str) -> bool {
 | 
					fn _validate_token(token: &str) -> bool {
 | 
				
			||||||
    match CONFIG.admin_token().as_ref() {
 | 
					    match CONFIG.admin_token().as_ref() {
 | 
				
			||||||
        None => false,
 | 
					        None => false,
 | 
				
			||||||
 | 
					        Some(t) if t.starts_with("$argon2") => {
 | 
				
			||||||
 | 
					            use argon2::password_hash::PasswordVerifier;
 | 
				
			||||||
 | 
					            match argon2::password_hash::PasswordHash::new(t) {
 | 
				
			||||||
 | 
					                Ok(h) => {
 | 
				
			||||||
 | 
					                    // NOTE: hash params from `ADMIN_TOKEN` are used instead of what is configured in the `Argon2` instance.
 | 
				
			||||||
 | 
					                    argon2::Argon2::default().verify_password(token.trim().as_ref(), &h).is_ok()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Err(e) => {
 | 
				
			||||||
 | 
					                    error!("The configured Argon2 PHC in `ADMIN_TOKEN` is invalid: {e}");
 | 
				
			||||||
 | 
					                    false
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        Some(t) => crate::crypto::ct_eq(t.trim(), token.trim()),
 | 
					        Some(t) => crate::crypto::ct_eq(t.trim(), token.trim()),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ static CONFIG_FILE: Lazy<String> = Lazy::new(|| {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
pub static CONFIG: Lazy<Config> = Lazy::new(|| {
 | 
					pub static CONFIG: Lazy<Config> = Lazy::new(|| {
 | 
				
			||||||
    Config::load().unwrap_or_else(|e| {
 | 
					    Config::load().unwrap_or_else(|e| {
 | 
				
			||||||
        println!("Error loading config:\n\t{e:?}\n");
 | 
					        println!("Error loading config:\n  {e:?}\n");
 | 
				
			||||||
        exit(12)
 | 
					        exit(12)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
@@ -872,6 +872,23 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
 | 
				
			|||||||
        err!("`EVENT_CLEANUP_SCHEDULE` is not a valid cron expression")
 | 
					        err!("`EVENT_CLEANUP_SCHEDULE` is not a valid cron expression")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if !cfg.disable_admin_token {
 | 
				
			||||||
 | 
					        match cfg.admin_token.as_ref() {
 | 
				
			||||||
 | 
					            Some(t) if t.starts_with("$argon2") => {
 | 
				
			||||||
 | 
					                if let Err(e) = argon2::password_hash::PasswordHash::new(t) {
 | 
				
			||||||
 | 
					                    err!(format!("The configured Argon2 PHC in `ADMIN_TOKEN` is invalid: '{e}'"))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Some(_) => {
 | 
				
			||||||
 | 
					                println!(
 | 
				
			||||||
 | 
					                    "[NOTICE] You are using a plain text `ADMIN_TOKEN` which is insecure.\n\
 | 
				
			||||||
 | 
					                Please generate a secure Argon2 PHC string by using `vaultwarden hash` or `argon2`.\n\
 | 
				
			||||||
 | 
					                See: https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page#secure-the-admin_token\n"
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										106
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -118,14 +118,22 @@ async fn main() -> Result<(), Error> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const HELP: &str = "\
 | 
					const HELP: &str = "\
 | 
				
			||||||
        Alternative implementation of the Bitwarden server API written in Rust
 | 
					Alternative implementation of the Bitwarden server API written in Rust
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        USAGE:
 | 
					USAGE:
 | 
				
			||||||
            vaultwarden
 | 
					    vaultwarden [FLAGS|COMMAND]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FLAGS:
 | 
				
			||||||
 | 
					    -h, --help       Prints help information
 | 
				
			||||||
 | 
					    -v, --version    Prints the app version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COMMAND:
 | 
				
			||||||
 | 
					    hash [--preset {bitwarden|owasp}]  Generate an Argon2id PHC ADMIN_TOKEN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PRESETS:                  m=         t=          p=
 | 
				
			||||||
 | 
					    bitwarden (default) 64MiB, 3 Iterations, 4 Threads
 | 
				
			||||||
 | 
					    owasp               19MiB, 2 Iterations, 1 Thread
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        FLAGS:
 | 
					 | 
				
			||||||
            -h, --help       Prints help information
 | 
					 | 
				
			||||||
            -v, --version    Prints the app version
 | 
					 | 
				
			||||||
";
 | 
					";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const VERSION: Option<&str> = option_env!("VW_VERSION");
 | 
					pub const VERSION: Option<&str> = option_env!("VW_VERSION");
 | 
				
			||||||
@@ -142,24 +150,88 @@ fn parse_args() {
 | 
				
			|||||||
        println!("vaultwarden {version}");
 | 
					        println!("vaultwarden {version}");
 | 
				
			||||||
        exit(0);
 | 
					        exit(0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if let Some(command) = pargs.subcommand().unwrap_or_default() {
 | 
				
			||||||
 | 
					        if command == "hash" {
 | 
				
			||||||
 | 
					            use argon2::{
 | 
				
			||||||
 | 
					                password_hash::SaltString, Algorithm::Argon2id, Argon2, ParamsBuilder, PasswordHasher, Version::V0x13,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let mut argon2_params = ParamsBuilder::new();
 | 
				
			||||||
 | 
					            let preset: Option<String> = pargs.opt_value_from_str(["-p", "--preset"]).unwrap_or_default();
 | 
				
			||||||
 | 
					            let selected_preset;
 | 
				
			||||||
 | 
					            match preset.as_deref() {
 | 
				
			||||||
 | 
					                Some("owasp") => {
 | 
				
			||||||
 | 
					                    selected_preset = "owasp";
 | 
				
			||||||
 | 
					                    argon2_params.m_cost(19456);
 | 
				
			||||||
 | 
					                    argon2_params.t_cost(2);
 | 
				
			||||||
 | 
					                    argon2_params.p_cost(1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _ => {
 | 
				
			||||||
 | 
					                    // Bitwarden preset is the default
 | 
				
			||||||
 | 
					                    selected_preset = "bitwarden";
 | 
				
			||||||
 | 
					                    argon2_params.m_cost(65540);
 | 
				
			||||||
 | 
					                    argon2_params.t_cost(3);
 | 
				
			||||||
 | 
					                    argon2_params.p_cost(4);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            println!("Generate an Argon2id PHC string using the '{selected_preset}' preset:\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let password = rpassword::prompt_password("Password: ").unwrap();
 | 
				
			||||||
 | 
					            if password.len() < 8 {
 | 
				
			||||||
 | 
					                println!("\nPassword must contain at least 8 characters");
 | 
				
			||||||
 | 
					                exit(1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let password_verify = rpassword::prompt_password("Confirm Password: ").unwrap();
 | 
				
			||||||
 | 
					            if password != password_verify {
 | 
				
			||||||
 | 
					                println!("\nPasswords do not match");
 | 
				
			||||||
 | 
					                exit(1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let argon2 = Argon2::new(Argon2id, V0x13, argon2_params.build().unwrap());
 | 
				
			||||||
 | 
					            let salt = SaltString::b64_encode(&crate::crypto::get_random_bytes::<32>()).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let argon2_timer = tokio::time::Instant::now();
 | 
				
			||||||
 | 
					            if let Ok(password_hash) = argon2.hash_password(password.as_bytes(), &salt) {
 | 
				
			||||||
 | 
					                println!(
 | 
				
			||||||
 | 
					                    "\n\
 | 
				
			||||||
 | 
					                    ADMIN_TOKEN='{password_hash}'\n\n\
 | 
				
			||||||
 | 
					                    Generation of the Argon2id PHC string took: {:?}",
 | 
				
			||||||
 | 
					                    argon2_timer.elapsed()
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                error!("Unable to generate Argon2id PHC hash.");
 | 
				
			||||||
 | 
					                exit(1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        exit(0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
fn launch_info() {
 | 
					fn launch_info() {
 | 
				
			||||||
    println!("/--------------------------------------------------------------------\\");
 | 
					    println!(
 | 
				
			||||||
    println!("|                        Starting Vaultwarden                        |");
 | 
					        "\
 | 
				
			||||||
 | 
					        /--------------------------------------------------------------------\\\n\
 | 
				
			||||||
 | 
					        |                        Starting Vaultwarden                        |"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Some(version) = VERSION {
 | 
					    if let Some(version) = VERSION {
 | 
				
			||||||
        println!("|{:^68}|", format!("Version {version}"));
 | 
					        println!("|{:^68}|", format!("Version {version}"));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    println!("|--------------------------------------------------------------------|");
 | 
					    println!(
 | 
				
			||||||
    println!("| This is an *unofficial* Bitwarden implementation, DO NOT use the   |");
 | 
					        "\
 | 
				
			||||||
    println!("| official channels to report bugs/features, regardless of client.   |");
 | 
					        |--------------------------------------------------------------------|\n\
 | 
				
			||||||
    println!("| Send usage/configuration questions or feature requests to:         |");
 | 
					        | This is an *unofficial* Bitwarden implementation, DO NOT use the   |\n\
 | 
				
			||||||
    println!("|   https://vaultwarden.discourse.group/                             |");
 | 
					        | official channels to report bugs/features, regardless of client.   |\n\
 | 
				
			||||||
    println!("| Report suspected bugs/issues in the software itself at:            |");
 | 
					        | Send usage/configuration questions or feature requests to:         |\n\
 | 
				
			||||||
    println!("|   https://github.com/dani-garcia/vaultwarden/issues/new            |");
 | 
					        |   https://github.com/dani-garcia/vaultwarden/discussions or        |\n\
 | 
				
			||||||
    println!("\\--------------------------------------------------------------------/\n");
 | 
					        |   https://vaultwarden.discourse.group/                             |\n\
 | 
				
			||||||
 | 
					        | Report suspected bugs/issues in the software itself at:            |\n\
 | 
				
			||||||
 | 
					        |   https://github.com/dani-garcia/vaultwarden/issues/new            |\n\
 | 
				
			||||||
 | 
					        \\--------------------------------------------------------------------/\n"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> {
 | 
					fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										37
									
								
								src/static/scripts/admin_settings.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								src/static/scripts/admin_settings.js
									
									
									
									
										vendored
									
									
								
							@@ -157,6 +157,41 @@ function masterCheck(check_id, inputs_query) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This will check if the ADMIN_TOKEN is not a Argon2 hashed value.
 | 
				
			||||||
 | 
					// Else it will show a warning, unless someone has closed it.
 | 
				
			||||||
 | 
					// Then it will not show this warning for 30 days.
 | 
				
			||||||
 | 
					function checkAdminToken() {
 | 
				
			||||||
 | 
					    const admin_token = document.getElementById("input_admin_token");
 | 
				
			||||||
 | 
					    const disable_admin_token = document.getElementById("input_disable_admin_token");
 | 
				
			||||||
 | 
					    if (!disable_admin_token.checked && !admin_token.value.startsWith("$argon2")) {
 | 
				
			||||||
 | 
					        // Check if the warning has been closed before and 30 days have passed
 | 
				
			||||||
 | 
					        const admin_token_warning_closed = localStorage.getItem("admin_token_warning_closed");
 | 
				
			||||||
 | 
					        if (admin_token_warning_closed !== null) {
 | 
				
			||||||
 | 
					            const closed_date = new Date(parseInt(admin_token_warning_closed));
 | 
				
			||||||
 | 
					            const current_date = new Date();
 | 
				
			||||||
 | 
					            const thirtyDays = 1000*60*60*24*30;
 | 
				
			||||||
 | 
					            if (current_date - closed_date < thirtyDays) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // When closing the alert, store the current date/time in the browser
 | 
				
			||||||
 | 
					        const admin_token_warning = document.getElementById("admin_token_warning");
 | 
				
			||||||
 | 
					        admin_token_warning.addEventListener("closed.bs.alert", function() {
 | 
				
			||||||
 | 
					            const d = new Date();
 | 
				
			||||||
 | 
					            localStorage.setItem("admin_token_warning_closed", d.getTime());
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Display the warning
 | 
				
			||||||
 | 
					        admin_token_warning.classList.remove("d-none");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This will check for specific configured values, and when needed will show a warning div
 | 
				
			||||||
 | 
					function showWarnings() {
 | 
				
			||||||
 | 
					    checkAdminToken();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const config_form = document.getElementById("config-form");
 | 
					const config_form = document.getElementById("config-form");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// onLoad events
 | 
					// onLoad events
 | 
				
			||||||
@@ -192,4 +227,6 @@ document.addEventListener("DOMContentLoaded", (/*event*/) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    config_form.addEventListener("submit", saveConfig);
 | 
					    config_form.addEventListener("submit", saveConfig);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    showWarnings();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
@@ -1,4 +1,10 @@
 | 
				
			|||||||
<main class="container-xl">
 | 
					<main class="container-xl">
 | 
				
			||||||
 | 
					    <div id="admin_token_warning" class="alert alert-warning alert-dismissible fade show d-none">
 | 
				
			||||||
 | 
					        <button type="button" class="btn-close" data-bs-target="admin_token_warning" data-bs-dismiss="alert" aria-label="Close"></button>
 | 
				
			||||||
 | 
					        You are using a plain text `ADMIN_TOKEN` which is insecure.<br>
 | 
				
			||||||
 | 
					        Please generate a secure Argon2 PHC string by using `vaultwarden hash` or `argon2`.<br>
 | 
				
			||||||
 | 
					        See: <a href="https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page#secure-the-admin_token" target="_blank" rel="noopener noreferrer">Enabling admin page - Secure the `ADMIN_TOKEN`</a>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
    <div id="config-block" class="align-items-center p-3 mb-3 bg-secondary rounded shadow">
 | 
					    <div id="config-block" class="align-items-center p-3 mb-3 bg-secondary rounded shadow">
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
            <h6 class="text-white mb-3">Configuration</h6>
 | 
					            <h6 class="text-white mb-3">Configuration</h6>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user