From 5a7652f3a47ab184f3054c617efe8b501db5e289 Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Tue, 22 Apr 2025 00:42:56 +0200 Subject: [PATCH] Make ldap search filter configurable --- Cargo.lock | 34 ++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + manifests/deployment.yaml | 2 ++ src/ldap.rs | 31 +++++++++++++++++++++++++++++-- 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c39ad2..8fc0a6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1684,6 +1684,16 @@ dependencies = [ "url", ] +[[package]] +name = "leon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a865ffec5587961f5afc6d365bccb304f4feaa1928f4fe94c91c9d210d7310" +dependencies = [ + "miette", + "thiserror 2.0.12", +] + [[package]] name = "libc" version = "0.2.172" @@ -1772,6 +1782,29 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "miette" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a955165f87b37fd1862df2a59547ac542c77ef6d17c666f619d1ad22dd89484" +dependencies = [ + "cfg-if", + "miette-derive", + "thiserror 1.0.69", + "unicode-width 0.1.14", +] + +[[package]] +name = "miette-derive" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "mime" version = "0.3.17" @@ -2945,6 +2978,7 @@ dependencies = [ "hyper", "hyper-util", "ldap3", + "leon", "pin-project-lite", "rand 0.8.5", "ratatui", diff --git a/Cargo.toml b/Cargo.toml index a9f6d26..0e457c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ http-body-util = { version = "0.1.3", features = ["full"] } hyper = { version = "1.6.0", features = ["full"] } hyper-util = { version = "0.1.11", features = ["full"] } ldap3 = "0.11.5" +leon = "3.0.2" pin-project-lite = "0.2.16" rand = "0.8.5" ratatui = { version = "0.29.0", features = ["unstable-backend-writer"] } diff --git a/manifests/deployment.yaml b/manifests/deployment.yaml index d6c81d4..6aa7270 100644 --- a/manifests/deployment.yaml +++ b/manifests/deployment.yaml @@ -55,6 +55,8 @@ spec: value: ldap://lldap.lldap.svc.cluster.local:3890 - name: LDAP_BASE value: ou=people,dc=huizinga,dc=dev + - name: LDAP_SEARCH_FILTER + value: (uid={username}) - name: LDAP_BIND_DN valueFrom: secretKeyRef: diff --git a/src/ldap.rs b/src/ldap.rs index 28ab0fc..01dc4ee 100644 --- a/src/ldap.rs +++ b/src/ldap.rs @@ -1,4 +1,5 @@ use ldap3::{LdapConnAsync, SearchEntry}; +use leon::{Template, vals}; use russh::keys::PublicKey; use tokio::select; use tokio::task::JoinHandle; @@ -9,6 +10,7 @@ use tracing::{debug, error}; pub struct Ldap { base: String, ldap: ldap3::Ldap, + search_filter: String, } #[derive(Debug, thiserror::Error)] @@ -21,6 +23,10 @@ pub enum LdapError { MissingEnvironmentVariable(&'static str), #[error("Could not read password file: {0}")] CouldNotReadPasswordFile(#[from] std::io::Error), + #[error("Failed to parse search filter: {0}")] + FailedToParseSearchFilter(#[from] leon::ParseError), + #[error("Failed to render search filter: {0}")] + FailedToRenderSearchFilter(#[from] leon::RenderError), } impl Ldap { @@ -33,6 +39,8 @@ impl Ldap { .map_err(|_| LdapError::MissingEnvironmentVariable("LDAP_BASE"))?; let bind_dn = std::env::var("LDAP_BIND_DN") .map_err(|_| LdapError::MissingEnvironmentVariable("LDAP_BIND_DN"))?; + let search_filter = std::env::var("LDAP_SEARCH_FILTER") + .map_err(|_| LdapError::MissingEnvironmentVariable("LDAP_SEARCH_FILTER"))?; let password = std::env::var("LDAP_PASSWORD_FILE").map_or_else( |_| { @@ -65,20 +73,39 @@ impl Ldap { ldap.simple_bind(&bind_dn, &password).await?.success()?; - Ok((Self { base, ldap }, handle)) + Ok(( + Self { + base, + ldap, + search_filter, + }, + handle, + )) } pub async fn get_ssh_keys( &mut self, user: impl AsRef, ) -> Result, LdapError> { + let search_filter = Template::parse(&self.search_filter)?; + + let search_filter = search_filter.render(&&vals(|key| { + if key == "username" { + Some(user.as_ref().to_string().into()) + } else { + None + } + }))?; + + debug!("search_filter = {search_filter}"); + Ok(self .ldap .search( &self.base, ldap3::Scope::Subtree, // TODO: Make this not hardcoded - &format!("(uid={})", user.as_ref()), + &search_filter, vec!["sshkeys"], ) .await?