diff --git a/Cargo.lock b/Cargo.lock index 86eaad7..f34d975 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1465,6 +1465,16 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[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.171" @@ -1496,6 +1506,7 @@ dependencies = [ "insta", "k8s-openapi", "kube", + "leon", "lldap_auth", "passwords", "queries", @@ -1594,6 +1605,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", +] + +[[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 2.0.100", +] + [[package]] name = "mime" version = "0.3.17" @@ -2921,6 +2955,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "unicode-xid" version = "0.2.6" diff --git a/Cargo.toml b/Cargo.toml index 242ad06..8167fff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ reqwest = { version = "0.12.14", default-features = false, features = [ git-version = "0.3.9" color-eyre = "0.6.3" dotenvy = "0.15.7" +leon = "3.0.2" [dev-dependencies] insta = { workspace = true } diff --git a/manifests/deployment.yaml b/manifests/deployment.yaml index 9f71a4a..a51d74b 100644 --- a/manifests/deployment.yaml +++ b/manifests/deployment.yaml @@ -43,3 +43,5 @@ spec: secretKeyRef: name: lldap-credentials key: lldap-ldap-user-pass + - name: BIND_DN_TEMPLATE + value: uid={username},ou=people,dc=huizinga,dc=dev diff --git a/src/context.rs b/src/context.rs index 9aa9c21..27ad941 100644 --- a/src/context.rs +++ b/src/context.rs @@ -10,10 +10,16 @@ pub struct Context { pub lldap_config: LldapConfig, pub controller_name: String, pub recorder: Recorder, + pub bind_dn_template: String, } impl Context { - pub fn new(controller_name: &str, client: kube::Client, lldap_config: LldapConfig) -> Self { + pub fn new( + controller_name: &str, + client: kube::Client, + lldap_config: LldapConfig, + bind_dn_template: impl Into, + ) -> Self { let reporter: Reporter = controller_name.into(); let recorder = Recorder::new(client.clone(), reporter); @@ -22,6 +28,7 @@ impl Context { lldap_config, controller_name: controller_name.into(), recorder, + bind_dn_template: bind_dn_template.into(), } } } diff --git a/src/main.rs b/src/main.rs index 3d9b16c..9217c85 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use std::time::Duration; +use color_eyre::eyre::Context as _; use dotenvy::dotenv; use futures::StreamExt; use k8s_openapi::api::core::v1::Secret; @@ -54,12 +55,16 @@ async fn main() -> color_eyre::Result<()> { info!(version = VERSION, "Starting"); + let bind_dn_template = + std::env::var("BIND_DN_TEMPLATE").wrap_err("BIND_DN_TEMPLATE is not set")?; + let client = KubeClient::try_default().await?; let data = Context::new( "lldap.huizinga.dev", client.clone(), LldapConfig::try_from_env()?, + bind_dn_template, ); let secrets = Api::::all(client.clone()); diff --git a/src/resources/service_user.rs b/src/resources/service_user.rs index 1ee9dc9..ef3ae82 100644 --- a/src/resources/service_user.rs +++ b/src/resources/service_user.rs @@ -9,6 +9,7 @@ use k8s_openapi::apimachinery::pkg::apis::meta::v1::OwnerReference; use kube::api::{ObjectMeta, Patch, PatchParams, PostParams}; use kube::runtime::controller::Action; use kube::{Api, CustomResource, Resource}; +use leon::{Template, vals}; use passwords::PasswordGenerator; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -113,6 +114,35 @@ impl Reconcile for ServiceUser { debug!(name, secret_name, "Generating new secret"); new_secret(&username, oref) + }) + .and_modify(|secret| { + let bind_dn_template = match Template::parse(&ctx.bind_dn_template) { + Ok(template) => template, + Err(err) => { + warn!("Invalid bind_dn template: {err}"); + return; + } + }; + + let bind_dn = match bind_dn_template.render(&&vals(|key| { + if key == "username" { + Some(username.clone().into()) + } else { + warn!("Invalid bind_dn template key: {key}"); + None + } + })) { + Ok(bind_dn) => bind_dn, + Err(err) => { + warn!("Failed to render bind_dn template: {err}"); + return; + } + }; + + secret + .string_data + .get_or_insert_default() + .insert("bind_dn".into(), bind_dn); }); trace!(name, "Committing secret");