@ -61,6 +61,11 @@ use std::{
thread ,
} ;
use tokio ::{
fs ::File ,
io ::{ AsyncBufReadExt , BufReader } ,
} ;
#[ macro_use ]
mod error ;
mod api ;
@ -89,7 +94,7 @@ async fn main() -> Result<(), Error> {
let extra_debug = matches! ( level , LF ::Trace | LF ::Debug ) ;
check_data_folder ( ) ;
check_data_folder ( ) .await ;
check_rsa_keys ( ) . unwrap_or_else ( | _ | {
error ! ( "Error creating keys, exiting..." ) ;
exit ( 1 ) ;
@ -286,7 +291,7 @@ fn create_dir(path: &str, description: &str) {
create_dir_all ( path ) . expect ( & err_msg ) ;
}
fn check_data_folder ( ) {
async fn check_data_folder ( ) {
let data_folder = & CONFIG . data_folder ( ) ;
let path = Path ::new ( data_folder ) ;
if ! path . exists ( ) {
@ -299,9 +304,10 @@ fn check_data_folder() {
exit ( 1 ) ;
}
let persistent_volume_check_file = format! ( "{data_folder}/vaultwarden_docker_persistent_volume_check" ) ;
let check_file = Path ::new ( & persistent_volume_check_file ) ;
if check_file . exists ( ) & & std ::env ::var ( "I_REALLY_WANT_VOLATILE_STORAGE" ) . is_err ( ) {
if is_running_in_docker ( )
& & std ::env ::var ( "I_REALLY_WANT_VOLATILE_STORAGE" ) . is_err ( )
& & ! docker_data_folder_is_persistent ( data_folder ) . await
{
error ! (
" No persistent volume ! \ n \
########################################################################################\ n \
@ -314,6 +320,38 @@ fn check_data_folder() {
}
}
/// Detect when using Docker or Podman the DATA_FOLDER is either a bind-mount or a volume created manually.
/// If not created manually, then the data will not be persistent.
/// A none persistent volume in either Docker or Podman is represented by a 64 alphanumerical string.
/// If we detect this string, we will alert about not having a persistent self defined volume.
/// This probably means that someone forgot to add `-v /path/to/vaultwarden_data/:/data`
async fn docker_data_folder_is_persistent ( data_folder : & str ) -> bool {
if let Ok ( mountinfo ) = File ::open ( "/proc/self/mountinfo" ) . await {
// Since there can only be one mountpoint to the DATA_FOLDER
// We do a basic check for this mountpoint surrounded by a space.
let data_folder_match = if data_folder . starts_with ( '/' ) {
format! ( " {data_folder} " )
} else {
format! ( " /{data_folder} " )
} ;
let mut lines = BufReader ::new ( mountinfo ) . lines ( ) ;
while let Some ( line ) = lines . next_line ( ) . await . unwrap_or_default ( ) {
// Only execute a regex check if we find the base match
if line . contains ( & data_folder_match ) {
let re = regex ::Regex ::new ( r"/volumes/[a-z0-9]{64}/_data /" ) . unwrap ( ) ;
if re . is_match ( & line ) {
return false ;
}
// If we did found a match for the mountpoint, but not the regex, then still stop searching.
break ;
}
}
}
// In all other cases, just assume a true.
// This is just an informative check to try and prevent data loss.
true
}
fn check_rsa_keys ( ) -> Result < ( ) , crate ::error ::Error > {
// If the RSA keys don't exist, try to create them
let priv_path = CONFIG . private_rsa_key ( ) ;