mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
when the HTTPS server fails, handle that gracefully (#2546)
This commit is contained in:
parent
816fde766f
commit
48f33fb8c9
|
@ -9,6 +9,7 @@ set -e
|
||||||
|
|
||||||
WAIT_TIMER=5
|
WAIT_TIMER=5
|
||||||
|
|
||||||
|
|
||||||
echo "Building release binaries..."
|
echo "Building release binaries..."
|
||||||
cargo build --release --bin kanidm --bin kanidmd
|
cargo build --release --bin kanidm --bin kanidmd
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ if [ -d '.git' ]; then
|
||||||
cd server/daemon/ || exit 1
|
cd server/daemon/ || exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
if [ ! -f "run_insecure_dev_server.sh" ]; then
|
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"
|
echo "I'm not sure where you are, please run this from the root of the repository or the server/daemon directory"
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -29,12 +31,13 @@ echo "Generating certificates..."
|
||||||
cargo run --bin kanidmd --release cert-generate --config ../../examples/insecure_server.toml
|
cargo run --bin kanidmd --release cert-generate --config ../../examples/insecure_server.toml
|
||||||
|
|
||||||
echo "Making sure it runs with the DB..."
|
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..."
|
echo "Running the server..."
|
||||||
cargo run --bin kanidmd --release server --config ../../examples/insecure_server.toml &
|
cargo run --bin kanidmd --release server --config ../../examples/insecure_server.toml &
|
||||||
KANIDMD_PID=$!
|
KANIDMD_PID=$!
|
||||||
echo "${KANIDMD_PID}"
|
echo "Kanidm PID: ${KANIDMD_PID}"
|
||||||
|
|
||||||
|
|
||||||
if [ "$(jobs -p | wc -l)" -eq 0 ]; then
|
if [ "$(jobs -p | wc -l)" -eq 0 ]; then
|
||||||
echo "Kanidmd failed to start!"
|
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"
|
KANIDM_CA_PATH="/tmp/kanidm/ca.pem"
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
echo "Waiting the server to start... testing ${KANIDM_URL}"
|
echo "Waiting for the server to start... testing ${KANIDM_URL}"
|
||||||
curl --cacert "${KANIDM_CA_PATH}" -fs "${KANIDM_URL}" >/dev/null && break
|
curl --cacert "${KANIDM_CA_PATH}" -fs "${KANIDM_URL}/status" >/dev/null && break
|
||||||
sleep 2
|
sleep 2
|
||||||
ATTEMPT="$((ATTEMPT + 1))"
|
ATTEMPT="$((ATTEMPT + 1))"
|
||||||
if [ "${ATTEMPT}" -gt 3 ]; then
|
if [ "${ATTEMPT}" -gt 3 ]; then
|
||||||
|
|
|
@ -181,6 +181,7 @@ pub async fn create_https_server(
|
||||||
qe_w_ref: &'static QueryServerWriteV1,
|
qe_w_ref: &'static QueryServerWriteV1,
|
||||||
qe_r_ref: &'static QueryServerReadV1,
|
qe_r_ref: &'static QueryServerReadV1,
|
||||||
mut rx: broadcast::Receiver<CoreAction>,
|
mut rx: broadcast::Receiver<CoreAction>,
|
||||||
|
server_message_tx: broadcast::Sender<CoreAction>,
|
||||||
) -> Result<tokio::task::JoinHandle<()>, ()> {
|
) -> Result<tokio::task::JoinHandle<()>, ()> {
|
||||||
let js_files = get_js_files(config.role)?;
|
let js_files = get_js_files(config.role)?;
|
||||||
// set up the CSP headers
|
// set up the CSP headers
|
||||||
|
@ -354,10 +355,26 @@ pub async fn create_https_server(
|
||||||
tokio::spawn(axum_server::bind(addr).serve(app))
|
tokio::spawn(axum_server::bind(addr).serve(app))
|
||||||
}
|
}
|
||||||
} => {
|
} => {
|
||||||
if let Err(err) = res {
|
match res {
|
||||||
error!("Web server exited with {:?}", err);
|
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")]
|
#[cfg(feature = "otel")]
|
||||||
opentelemetry::global::shutdown_tracer_provider();
|
opentelemetry::global::shutdown_tracer_provider();
|
||||||
|
@ -399,10 +416,7 @@ async fn server_loop(
|
||||||
|
|
||||||
// If configured, setup TLS client authentication.
|
// If configured, setup TLS client authentication.
|
||||||
if let Some(client_ca) = tls_param.client_ca.as_ref() {
|
if let Some(client_ca) = tls_param.client_ca.as_ref() {
|
||||||
debug!(
|
info!("Loading client certificates from {}", client_ca.display());
|
||||||
"Configuring client certificates from {}",
|
|
||||||
client_ca.display()
|
|
||||||
);
|
|
||||||
|
|
||||||
let verify = SslVerifyMode::PEER;
|
let verify = SslVerifyMode::PEER;
|
||||||
// In future we may add a "require mTLS option" which would necesitate this.
|
// 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| {
|
let read_dir = fs::read_dir(client_ca).map_err(|err| {
|
||||||
std::io::Error::new(
|
std::io::Error::new(
|
||||||
ErrorKind::Other,
|
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| {
|
cert_store.add_cert(cert.clone()).map_err(|err| {
|
||||||
std::io::Error::new(
|
std::io::Error::new(
|
||||||
ErrorKind::Other,
|
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
|
// This tells the client what CA's they should use. It DOES NOT
|
||||||
|
|
|
@ -748,7 +748,7 @@ impl Display for TaskName {
|
||||||
|
|
||||||
pub struct CoreHandle {
|
pub struct CoreHandle {
|
||||||
clean_shutdown: bool,
|
clean_shutdown: bool,
|
||||||
tx: broadcast::Sender<CoreAction>,
|
pub tx: broadcast::Sender<CoreAction>,
|
||||||
/// This stores a name for the handle, and the handle itself so we can tell which failed/succeeded at the end.
|
/// 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<()>)>,
|
handles: Vec<(TaskName, tokio::task::JoinHandle<()>)>,
|
||||||
}
|
}
|
||||||
|
@ -1068,6 +1068,7 @@ pub async fn create_server_core(
|
||||||
server_write_ref,
|
server_write_ref,
|
||||||
server_read_ref,
|
server_read_ref,
|
||||||
broadcast_tx.subscribe(),
|
broadcast_tx.subscribe(),
|
||||||
|
broadcast_tx.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
let sctx = create_server_core(config, config_test).await;
|
||||||
if !config_test {
|
if !config_test {
|
||||||
// On linux, notify systemd.
|
// On linux, notify systemd.
|
||||||
|
@ -541,46 +578,54 @@ async fn kanidm_main() -> ExitCode {
|
||||||
loop {
|
loop {
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
{
|
{
|
||||||
|
let mut listener = sctx.tx.subscribe();
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
Ok(()) = tokio::signal::ctrl_c() => {
|
Ok(()) = tokio::signal::ctrl_c() => {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
Some(()) = async move {
|
Some(()) = async move {
|
||||||
let sigterm = tokio::signal::unix::SignalKind::terminate();
|
let sigterm = tokio::signal::unix::SignalKind::terminate();
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
tokio::signal::unix::signal(sigterm).unwrap().recv().await
|
tokio::signal::unix::signal(sigterm).unwrap().recv().await
|
||||||
} => {
|
} => {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
Some(()) = async move {
|
Some(()) = async move {
|
||||||
let sigterm = tokio::signal::unix::SignalKind::alarm();
|
let sigterm = tokio::signal::unix::SignalKind::alarm();
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
tokio::signal::unix::signal(sigterm).unwrap().recv().await
|
tokio::signal::unix::signal(sigterm).unwrap().recv().await
|
||||||
} => {
|
} => {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
Some(()) = async move {
|
Some(()) = async move {
|
||||||
let sigterm = tokio::signal::unix::SignalKind::hangup();
|
let sigterm = tokio::signal::unix::SignalKind::hangup();
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
tokio::signal::unix::signal(sigterm).unwrap().recv().await
|
tokio::signal::unix::signal(sigterm).unwrap().recv().await
|
||||||
} => {
|
} => {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
Some(()) = async move {
|
Some(()) = async move {
|
||||||
let sigterm = tokio::signal::unix::SignalKind::user_defined1();
|
let sigterm = tokio::signal::unix::SignalKind::user_defined1();
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
tokio::signal::unix::signal(sigterm).unwrap().recv().await
|
tokio::signal::unix::signal(sigterm).unwrap().recv().await
|
||||||
} => {
|
} => {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
Some(()) = async move {
|
Some(()) = async move {
|
||||||
let sigterm = tokio::signal::unix::SignalKind::user_defined2();
|
let sigterm = tokio::signal::unix::SignalKind::user_defined2();
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
tokio::signal::unix::signal(sigterm).unwrap().recv().await
|
tokio::signal::unix::signal(sigterm).unwrap().recv().await
|
||||||
} => {
|
} => {
|
||||||
// Ignore
|
// 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")]
|
#[cfg(target_family = "windows")]
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue