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"
|
#cert_file="/data/cert.pem"
|
||||||
## Certificate key file.
|
## Certificate key file.
|
||||||
#key_file="/data/key.pem"
|
#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)]
|
#[clap(flatten)]
|
||||||
pub ldaps_opts: LdapsOpts,
|
pub ldaps_opts: LdapsOpts,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
pub healthcheck_opts: HealthcheckOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser, Clone)]
|
#[derive(Debug, Parser, Clone)]
|
||||||
@@ -264,6 +267,18 @@ pub struct ExportGraphQLSchemaOpts {
|
|||||||
pub output_file: Option<String>,
|
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 {
|
pub fn init() -> CLIOpts {
|
||||||
CLIOpts::parse()
|
CLIOpts::parse()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
cli::{
|
cli::{
|
||||||
GeneralConfigOpts, LdapsOpts, RunOpts, SmtpEncryption, SmtpOpts, TestEmailOpts,
|
GeneralConfigOpts, HealthcheckOpts, LdapsOpts, RunOpts, SmtpEncryption, SmtpOpts,
|
||||||
TrueFalseAlways,
|
TestEmailOpts, TrueFalseAlways,
|
||||||
},
|
},
|
||||||
database_string::DatabaseUrl,
|
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)]
|
#[derive(Clone, Deserialize, Serialize, derive_more::Debug)]
|
||||||
#[debug(r#""{_0}""#)]
|
#[debug(r#""{_0}""#)]
|
||||||
pub struct HttpUrl(pub Url);
|
pub struct HttpUrl(pub Url);
|
||||||
@@ -138,6 +153,8 @@ pub struct Configuration {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
#[builder(field(private), default = "None")]
|
#[builder(field(private), default = "None")]
|
||||||
server_setup: Option<ServerSetupConfig>,
|
server_setup: Option<ServerSetupConfig>,
|
||||||
|
#[builder(default)]
|
||||||
|
pub healthcheck_options: HealthcheckOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for Configuration {
|
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> {
|
fn extract_keys(dict: &figment::value::Dict) -> HashSet<String> {
|
||||||
use figment::value::{Dict, Value};
|
use figment::value::{Dict, Value};
|
||||||
fn process_value(value: &Dict, keys: &mut HashSet<String>, path: &mut Vec<String>) {
|
fn process_value(value: &Dict, keys: &mut HashSet<String>, path: &mut Vec<String>) {
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "info", err)]
|
#[instrument(level = "info", err)]
|
||||||
pub async fn check_ldap(port: u16) -> Result<()> {
|
pub async fn check_ldap(host: &str, port: u16) -> Result<()> {
|
||||||
check_ldap_endpoint(TcpStream::connect(format!("localhost:{port}")).await?).await
|
check_ldap_endpoint(TcpStream::connect((host, port)).await?).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_root_certificates() -> rustls::RootCertStore {
|
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())
|
Ok(std::sync::Arc::new(client_config).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all, level = "info", err, fields(port = %ldaps_options.port))]
|
#[instrument(skip_all, level = "info", err, fields(host = %host, port = %ldaps_options.port))]
|
||||||
pub async fn check_ldaps(ldaps_options: &LdapsOptions) -> Result<()> {
|
pub async fn check_ldaps(host: &str, ldaps_options: &LdapsOptions) -> Result<()> {
|
||||||
if !ldaps_options.enabled {
|
if !ldaps_options.enabled {
|
||||||
info!("LDAPS not enabled");
|
info!("LDAPS not enabled");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let tls_connector =
|
let tls_connector =
|
||||||
get_tls_connector(ldaps_options).context("while preparing the tls connection")?;
|
get_tls_connector(ldaps_options).context("while preparing the tls connection")?;
|
||||||
let url = format!("localhost:{}", ldaps_options.port);
|
|
||||||
check_ldap_endpoint(
|
check_ldap_endpoint(
|
||||||
tls_connector
|
tls_connector
|
||||||
.connect(
|
.connect(
|
||||||
rustls::ServerName::try_from("localhost")
|
rustls::ServerName::try_from(host).context("while parsing the server name")?,
|
||||||
.context("while parsing the server name")?,
|
TcpStream::connect((host, ldaps_options.port))
|
||||||
TcpStream::connect(&url)
|
|
||||||
.await
|
.await
|
||||||
.context("while connecting TCP")?,
|
.context("while connecting TCP")?,
|
||||||
)
|
)
|
||||||
@@ -151,8 +149,8 @@ pub async fn check_ldaps(ldaps_options: &LdapsOptions) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "info", err)]
|
#[instrument(level = "info", err)]
|
||||||
pub async fn check_api(port: u16) -> Result<()> {
|
pub async fn check_api(host: &str, port: u16) -> Result<()> {
|
||||||
reqwest::get(format!("http://localhost:{port}/health"))
|
reqwest::get(format!("http://{host}:{port}/health"))
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status()?;
|
||||||
info!("Success");
|
info!("Success");
|
||||||
|
|||||||
+12
-3
@@ -255,9 +255,18 @@ async fn run_healthcheck(opts: RunOpts) -> Result<()> {
|
|||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
let delay = Duration::from_millis(3000);
|
let delay = Duration::from_millis(3000);
|
||||||
let (ldap, ldaps, api) = tokio::join!(
|
let (ldap, ldaps, api) = tokio::join!(
|
||||||
timeout(delay, healthcheck::check_ldap(config.ldap_port)),
|
timeout(
|
||||||
timeout(delay, healthcheck::check_ldaps(&config.ldaps_options)),
|
delay,
|
||||||
timeout(delay, healthcheck::check_api(config.http_port)),
|
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]
|
let failure = [ldap, ldaps, api]
|
||||||
|
|||||||
Reference in New Issue
Block a user