diff --git a/scripts/test_run_release_server.sh b/scripts/test_run_release_server.sh index 2b556c2c5..896dfafcb 100755 --- a/scripts/test_run_release_server.sh +++ b/scripts/test_run_release_server.sh @@ -9,6 +9,7 @@ set -e WAIT_TIMER=5 + echo "Building release binaries..." cargo build --release --bin kanidm --bin kanidmd @@ -18,6 +19,7 @@ if [ -d '.git' ]; then cd server/daemon/ || exit 1 fi + if [ ! -f "run_insecure_dev_server.sh" ]; then echo "I'm not sure where you are, please run this from the root of the repository or the server/daemon directory" exit 1 @@ -29,12 +31,13 @@ echo "Generating certificates..." cargo run --bin kanidmd --release cert-generate --config ../../examples/insecure_server.toml echo "Making sure it runs with the DB..." -cargo run --bin kanidmd --release recover-account idm_admin -o json +cargo run --bin kanidmd --release recover-account idm_admin -o json --config ../../examples/insecure_server.toml echo "Running the server..." cargo run --bin kanidmd --release server --config ../../examples/insecure_server.toml & KANIDMD_PID=$! -echo "${KANIDMD_PID}" +echo "Kanidm PID: ${KANIDMD_PID}" + if [ "$(jobs -p | wc -l)" -eq 0 ]; then echo "Kanidmd failed to start!" @@ -48,8 +51,8 @@ KANIDM_URL="$(rg origin "${KANIDM_CONFIG_FILE}" | awk '{print $NF}' | tr -d '"') KANIDM_CA_PATH="/tmp/kanidm/ca.pem" while true; do - echo "Waiting the server to start... testing ${KANIDM_URL}" - curl --cacert "${KANIDM_CA_PATH}" -fs "${KANIDM_URL}" >/dev/null && break + echo "Waiting for the server to start... testing ${KANIDM_URL}" + curl --cacert "${KANIDM_CA_PATH}" -fs "${KANIDM_URL}/status" >/dev/null && break sleep 2 ATTEMPT="$((ATTEMPT + 1))" if [ "${ATTEMPT}" -gt 3 ]; then diff --git a/server/core/src/https/mod.rs b/server/core/src/https/mod.rs index 5b7cbc8a6..6bd5ff98f 100644 --- a/server/core/src/https/mod.rs +++ b/server/core/src/https/mod.rs @@ -181,6 +181,7 @@ pub async fn create_https_server( qe_w_ref: &'static QueryServerWriteV1, qe_r_ref: &'static QueryServerReadV1, mut rx: broadcast::Receiver, + server_message_tx: broadcast::Sender, ) -> Result, ()> { let js_files = get_js_files(config.role)?; // set up the CSP headers @@ -354,10 +355,26 @@ pub async fn create_https_server( tokio::spawn(axum_server::bind(addr).serve(app)) } } => { - if let Err(err) = res { - error!("Web server exited with {:?}", err); - } + match res { + Ok(res_inner) => { + match res_inner { + Ok(_) => debug!("Web server exited OK"), + Err(err) => { + error!("Web server exited with {:?}", err); + } + } + + }, + Err(err) => { + error!("Web server exited with {:?}", err); + } + }; + if let Err(err) = server_message_tx.send(CoreAction::Shutdown) { + error!("Web server failed to send shutdown message! {:?}", err) + }; } + + }; #[cfg(feature = "otel")] opentelemetry::global::shutdown_tracer_provider(); @@ -399,10 +416,7 @@ async fn server_loop( // If configured, setup TLS client authentication. if let Some(client_ca) = tls_param.client_ca.as_ref() { - debug!( - "Configuring client certificates from {}", - client_ca.display() - ); + info!("Loading client certificates from {}", client_ca.display()); let verify = SslVerifyMode::PEER; // In future we may add a "require mTLS option" which would necesitate this. @@ -425,7 +439,11 @@ async fn server_loop( let read_dir = fs::read_dir(client_ca).map_err(|err| { std::io::Error::new( ErrorKind::Other, - format!("Failed to create TLS listener: {:?}", err), + format!( + "Failed to create TLS listener while loading client ca from {}: {:?}", + client_ca.display(), + err + ), ) })?; @@ -458,7 +476,10 @@ async fn server_loop( cert_store.add_cert(cert.clone()).map_err(|err| { std::io::Error::new( ErrorKind::Other, - format!("Failed to create TLS listener: {:?}", err), + format!( + "Failed to load cert store while creating TLS listener: {:?}", + err + ), ) })?; // This tells the client what CA's they should use. It DOES NOT diff --git a/server/core/src/lib.rs b/server/core/src/lib.rs index 47fc854e1..a0a56f6ec 100644 --- a/server/core/src/lib.rs +++ b/server/core/src/lib.rs @@ -748,7 +748,7 @@ impl Display for TaskName { pub struct CoreHandle { clean_shutdown: bool, - tx: broadcast::Sender, + pub tx: broadcast::Sender, /// This stores a name for the handle, and the handle itself so we can tell which failed/succeeded at the end. handles: Vec<(TaskName, tokio::task::JoinHandle<()>)>, } @@ -1068,6 +1068,7 @@ pub async fn create_server_core( server_write_ref, server_read_ref, broadcast_tx.subscribe(), + broadcast_tx.clone(), ) .await { diff --git a/server/daemon/src/main.rs b/server/daemon/src/main.rs index 48c4b0bee..d2523896b 100644 --- a/server/daemon/src/main.rs +++ b/server/daemon/src/main.rs @@ -530,6 +530,43 @@ async fn kanidm_main() -> ExitCode { } } + if let Some(ca_dir) = &(sconfig.tls_client_ca) { + // check that the TLS client CA config option is what we expect + let ca_dir_path = PathBuf::from(&ca_dir); + if !ca_dir_path.exists() { + error!( + "TLS CA folder {} does not exist, server startup will FAIL!", + ca_dir + ); + let diag = kanidm_lib_file_permissions::diagnose_path(&ca_dir_path); + info!(%diag); + } + + let i_meta = match metadata(&ca_dir_path) { + Ok(m) => m, + Err(e) => { + error!("Unable to read metadata for '{}' - {:?}", ca_dir, e); + let diag = kanidm_lib_file_permissions::diagnose_path(&ca_dir_path); + info!(%diag); + return ExitCode::FAILURE; + } + }; + if !i_meta.is_dir() { + error!( + "ERROR: Refusing to run - TLS Client CA folder {} may not be a directory", + ca_dir + ); + return ExitCode::FAILURE; + } + if kanidm_lib_file_permissions::readonly(&i_meta) { + warn!("WARNING: TLS Client CA folder permissions on {} indicate it may not be RW. This could cause the server start up to fail!", ca_dir); + } + #[cfg(not(target_os = "windows"))] + if i_meta.mode() & 0o007 != 0 { + warn!("WARNING: TLS Client CA folder {} has 'everyone' permission bits in the mode. This could be a security risk ...", ca_dir); + } + } + let sctx = create_server_core(config, config_test).await; if !config_test { // On linux, notify systemd. @@ -541,46 +578,54 @@ async fn kanidm_main() -> ExitCode { loop { #[cfg(target_family = "unix")] { + let mut listener = sctx.tx.subscribe(); tokio::select! { - Ok(()) = tokio::signal::ctrl_c() => { - break - } - Some(()) = async move { - let sigterm = tokio::signal::unix::SignalKind::terminate(); - #[allow(clippy::unwrap_used)] - tokio::signal::unix::signal(sigterm).unwrap().recv().await - } => { - break - } - Some(()) = async move { - let sigterm = tokio::signal::unix::SignalKind::alarm(); - #[allow(clippy::unwrap_used)] - tokio::signal::unix::signal(sigterm).unwrap().recv().await - } => { - // Ignore - } - Some(()) = async move { - let sigterm = tokio::signal::unix::SignalKind::hangup(); - #[allow(clippy::unwrap_used)] - tokio::signal::unix::signal(sigterm).unwrap().recv().await - } => { - // Ignore - } - Some(()) = async move { - let sigterm = tokio::signal::unix::SignalKind::user_defined1(); - #[allow(clippy::unwrap_used)] - tokio::signal::unix::signal(sigterm).unwrap().recv().await - } => { - // Ignore - } - Some(()) = async move { - let sigterm = tokio::signal::unix::SignalKind::user_defined2(); - #[allow(clippy::unwrap_used)] - tokio::signal::unix::signal(sigterm).unwrap().recv().await - } => { - // Ignore - } - } + Ok(()) = tokio::signal::ctrl_c() => { + break + } + Some(()) = async move { + let sigterm = tokio::signal::unix::SignalKind::terminate(); + #[allow(clippy::unwrap_used)] + tokio::signal::unix::signal(sigterm).unwrap().recv().await + } => { + break + } + Some(()) = async move { + let sigterm = tokio::signal::unix::SignalKind::alarm(); + #[allow(clippy::unwrap_used)] + tokio::signal::unix::signal(sigterm).unwrap().recv().await + } => { + // Ignore + } + Some(()) = async move { + let sigterm = tokio::signal::unix::SignalKind::hangup(); + #[allow(clippy::unwrap_used)] + tokio::signal::unix::signal(sigterm).unwrap().recv().await + } => { + // Ignore + } + Some(()) = async move { + let sigterm = tokio::signal::unix::SignalKind::user_defined1(); + #[allow(clippy::unwrap_used)] + tokio::signal::unix::signal(sigterm).unwrap().recv().await + } => { + // Ignore + } + Some(()) = async move { + let sigterm = tokio::signal::unix::SignalKind::user_defined2(); + #[allow(clippy::unwrap_used)] + tokio::signal::unix::signal(sigterm).unwrap().recv().await + } => { + // Ignore + } + // we got a message on thr broadcast from somewhere else + Ok(msg) = async move { + listener.recv().await + } => { + debug!("Main loop received message: {:?}", msg); + break + } + } } #[cfg(target_family = "windows")] {