mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Adding healthcheck functionality to kanidmd (#1330)
* closes #1220, adds healthcheck functionality to kanidmd * ssl is old and busted, tls is great
This commit is contained in:
parent
421344c347
commit
8255c937e5
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1178,6 +1178,7 @@ dependencies = [
|
||||||
"kanidmd_core",
|
"kanidmd_core",
|
||||||
"kanidmd_lib",
|
"kanidmd_lib",
|
||||||
"profiles",
|
"profiles",
|
||||||
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"sketching",
|
"sketching",
|
||||||
"tikv-jemallocator",
|
"tikv-jemallocator",
|
||||||
|
@ -2394,6 +2395,7 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-openssl",
|
"tokio-openssl",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
|
@ -885,7 +885,7 @@ fn config_security_checks(cfg_path: &Path) -> bool {
|
||||||
let cfg_meta = match metadata(&cfg_path) {
|
let cfg_meta = match metadata(&cfg_path) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Unable to read metadata for {} - {:?}", cfg_path_str, e);
|
error!("Unable to read metadata for '{}' during security checks - {:?}", cfg_path_str, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,6 +36,7 @@ tide-openssl.workspace = true
|
||||||
tokio = { workspace = true, features = ["net", "sync", "io-util", "macros"] }
|
tokio = { workspace = true, features = ["net", "sync", "io-util", "macros"] }
|
||||||
tokio-openssl.workspace = true
|
tokio-openssl.workspace = true
|
||||||
tokio-util = { workspace = true, features = ["codec"] }
|
tokio-util = { workspace = true, features = ["codec"] }
|
||||||
|
toml = {workspace = true}
|
||||||
tracing = { workspace = true, features = ["attributes"] }
|
tracing = { workspace = true, features = ["attributes"] }
|
||||||
uuid = { workspace = true, features = ["serde", "v4" ] }
|
uuid = { workspace = true, features = ["serde", "v4" ] }
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
//! or domain entries that are able to be replicated.
|
//! or domain entries that are able to be replicated.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use kanidm_proto::messages::ConsoleOutputMode;
|
use kanidm_proto::messages::ConsoleOutputMode;
|
||||||
|
@ -40,6 +44,39 @@ pub struct TlsConfiguration {
|
||||||
pub key: String,
|
pub key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ServerConfig {
|
||||||
|
pub bindaddress: Option<String>,
|
||||||
|
pub ldapbindaddress: Option<String>,
|
||||||
|
pub trust_x_forward_for: Option<bool>,
|
||||||
|
// pub threads: Option<usize>,
|
||||||
|
pub db_path: String,
|
||||||
|
pub db_fs_type: Option<String>,
|
||||||
|
pub db_arc_size: Option<usize>,
|
||||||
|
pub tls_chain: Option<String>,
|
||||||
|
pub tls_key: Option<String>,
|
||||||
|
pub online_backup: Option<OnlineBackup>,
|
||||||
|
pub domain: String,
|
||||||
|
pub origin: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub role: ServerRole,
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
||||||
pub enum ServerRole {
|
pub enum ServerRole {
|
||||||
WriteReplica,
|
WriteReplica,
|
||||||
|
@ -182,6 +219,16 @@ impl Configuration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Startup config action, used in kanidmd server etc
|
||||||
|
pub fn update_config_for_server_mode(&mut self, sconfig: &ServerConfig) {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
debug!("update_config_for_server_mode {:?}", sconfig);
|
||||||
|
self.update_tls(&sconfig.tls_chain, &sconfig.tls_key);
|
||||||
|
self.update_bind(&sconfig.bindaddress);
|
||||||
|
self.update_ldapbind(&sconfig.ldapbindaddress);
|
||||||
|
self.update_online_backup(&sconfig.online_backup);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_trust_x_forward_for(&mut self, t: Option<bool>) {
|
pub fn update_trust_x_forward_for(&mut self, t: Option<bool>) {
|
||||||
self.trust_x_forward_for = t.unwrap_or(false);
|
self.trust_x_forward_for = t.unwrap_or(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,10 @@ kanidmd_core.workspace = true
|
||||||
sketching.workspace = true
|
sketching.workspace = true
|
||||||
|
|
||||||
clap = { workspace = true, features = ["env"] }
|
clap = { workspace = true, features = ["env"] }
|
||||||
|
reqwest = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal"] }
|
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal"] }
|
||||||
toml.workspace = true
|
toml = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(target_family = "windows")'.dependencies]
|
[target.'cfg(target_family = "windows")'.dependencies]
|
||||||
whoami.workspace = true
|
whoami.workspace = true
|
||||||
|
|
|
@ -14,15 +14,14 @@
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||||
|
|
||||||
use std::fs::{metadata, File, Metadata};
|
use std::fs::{metadata, Metadata};
|
||||||
use std::io::Read;
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
use kanidmd_core::config::{Configuration, OnlineBackup, ServerRole};
|
use kanidmd_core::config::{Configuration, ServerConfig};
|
||||||
use kanidmd_core::{
|
use kanidmd_core::{
|
||||||
backup_server_core, create_server_core, dbscan_get_id2entry_core, dbscan_list_id2entry_core,
|
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,
|
dbscan_list_index_analysis_core, dbscan_list_index_core, dbscan_list_indexes_core,
|
||||||
|
@ -31,7 +30,6 @@ use kanidmd_core::{
|
||||||
};
|
};
|
||||||
#[cfg(not(target_family = "windows"))]
|
#[cfg(not(target_family = "windows"))]
|
||||||
use kanidmd_lib::utils::file_permissions_readonly;
|
use kanidmd_lib::utils::file_permissions_readonly;
|
||||||
use serde::Deserialize;
|
|
||||||
use sketching::tracing_forest::traits::*;
|
use sketching::tracing_forest::traits::*;
|
||||||
use sketching::tracing_forest::util::*;
|
use sketching::tracing_forest::util::*;
|
||||||
use sketching::tracing_forest::{self};
|
use sketching::tracing_forest::{self};
|
||||||
|
@ -42,38 +40,6 @@ use whoami;
|
||||||
|
|
||||||
include!("./opt.rs");
|
include!("./opt.rs");
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct ServerConfig {
|
|
||||||
pub bindaddress: Option<String>,
|
|
||||||
pub ldapbindaddress: Option<String>,
|
|
||||||
pub trust_x_forward_for: Option<bool>,
|
|
||||||
// pub threads: Option<usize>,
|
|
||||||
pub db_path: String,
|
|
||||||
pub db_fs_type: Option<String>,
|
|
||||||
pub db_arc_size: Option<usize>,
|
|
||||||
pub tls_chain: Option<String>,
|
|
||||||
pub tls_key: Option<String>,
|
|
||||||
pub online_backup: Option<OnlineBackup>,
|
|
||||||
pub domain: String,
|
|
||||||
pub origin: String,
|
|
||||||
#[serde(default)]
|
|
||||||
pub role: ServerRole,
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KanidmdOpt {
|
impl KanidmdOpt {
|
||||||
fn commonopt(&self) -> &CommonOpt {
|
fn commonopt(&self) -> &CommonOpt {
|
||||||
match self {
|
match self {
|
||||||
|
@ -114,6 +80,7 @@ impl KanidmdOpt {
|
||||||
KanidmdOpt::Database {
|
KanidmdOpt::Database {
|
||||||
commands: DbCommands::Vacuum(copt),
|
commands: DbCommands::Vacuum(copt),
|
||||||
} => &copt,
|
} => &copt,
|
||||||
|
KanidmdOpt::HealthCheck(hcopt) => &hcopt.commonopts,
|
||||||
KanidmdOpt::Version(copt) => &copt,
|
KanidmdOpt::Version(copt) => &copt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,7 +91,7 @@ fn read_file_metadata(path: &PathBuf) -> Metadata {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Unable to read metadata for {} - {:?}",
|
"Unable to read metadata for '{}' - {:?}",
|
||||||
path.to_str().unwrap_or("invalid file path"),
|
path.to_str().unwrap_or("invalid file path"),
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
|
@ -290,10 +257,7 @@ async fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
// configuration options that only relate to server mode
|
// configuration options that only relate to server mode
|
||||||
config.update_tls(&sconfig.tls_chain, &sconfig.tls_key);
|
config.update_config_for_server_mode(&sconfig);
|
||||||
config.update_bind(&sconfig.bindaddress);
|
|
||||||
config.update_ldapbind(&sconfig.ldapbindaddress);
|
|
||||||
config.update_online_backup(&sconfig.online_backup);
|
|
||||||
|
|
||||||
if let Some(i_str) = &(sconfig.tls_chain) {
|
if let Some(i_str) = &(sconfig.tls_chain) {
|
||||||
let i_path = PathBuf::from(i_str.as_str());
|
let i_path = PathBuf::from(i_str.as_str());
|
||||||
|
@ -470,6 +434,66 @@ async fn main() {
|
||||||
eprintln!("Running in vacuum mode ...");
|
eprintln!("Running in vacuum mode ...");
|
||||||
vacuum_server_core(&config);
|
vacuum_server_core(&config);
|
||||||
}
|
}
|
||||||
|
KanidmdOpt::HealthCheck(sopt) => {
|
||||||
|
config.update_config_for_server_mode(&sconfig);
|
||||||
|
|
||||||
|
debug!("{sopt:?}");
|
||||||
|
|
||||||
|
let healthcheck_url = format!("https://{}/status", config.address);
|
||||||
|
debug!("Checking {healthcheck_url}");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let client = reqwest::ClientBuilder::new()
|
||||||
|
.danger_accept_invalid_certs(sopt.no_verify_tls)
|
||||||
|
.danger_accept_invalid_hostnames(sopt.no_verify_tls)
|
||||||
|
.https_only(true);
|
||||||
|
// TODO: work out how to pull the CA from the chain
|
||||||
|
// client = match config.tls_config {
|
||||||
|
// Some(tls_config) => {
|
||||||
|
// eprintln!("{:?}", tls_config);
|
||||||
|
// let mut buf = Vec::new();
|
||||||
|
// File::open(tls_config.chain)
|
||||||
|
// .unwrap()
|
||||||
|
// .read_to_end(&mut buf)
|
||||||
|
// .unwrap();
|
||||||
|
// eprintln!("buf: {:?}", buf);
|
||||||
|
// match reqwest::Certificate::from_pem(&buf){
|
||||||
|
// Ok(cert) => client.add_root_certificate(cert),
|
||||||
|
// Err(err) => {
|
||||||
|
// error!("Failed to read TLS chain: {err:?}");
|
||||||
|
// client
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// },
|
||||||
|
// None => client,
|
||||||
|
// };
|
||||||
|
|
||||||
|
let client = client
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
let req = match client.get(&healthcheck_url).send().await {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(error) => {
|
||||||
|
let error_message = {
|
||||||
|
if error.is_timeout() {
|
||||||
|
format!("Timeout connecting to url={healthcheck_url}")
|
||||||
|
} else if error.is_connect() {
|
||||||
|
format!("Connection failed: {}", error.to_string())
|
||||||
|
} else {
|
||||||
|
format!("Failed to complete healthcheck: {:?}", error)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
eprintln!("CRITICAL: {error_message}");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug!("Request: {req:?}");
|
||||||
|
println!("OK")
|
||||||
|
}
|
||||||
KanidmdOpt::Version(_) => {
|
KanidmdOpt::Version(_) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,16 @@ struct DbScanListIndex {
|
||||||
commonopts: CommonOpt,
|
commonopts: CommonOpt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug,Parser)]
|
||||||
|
struct HealthCheckArgs {
|
||||||
|
/// Disable TLS verification
|
||||||
|
#[clap(short, long, action)]
|
||||||
|
no_verify_tls: bool,
|
||||||
|
#[clap(flatten)]
|
||||||
|
commonopts: CommonOpt,
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
struct DbScanGetIndex {
|
struct DbScanGetIndex {
|
||||||
|
@ -149,6 +159,10 @@ enum KanidmdOpt {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
commands: DomainSettingsCmds,
|
commands: DomainSettingsCmds,
|
||||||
},
|
},
|
||||||
|
/// Load the server config and check services are listening
|
||||||
|
#[clap(name = "healthcheck")]
|
||||||
|
HealthCheck(HealthCheckArgs),
|
||||||
|
|
||||||
/// Print the program version and exit
|
/// Print the program version and exit
|
||||||
#[clap(name="version")]
|
#[clap(name="version")]
|
||||||
Version(CommonOpt)
|
Version(CommonOpt)
|
||||||
|
|
Loading…
Reference in a new issue