2019-07-29 09:09:09 +02:00
#![ deny(warnings) ]
2020-08-04 04:58:11 +02:00
#![ warn(unused_extern_crates) ]
2022-02-20 03:43:38 +01:00
#![ deny(clippy::todo) ]
#![ deny(clippy::unimplemented) ]
2020-08-04 04:58:11 +02:00
#![ deny(clippy::unwrap_used) ]
#![ deny(clippy::expect_used) ]
#![ deny(clippy::panic) ]
#![ deny(clippy::unreachable) ]
#![ deny(clippy::await_holding_lock) ]
#![ deny(clippy::needless_pass_by_value) ]
#![ deny(clippy::trivially_copy_pass_by_ref) ]
2019-07-29 09:09:09 +02:00
2022-05-31 06:13:21 +02:00
#[ cfg(not(target_family = " windows " )) ]
2021-02-13 07:17:58 +01:00
#[ global_allocator ]
2022-05-24 02:49:34 +02:00
static ALLOC : tikv_jemallocator ::Jemalloc = tikv_jemallocator ::Jemalloc ;
2021-02-13 07:17:58 +01:00
2022-07-06 02:53:43 +02:00
#[ cfg(not(target_family = " windows " )) ] // not needed for windows builds
2020-07-28 08:55:58 +02:00
use users ::{ get_current_gid , get_current_uid , get_effective_gid , get_effective_uid } ;
2022-07-06 02:53:43 +02:00
#[ cfg(target_family = " windows " ) ] // for windows builds
use whoami ;
2020-07-28 08:55:58 +02:00
2021-12-31 00:11:20 +01:00
use serde ::Deserialize ;
2020-07-28 08:55:58 +02:00
use std ::fs ::{ metadata , File , Metadata } ;
2021-06-27 03:30:40 +02:00
#[ cfg(target_family = " unix " ) ]
2020-07-28 08:55:58 +02:00
use std ::os ::unix ::fs ::MetadataExt ;
2021-06-27 03:30:40 +02:00
use std ::io ::Read ;
2020-06-18 02:30:42 +02:00
use std ::path ::Path ;
2020-01-08 11:49:26 +01:00
use std ::path ::PathBuf ;
2020-06-18 02:30:42 +02:00
use std ::str ::FromStr ;
2019-05-08 02:39:46 +02:00
2020-06-18 02:30:42 +02:00
use kanidm ::audit ::LogLevel ;
2022-06-28 01:22:31 +02:00
use kanidm ::config ::{ Configuration , OnlineBackup , ServerRole } ;
2022-02-20 03:43:38 +01:00
use kanidm ::tracing_tree ;
2022-07-06 02:53:43 +02:00
#[ cfg(not(target_family = " windows " )) ]
2022-02-20 03:43:38 +01:00
use kanidm ::utils ::file_permissions_readonly ;
use score ::{
2021-07-01 06:51:25 +02:00
backup_server_core , create_server_core , dbscan_get_id2entry_core , dbscan_list_id2entry_core ,
dbscan_list_index_analysis_core , dbscan_list_index_core , dbscan_list_indexes_core ,
domain_rename_core , recover_account_core , reindex_server_core , restore_server_core ,
vacuum_server_core , verify_server_core ,
2019-07-29 09:09:18 +02:00
} ;
2019-07-15 01:15:25 +02:00
2022-06-11 07:24:29 +02:00
use clap ::{ Args , Parser , Subcommand } ;
2019-07-15 01:15:25 +02:00
2021-02-13 04:46:22 +01:00
include! ( " ./opt.rs " ) ;
2020-06-18 02:30:42 +02:00
#[ derive(Debug, Deserialize) ]
struct ServerConfig {
pub bindaddress : Option < String > ,
pub ldapbindaddress : Option < String > ,
// pub threads: Option<usize>,
pub db_path : String ,
2020-08-04 08:52:57 +02:00
pub db_fs_type : Option < String > ,
2021-04-14 01:56:40 +02:00
pub db_arc_size : Option < usize > ,
2021-02-16 02:40:25 +01:00
pub tls_chain : Option < String > ,
2020-06-18 02:30:42 +02:00
pub tls_key : Option < String > ,
pub log_level : Option < String > ,
2021-07-31 09:13:46 +02:00
pub online_backup : Option < OnlineBackup > ,
2022-02-15 07:17:43 +01:00
pub domain : String ,
2020-12-02 02:12:07 +01:00
pub origin : String ,
2021-05-06 12:58:22 +02:00
#[ serde(default) ]
pub role : ServerRole ,
2020-06-18 02:30:42 +02:00
}
impl ServerConfig {
pub fn new < P : AsRef < Path > > ( config_path : P ) -> Result < Self , ( ) > {
let mut f = File ::open ( config_path ) . map_err ( | e | {
eprintln! ( " Unable to open config file [ {:?} ] 🥺 " , e ) ;
} ) ? ;
let mut contents = String ::new ( ) ;
f . read_to_string ( & mut contents )
. map_err ( | e | eprintln! ( " unable to read contents {:?} " , e ) ) ? ;
toml ::from_str ( contents . as_str ( ) ) . map_err ( | e | eprintln! ( " unable to parse config {:?} " , e ) )
}
2019-07-15 01:15:25 +02:00
}
2021-02-13 04:46:22 +01:00
impl KanidmdOpt {
2020-06-18 02:30:42 +02:00
fn commonopt ( & self ) -> & CommonOpt {
2019-09-06 05:05:27 +02:00
match self {
2021-02-21 06:04:58 +01:00
KanidmdOpt ::Server ( sopt )
2022-02-15 07:17:43 +01:00
| KanidmdOpt ::ConfigTest ( sopt )
2022-06-11 07:24:29 +02:00
| KanidmdOpt ::DbScan {
commands : DbScanOpt ::ListIndexes ( sopt ) ,
}
| KanidmdOpt ::DbScan {
commands : DbScanOpt ::ListId2Entry ( sopt ) ,
}
| KanidmdOpt ::DbScan {
commands : DbScanOpt ::ListIndexAnalysis ( sopt ) ,
} = > & sopt ,
2022-07-07 05:03:08 +02:00
KanidmdOpt ::Database {
commands : DbCommands ::Backup ( bopt ) ,
} = > & bopt . commonopts ,
KanidmdOpt ::Database {
commands : DbCommands ::Restore ( ropt ) ,
} = > & ropt . commonopts ,
2021-02-13 04:46:22 +01:00
KanidmdOpt ::RecoverAccount ( ropt ) = > & ropt . commonopts ,
2022-06-11 07:24:29 +02:00
KanidmdOpt ::DbScan {
commands : DbScanOpt ::ListIndex ( dopt ) ,
} = > & dopt . commonopts ,
2021-07-01 06:51:25 +02:00
// KanidmdOpt::DbScan(DbScanOpt::GetIndex(dopt)) => &dopt.commonopts,
2022-06-11 07:24:29 +02:00
KanidmdOpt ::DbScan {
commands : DbScanOpt ::GetId2Entry ( dopt ) ,
} = > & dopt . commonopts ,
2022-07-07 05:03:08 +02:00
KanidmdOpt ::DomainSettings {
commands : DomainSettingsCmds ::DomainChange ( sopt ) ,
} = > & sopt ,
KanidmdOpt ::Database {
commands : DbCommands ::Verify ( sopt ) ,
}
| KanidmdOpt ::Database {
commands : DbCommands ::Reindex ( sopt ) ,
} = > & sopt ,
KanidmdOpt ::Database {
commands : DbCommands ::Vacuum ( copt ) ,
} = > & copt ,
2019-09-06 05:05:27 +02:00
}
}
}
2020-07-28 08:55:58 +02:00
fn read_file_metadata ( path : & PathBuf ) -> Metadata {
match metadata ( path ) {
Ok ( m ) = > m ,
Err ( e ) = > {
eprintln! (
" Unable to read metadata for {} - {:?} " ,
2020-08-04 04:58:11 +02:00
path . to_str ( ) . unwrap_or ( " invalid file path " ) ,
2020-07-28 08:55:58 +02:00
e
) ;
std ::process ::exit ( 1 ) ;
}
}
}
2022-07-06 02:53:43 +02:00
/// Gets the user details if we're running in unix-land
#[ cfg(not(target_family = " windows " )) ]
fn get_user_details_unix ( ) -> ( u32 , u32 ) {
2020-07-28 08:55:58 +02:00
let cuid = get_current_uid ( ) ;
let ceuid = get_effective_uid ( ) ;
let cgid = get_current_gid ( ) ;
let cegid = get_effective_gid ( ) ;
if cuid = = 0 | | ceuid = = 0 | | cgid = = 0 | | cegid = = 0 {
2020-10-30 02:12:06 +01:00
eprintln! ( " WARNING: This is running as uid == 0 (root) which may be a security risk. " ) ;
// eprintln!("ERROR: Refusing to run - this process must not operate as root.");
// std::process::exit(1);
2020-07-28 08:55:58 +02:00
}
if cuid ! = ceuid | | cgid ! = cegid {
eprintln! ( " {} != {} || {} != {} " , cuid , ceuid , cgid , cegid ) ;
eprintln! ( " ERROR: Refusing to run - uid and euid OR gid and egid must be consistent. " ) ;
std ::process ::exit ( 1 ) ;
}
2022-07-06 02:53:43 +02:00
( cuid , ceuid )
}
/// Get information on the windows username
#[ cfg(target_family = " windows " ) ]
fn get_user_details_windows ( ) {
eprintln! (
" Running on windows, current username is: {:?} " ,
whoami ::username ( )
) ;
}
#[ tokio::main ]
async fn main ( ) {
tracing_tree ::main_init ( ) ;
// Get information on the windows username
#[ cfg(target_family = " windows " ) ]
get_user_details_windows ( ) ;
// Get info about who we are.
#[ cfg(target_family = " unix " ) ]
let ( cuid , ceuid ) = get_user_details_unix ( ) ;
2020-07-28 08:55:58 +02:00
2019-07-15 01:15:25 +02:00
// Read cli args, determine if we should backup/restore
2022-06-11 07:24:29 +02:00
let opt = KanidmdParser ::parse ( ) ;
2019-07-15 01:15:25 +02:00
let mut config = Configuration ::new ( ) ;
2022-07-06 02:53:43 +02:00
// Check the permissions are OK.
#[ cfg(target_family = " unix " ) ]
{
let cfg_meta = read_file_metadata ( & ( opt . commands . commonopt ( ) . config_path ) ) ;
#[ cfg(target_family = " unix " ) ]
if ! file_permissions_readonly ( & cfg_meta ) {
eprintln! ( " WARNING: permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ... " ,
opt . commands . commonopt ( ) . config_path . to_str ( ) . unwrap_or ( " invalid file path " ) ) ;
}
2020-07-28 08:55:58 +02:00
2022-07-06 02:53:43 +02:00
#[ cfg(target_family = " unix " ) ]
if cfg_meta . mode ( ) & 0o007 ! = 0 {
eprintln! ( " WARNING: {} has 'everyone' permission bits in the mode. This could be a security risk ... " ,
opt . commands . commonopt ( ) . config_path . to_str ( ) . unwrap_or ( " invalid file path " )
) ;
}
2020-07-28 08:55:58 +02:00
2022-07-06 02:53:43 +02:00
#[ cfg(target_family = " unix " ) ]
if cfg_meta . uid ( ) = = cuid | | cfg_meta . uid ( ) = = ceuid {
eprintln! ( " WARNING: {} owned by the current uid, which may allow file permission changes. This could be a security risk ... " ,
opt . commands . commonopt ( ) . config_path . to_str ( ) . unwrap_or ( " invalid file path " )
) ;
}
2020-07-28 08:55:58 +02:00
}
// Read our config
2022-06-11 07:24:29 +02:00
let sconfig = match ServerConfig ::new ( & ( opt . commands . commonopt ( ) . config_path ) ) {
2020-06-18 02:30:42 +02:00
Ok ( c ) = > c ,
Err ( e ) = > {
eprintln! ( " Config Parse failure {:?} " , e ) ;
std ::process ::exit ( 1 ) ;
}
} ;
// Apply the file requirements
let ll = sconfig
. log_level
. map ( | ll | match LogLevel ::from_str ( ll . as_str ( ) ) {
Ok ( v ) = > v as u32 ,
Err ( e ) = > {
eprintln! ( " {:?} " , e ) ;
std ::process ::exit ( 1 ) ;
}
} ) ;
2020-07-28 08:55:58 +02:00
// Check the permissions of the files from the configuration.
let db_path = PathBuf ::from ( sconfig . db_path . as_str ( ) ) ;
2021-07-08 02:09:15 +02:00
// We can't check the db_path permissions because it may not exist yet!
2020-07-28 08:55:58 +02:00
if let Some ( db_parent_path ) = db_path . parent ( ) {
if ! db_parent_path . exists ( ) {
eprintln! (
" DB folder {} may not exist, server startup may FAIL! " ,
2020-08-04 04:58:11 +02:00
db_parent_path . to_str ( ) . unwrap_or ( " invalid file path " )
2020-07-28 08:55:58 +02:00
) ;
}
let db_par_path_buf = db_parent_path . to_path_buf ( ) ;
let i_meta = read_file_metadata ( & db_par_path_buf ) ;
if ! i_meta . is_dir ( ) {
eprintln! (
" ERROR: Refusing to run - DB folder {} may not be a directory " ,
2020-08-04 04:58:11 +02:00
db_par_path_buf . to_str ( ) . unwrap_or ( " invalid file path " )
2020-07-28 08:55:58 +02:00
) ;
std ::process ::exit ( 1 ) ;
}
2022-07-06 02:53:43 +02:00
// TODO: windows support for DB folder permissions checks
#[ cfg(target_family = " unix " ) ]
{
if ! file_permissions_readonly ( & i_meta ) {
eprintln! ( " WARNING: DB folder permissions on {} indicate it may not be RW. This could cause the server start up to fail! " , db_par_path_buf . to_str ( ) . unwrap_or ( " invalid file path " ) ) ;
}
if i_meta . mode ( ) & 0o007 ! = 0 {
eprintln! ( " WARNING: DB folder {} has 'everyone' permission bits in the mode. This could be a security risk ... " , db_par_path_buf . to_str ( ) . unwrap_or ( " invalid file path " ) ) ;
}
2020-07-28 08:55:58 +02:00
}
}
2020-06-18 02:30:42 +02:00
config . update_log_level ( ll ) ;
config . update_db_path ( & sconfig . db_path . as_str ( ) ) ;
2020-08-04 08:52:57 +02:00
config . update_db_fs_type ( & sconfig . db_fs_type ) ;
2020-12-02 02:12:07 +01:00
config . update_origin ( & sconfig . origin . as_str ( ) ) ;
2022-02-15 07:17:43 +01:00
config . update_domain ( & sconfig . domain . as_str ( ) ) ;
2021-04-14 01:56:40 +02:00
config . update_db_arc_size ( sconfig . db_arc_size ) ;
2021-05-06 12:58:22 +02:00
config . update_role ( sconfig . role ) ;
2022-06-28 01:22:31 +02:00
config . update_output_mode ( opt . commands . commonopt ( ) . output_mode . to_owned ( ) . into ( ) ) ;
2020-06-18 02:30:42 +02:00
// Apply any cli overrides, normally debug level.
2022-06-11 07:24:29 +02:00
if let Some ( dll ) = opt . commands . commonopt ( ) . debug . as_ref ( ) {
2020-06-21 13:57:48 +02:00
config . update_log_level ( Some ( dll . clone ( ) as u32 ) ) ;
}
2021-03-26 02:22:00 +01:00
// ::std::env::set_var("RUST_LOG", "tide=info,kanidm=info,webauthn=debug");
2021-12-17 04:54:13 +01:00
// env_logger::builder()
// .format_timestamp(None)
// .format_level(false)
// .init();
2019-07-12 07:28:46 +02:00
2022-06-11 07:24:29 +02:00
match & opt . commands {
2022-02-15 07:17:43 +01:00
KanidmdOpt ::Server ( _sopt ) | KanidmdOpt ::ConfigTest ( _sopt ) = > {
2022-06-11 07:24:29 +02:00
let config_test = matches! ( & opt . commands , KanidmdOpt ::ConfigTest ( _ ) ) ;
2022-02-15 07:17:43 +01:00
if config_test {
eprintln! ( " Running in server configuration test mode ... " ) ;
} else {
eprintln! ( " Running in server mode ... " ) ;
} ;
2021-07-09 01:49:26 +02:00
// configuration options that only relate to server mode
config . update_tls ( & sconfig . tls_chain , & sconfig . tls_key ) ;
config . update_bind ( & sconfig . bindaddress ) ;
config . update_ldapbind ( & sconfig . ldapbindaddress ) ;
2021-07-31 09:13:46 +02:00
config . update_online_backup ( & sconfig . online_backup ) ;
2021-07-09 01:49:26 +02:00
if let Some ( i_str ) = & ( sconfig . tls_chain ) {
let i_path = PathBuf ::from ( i_str . as_str ( ) ) ;
2022-07-06 02:53:43 +02:00
// TODO: windows support for DB folder permissions checks
#[ cfg(not(target_family = " unix " )) ]
eprintln! ( " WARNING: permissions checks on windows aren't implemented, cannot check TLS Key at {:?} " , i_path ) ;
#[ cfg(target_family = " unix " ) ]
{
let i_meta = read_file_metadata ( & i_path ) ;
if ! file_permissions_readonly ( & i_meta ) {
eprintln! ( " WARNING: permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ... " , i_str ) ;
}
2021-07-09 01:49:26 +02:00
}
}
if let Some ( i_str ) = & ( sconfig . tls_key ) {
let i_path = PathBuf ::from ( i_str . as_str ( ) ) ;
2022-07-06 02:53:43 +02:00
// TODO: windows support for DB folder permissions checks
#[ cfg(not(target_family = " unix " )) ]
eprintln! ( " WARNING: permissions checks on windows aren't implemented, cannot check TLS Key at {:?} " , i_path ) ;
// TODO: windows support for DB folder permissions checks
#[ cfg(target_family = " unix " ) ]
{
let i_meta = read_file_metadata ( & i_path ) ;
if ! file_permissions_readonly ( & i_meta ) {
eprintln! ( " WARNING: permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ... " , i_str ) ;
}
if i_meta . mode ( ) & 0o007 ! = 0 {
eprintln! ( " WARNING: {} has 'everyone' permission bits in the mode. This could be a security risk ... " , i_str ) ;
}
2021-07-09 01:49:26 +02:00
}
}
2022-02-15 07:17:43 +01:00
let sctx = create_server_core ( config , config_test ) . await ;
if ! config_test {
match sctx {
Ok ( _sctx ) = > match tokio ::signal ::ctrl_c ( ) . await {
Ok ( _ ) = > {
eprintln! ( " Ctrl-C received, shutting down " ) ;
}
Err ( _ ) = > {
eprintln! ( " Invalid signal received, shutting down as a precaution ... " ) ;
}
} ,
2020-08-04 04:58:11 +02:00
Err ( _ ) = > {
2022-02-15 07:17:43 +01:00
eprintln! ( " Failed to start server core! " ) ;
// We may need to return an exit code here, but that may take some re-architecting
// to ensure we drop everything cleanly.
return ;
2020-08-04 04:58:11 +02:00
}
2020-06-05 06:01:20 +02:00
}
2022-02-15 07:17:43 +01:00
eprintln! ( " stopped 🛑 " ) ;
2020-06-05 06:01:20 +02:00
}
2019-07-15 01:15:25 +02:00
}
2022-07-07 05:03:08 +02:00
KanidmdOpt ::Database {
commands : DbCommands ::Backup ( bopt ) ,
} = > {
2020-06-18 02:30:42 +02:00
eprintln! ( " Running in backup mode ... " ) ;
2019-07-15 01:15:25 +02:00
let p = match bopt . path . to_str ( ) {
Some ( p ) = > p ,
None = > {
2020-06-18 02:30:42 +02:00
eprintln! ( " Invalid backup path " ) ;
2019-07-15 01:15:25 +02:00
std ::process ::exit ( 1 ) ;
}
} ;
2020-08-04 04:58:11 +02:00
backup_server_core ( & config , p ) ;
2019-07-15 01:15:25 +02:00
}
2022-07-07 05:03:08 +02:00
KanidmdOpt ::Database {
commands : DbCommands ::Restore ( ropt ) ,
} = > {
2020-06-18 02:30:42 +02:00
eprintln! ( " Running in restore mode ... " ) ;
2019-07-15 01:15:25 +02:00
let p = match ropt . path . to_str ( ) {
Some ( p ) = > p ,
None = > {
2020-06-18 02:30:42 +02:00
eprintln! ( " Invalid restore path " ) ;
2019-07-15 01:15:25 +02:00
std ::process ::exit ( 1 ) ;
}
} ;
2020-08-04 04:58:11 +02:00
restore_server_core ( & config , p ) ;
2019-07-15 01:15:25 +02:00
}
2022-07-07 05:03:08 +02:00
KanidmdOpt ::Database {
commands : DbCommands ::Verify ( _vopt ) ,
} = > {
2020-06-18 02:30:42 +02:00
eprintln! ( " Running in db verification mode ... " ) ;
2020-08-04 04:58:11 +02:00
verify_server_core ( & config ) ;
2019-07-29 09:09:09 +02:00
}
2021-02-13 04:46:22 +01:00
KanidmdOpt ::RecoverAccount ( raopt ) = > {
2020-06-18 02:30:42 +02:00
eprintln! ( " Running account recovery ... " ) ;
2021-06-02 01:30:37 +02:00
recover_account_core ( & config , & raopt . name ) ;
2019-09-04 03:06:37 +02:00
}
2022-07-07 05:03:08 +02:00
KanidmdOpt ::Database {
commands : DbCommands ::Reindex ( _copt ) ,
} = > {
2020-06-18 02:30:42 +02:00
eprintln! ( " Running in reindex mode ... " ) ;
2020-08-04 04:58:11 +02:00
reindex_server_core ( & config ) ;
2019-11-17 03:36:32 +01:00
}
2022-06-11 07:24:29 +02:00
KanidmdOpt ::DbScan {
commands : DbScanOpt ::ListIndexes ( _ ) ,
} = > {
2021-07-01 06:51:25 +02:00
eprintln! ( " 👀 db scan - list indexes " ) ;
dbscan_list_indexes_core ( & config ) ;
}
2022-06-11 07:24:29 +02:00
KanidmdOpt ::DbScan {
commands : DbScanOpt ::ListId2Entry ( _ ) ,
} = > {
2021-07-01 06:51:25 +02:00
eprintln! ( " 👀 db scan - list id2entry " ) ;
dbscan_list_id2entry_core ( & config ) ;
}
2022-06-11 07:24:29 +02:00
KanidmdOpt ::DbScan {
commands : DbScanOpt ::ListIndexAnalysis ( _ ) ,
} = > {
2021-07-01 06:51:25 +02:00
eprintln! ( " 👀 db scan - list index analysis " ) ;
dbscan_list_index_analysis_core ( & config ) ;
}
2022-06-11 07:24:29 +02:00
KanidmdOpt ::DbScan {
commands : DbScanOpt ::ListIndex ( dopt ) ,
} = > {
2021-07-01 06:51:25 +02:00
eprintln! ( " 👀 db scan - list index content - {} " , dopt . index_name ) ;
dbscan_list_index_core ( & config , dopt . index_name . as_str ( ) ) ;
}
2022-06-11 07:24:29 +02:00
KanidmdOpt ::DbScan {
commands : DbScanOpt ::GetId2Entry ( dopt ) ,
} = > {
2021-07-01 06:51:25 +02:00
eprintln! ( " 👀 db scan - get id2 entry - {} " , dopt . id ) ;
dbscan_get_id2entry_core ( & config , dopt . id ) ;
}
2022-07-07 05:03:08 +02:00
KanidmdOpt ::DomainSettings {
commands : DomainSettingsCmds ::DomainChange ( _dopt ) ,
} = > {
eprintln! ( " Running in domain name change mode ... this may take a long time ... " ) ;
domain_rename_core ( & config ) ;
}
KanidmdOpt ::Database {
commands : DbCommands ::Vacuum ( _copt ) ,
} = > {
eprintln! ( " Running in vacuum mode ... " ) ;
vacuum_server_core ( & config ) ;
}
2019-07-15 01:15:25 +02:00
}
2018-09-29 09:54:16 +02:00
}