mirror of
https://github.com/lldap/lldap.git
synced 2026-03-31 15:07:48 +01:00
server: allow specifying the healthcheck addresses
This change adds two new optional configuration options: - `ldap_healthcheck_host` to pair with `ldap_host` - `http_healthcheck_host` to pair with `http_host` These both default to `localhost` to preserve the existing behavior. Fixes #700
This commit is contained in:
@@ -159,3 +159,16 @@ key_seed = "RanD0m STR1ng"
|
||||
#cert_file="/data/cert.pem"
|
||||
## Certificate key file.
|
||||
#key_file="/data/key.pem"
|
||||
|
||||
## Options to configure the healthcheck command.
|
||||
## To set these options from environment variables, use the following format
|
||||
## (example with http_host): LLDAP_HEALTHCHECK_OPTIONS__HTTP_HOST
|
||||
[healthcheck_options]
|
||||
## The host address that the healthcheck should verify for the HTTP server.
|
||||
## If "http_host" is set to a specific IP address, this must be set to match if the built-in
|
||||
## healthcheck command is used. Note: if this is an IPv6 address, it must be wrapped in [].
|
||||
#http_host = "localhost"
|
||||
## The host address that the healthcheck should verify for the LDAP server.
|
||||
## If "ldap_host" is set to a specific IP address, this must be set to match if the built-in
|
||||
## healthcheck command is used.
|
||||
#ldap_host = "localhost"
|
||||
|
||||
@@ -174,6 +174,9 @@ pub struct RunOpts {
|
||||
|
||||
#[clap(flatten)]
|
||||
pub ldaps_opts: LdapsOpts,
|
||||
|
||||
#[clap(flatten)]
|
||||
pub healthcheck_opts: HealthcheckOpts,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser, Clone)]
|
||||
@@ -264,6 +267,18 @@ pub struct ExportGraphQLSchemaOpts {
|
||||
pub output_file: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser, Clone)]
|
||||
#[clap(next_help_heading = Some("HEALTHCHECK"))]
|
||||
pub struct HealthcheckOpts {
|
||||
/// Change the HTTP Host to test the health of. Default: "localhost"
|
||||
#[clap(long, env = "LLDAP_HEALTHCHECK_OPTIONS__HTTP_HOST")]
|
||||
pub healthcheck_http_host: Option<String>,
|
||||
|
||||
/// Change the LDAP Host to test the health of. Default: "localhost"
|
||||
#[clap(long, env = "LLDAP_HEALTHCHECK_OPTIONS__LDAP_HOST")]
|
||||
pub healthcheck_ldap_host: Option<String>,
|
||||
}
|
||||
|
||||
pub fn init() -> CLIOpts {
|
||||
CLIOpts::parse()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
cli::{
|
||||
GeneralConfigOpts, LdapsOpts, RunOpts, SmtpEncryption, SmtpOpts, TestEmailOpts,
|
||||
TrueFalseAlways,
|
||||
GeneralConfigOpts, HealthcheckOpts, LdapsOpts, RunOpts, SmtpEncryption, SmtpOpts,
|
||||
TestEmailOpts, TrueFalseAlways,
|
||||
},
|
||||
database_string::DatabaseUrl,
|
||||
};
|
||||
@@ -83,6 +83,21 @@ impl std::default::Default for LdapsOptions {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, derive_builder::Builder)]
|
||||
#[builder(pattern = "owned")]
|
||||
pub struct HealthcheckOptions {
|
||||
#[builder(default = r#"String::from("localhost")"#)]
|
||||
pub http_host: String,
|
||||
#[builder(default = r#"String::from("localhost")"#)]
|
||||
pub ldap_host: String,
|
||||
}
|
||||
|
||||
impl std::default::Default for HealthcheckOptions {
|
||||
fn default() -> Self {
|
||||
HealthcheckOptionsBuilder::default().build().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize, derive_more::Debug)]
|
||||
#[debug(r#""{_0}""#)]
|
||||
pub struct HttpUrl(pub Url);
|
||||
@@ -138,6 +153,8 @@ pub struct Configuration {
|
||||
#[serde(skip)]
|
||||
#[builder(field(private), default = "None")]
|
||||
server_setup: Option<ServerSetupConfig>,
|
||||
#[builder(default)]
|
||||
pub healthcheck_options: HealthcheckOptions,
|
||||
}
|
||||
|
||||
impl std::default::Default for Configuration {
|
||||
@@ -523,6 +540,18 @@ impl ConfigOverrider for SmtpOpts {
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigOverrider for HealthcheckOpts {
|
||||
fn override_config(&self, config: &mut Configuration) {
|
||||
self.healthcheck_http_host
|
||||
.as_ref()
|
||||
.inspect(|host| config.healthcheck_options.http_host.clone_from(host));
|
||||
|
||||
self.healthcheck_ldap_host
|
||||
.as_ref()
|
||||
.inspect(|host| config.healthcheck_options.ldap_host.clone_from(host));
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_keys(dict: &figment::value::Dict) -> HashSet<String> {
|
||||
use figment::value::{Dict, Value};
|
||||
fn process_value(value: &Dict, keys: &mut HashSet<String>, path: &mut Vec<String>) {
|
||||
|
||||
@@ -70,8 +70,8 @@ where
|
||||
}
|
||||
|
||||
#[instrument(level = "info", err)]
|
||||
pub async fn check_ldap(port: u16) -> Result<()> {
|
||||
check_ldap_endpoint(TcpStream::connect(format!("localhost:{port}")).await?).await
|
||||
pub async fn check_ldap(host: &str, port: u16) -> Result<()> {
|
||||
check_ldap_endpoint(TcpStream::connect((host, port)).await?).await
|
||||
}
|
||||
|
||||
fn get_root_certificates() -> rustls::RootCertStore {
|
||||
@@ -126,21 +126,19 @@ fn get_tls_connector(ldaps_options: &LdapsOptions) -> Result<RustlsTlsConnector>
|
||||
Ok(std::sync::Arc::new(client_config).into())
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "info", err, fields(port = %ldaps_options.port))]
|
||||
pub async fn check_ldaps(ldaps_options: &LdapsOptions) -> Result<()> {
|
||||
#[instrument(skip_all, level = "info", err, fields(host = %host, port = %ldaps_options.port))]
|
||||
pub async fn check_ldaps(host: &str, ldaps_options: &LdapsOptions) -> Result<()> {
|
||||
if !ldaps_options.enabled {
|
||||
info!("LDAPS not enabled");
|
||||
return Ok(());
|
||||
};
|
||||
let tls_connector =
|
||||
get_tls_connector(ldaps_options).context("while preparing the tls connection")?;
|
||||
let url = format!("localhost:{}", ldaps_options.port);
|
||||
check_ldap_endpoint(
|
||||
tls_connector
|
||||
.connect(
|
||||
rustls::ServerName::try_from("localhost")
|
||||
.context("while parsing the server name")?,
|
||||
TcpStream::connect(&url)
|
||||
rustls::ServerName::try_from(host).context("while parsing the server name")?,
|
||||
TcpStream::connect((host, ldaps_options.port))
|
||||
.await
|
||||
.context("while connecting TCP")?,
|
||||
)
|
||||
@@ -151,8 +149,8 @@ pub async fn check_ldaps(ldaps_options: &LdapsOptions) -> Result<()> {
|
||||
}
|
||||
|
||||
#[instrument(level = "info", err)]
|
||||
pub async fn check_api(port: u16) -> Result<()> {
|
||||
reqwest::get(format!("http://localhost:{port}/health"))
|
||||
pub async fn check_api(host: &str, port: u16) -> Result<()> {
|
||||
reqwest::get(format!("http://{host}:{port}/health"))
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
info!("Success");
|
||||
|
||||
+12
-3
@@ -255,9 +255,18 @@ async fn run_healthcheck(opts: RunOpts) -> Result<()> {
|
||||
use tokio::time::timeout;
|
||||
let delay = Duration::from_millis(3000);
|
||||
let (ldap, ldaps, api) = tokio::join!(
|
||||
timeout(delay, healthcheck::check_ldap(config.ldap_port)),
|
||||
timeout(delay, healthcheck::check_ldaps(&config.ldaps_options)),
|
||||
timeout(delay, healthcheck::check_api(config.http_port)),
|
||||
timeout(
|
||||
delay,
|
||||
healthcheck::check_ldap(&config.healthcheck_options.ldap_host, config.ldap_port)
|
||||
),
|
||||
timeout(
|
||||
delay,
|
||||
healthcheck::check_ldaps(&config.healthcheck_options.ldap_host, &config.ldaps_options)
|
||||
),
|
||||
timeout(
|
||||
delay,
|
||||
healthcheck::check_api(&config.healthcheck_options.http_host, config.http_port)
|
||||
),
|
||||
);
|
||||
|
||||
let failure = [ldap, ldaps, api]
|
||||
|
||||
Reference in New Issue
Block a user