From 9533e760e4d63917888fe73feec6076c89cf56be Mon Sep 17 00:00:00 2001 From: oddlama Date: Wed, 23 Aug 2023 15:19:08 +0200 Subject: [PATCH] chore: externalize kanidm-secret-manipulator --- pkgs/default.nix | 2 +- ...ault.nix => kanidm-secret-manipulator.nix} | 11 +- pkgs/kanidm-secret-manipulator/Cargo.lock | 389 ------------------ pkgs/kanidm-secret-manipulator/Cargo.toml | 16 - pkgs/kanidm-secret-manipulator/src/main.rs | 186 --------- 5 files changed, 10 insertions(+), 594 deletions(-) rename pkgs/{kanidm-secret-manipulator/default.nix => kanidm-secret-manipulator.nix} (66%) delete mode 100644 pkgs/kanidm-secret-manipulator/Cargo.lock delete mode 100644 pkgs/kanidm-secret-manipulator/Cargo.toml delete mode 100644 pkgs/kanidm-secret-manipulator/src/main.rs diff --git a/pkgs/default.nix b/pkgs/default.nix index c928a3e..354ee9f 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -2,6 +2,6 @@ (import ./caddy.nix) (import ./oauth2-proxy) (self: super: { - kanidm-secret-manipulator = self.callPackage ./kanidm-secret-manipulator {}; + kanidm-secret-manipulator = self.callPackage ./kanidm-secret-manipulator.nix {}; }) ] diff --git a/pkgs/kanidm-secret-manipulator/default.nix b/pkgs/kanidm-secret-manipulator.nix similarity index 66% rename from pkgs/kanidm-secret-manipulator/default.nix rename to pkgs/kanidm-secret-manipulator.nix index 621ea0e..5b1a328 100644 --- a/pkgs/kanidm-secret-manipulator/default.nix +++ b/pkgs/kanidm-secret-manipulator.nix @@ -8,8 +8,15 @@ rustPlatform.buildRustPackage rec { pname = "kanidm-secret-manipulator"; version = "1.0.0"; - src = ./.; - cargoHash = "sha256-EAPlI5wZ6ZByafWnCJ199SShtOppErjKyrNHAQIqr/Y="; + + src = fetchFromGitHub { + owner = "oddlama"; + repo = "kanidm-secret-manipulator"; + rev = "v${version}"; + hash = "sha256-mLOTnOsbUozYRDBXVYtIrUE5jDXEqW8HMO57qWsp1Go="; + }; + + cargoHash = "sha256-L//ZtfbOxV6Hf5x5tLAQ52MChSclzJlhI7sZKqvByMo="; nativeBuildInputs = [pkg-config]; buildInputs = [sqlite]; diff --git a/pkgs/kanidm-secret-manipulator/Cargo.lock b/pkgs/kanidm-secret-manipulator/Cargo.lock deleted file mode 100644 index 1a0fbb7..0000000 --- a/pkgs/kanidm-secret-manipulator/Cargo.lock +++ /dev/null @@ -1,389 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "argon2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e554a8638bdc1e4eae9984845306cc95f8a9208ba8d49c3859fd958b46774d" -dependencies = [ - "base64ct", - "blake2", - "cpufeatures", - "password-hash", -] - -[[package]] -name = "base64" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "base64urlsafedata" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18b3d30abb74120a9d5267463b9e0045fdccc4dd152e7249d966612dc1721384" -dependencies = [ - "base64", - "serde", - "serde_json", -] - -[[package]] -name = "bitflags" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "hashlink" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" -dependencies = [ - "hashbrown", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "kanidm-secret-manipulator" -version = "1.0.0" -dependencies = [ - "anyhow", - "argon2", - "base64urlsafedata", - "rand", - "rusqlite", - "serde", - "serde_json", -] - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "libsqlite3-sys" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" -dependencies = [ - "pkg-config", - "vcpkg", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "password-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rusqlite" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" -dependencies = [ - "bitflags", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "serde" -version = "1.0.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "syn" -version = "2.0.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/pkgs/kanidm-secret-manipulator/Cargo.toml b/pkgs/kanidm-secret-manipulator/Cargo.toml deleted file mode 100644 index d4b8c9e..0000000 --- a/pkgs/kanidm-secret-manipulator/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "kanidm-secret-manipulator" -version = "1.0.0" -edition = "2021" -authors = ["oddlama "] -description = "A helper utility that modifies the kanidm database to allow provisioning declarative secrets with NixOS" -license = "MIT" - -[dependencies] -anyhow = "1.0.75" -argon2 = "0.5.1" -base64urlsafedata = "0.1.3" -rand = "0.8.5" -rusqlite = "0.29.0" -serde = { version = "1.0.183", features = ["derive"] } -serde_json = "1.0.105" diff --git a/pkgs/kanidm-secret-manipulator/src/main.rs b/pkgs/kanidm-secret-manipulator/src/main.rs deleted file mode 100644 index 8604f3e..0000000 --- a/pkgs/kanidm-secret-manipulator/src/main.rs +++ /dev/null @@ -1,186 +0,0 @@ -use anyhow::{anyhow, Context, Result}; -use argon2::{Algorithm, Argon2}; -use base64urlsafedata::Base64UrlSafeData; -use rand::Rng; -use rusqlite::{params, Connection}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::fs::File; - -#[derive(Debug, Deserialize, Serialize)] -struct Argon2IDHash { - m: u32, - t: u32, - p: u32, - v: u32, - s: Base64UrlSafeData, - k: Base64UrlSafeData, -} - -#[derive(Debug, Deserialize)] -struct SecretMappings { - account_credentials: HashMap, - oauth2_basic_secrets: HashMap, -} - -const ACCOUNT_NAME_PATH: &str = "/ent/V2/attrs/name/N8/0"; -const ACCOUNT_PRIMARY_CREDENTIAL_PATH: &str = - "/ent/V2/attrs/primary_credential/CR/0/d/password/ARGON2ID"; - -const OAUTH2_BASIC_SECRET_PATH: &str = "/ent/V2/attrs/oauth2_rs_basic_secret/RU"; -const OAUTH2_NAME_PATH: &str = "/ent/V2/attrs/oauth2_rs_name/N8/0"; - -fn main() -> Result<()> { - // Read command-line arguments - let args: Vec = std::env::args().collect(); - if args.len() != 3 { - eprintln!("Usage: kanidm-secret-manipulator "); - std::process::exit(1); - } - - // Open JSON mappings file - let mappings_file = File::open(&args[2]).context("Failed to open mappings file")?; - let mut secret_mappings: SecretMappings = - serde_json::from_reader(mappings_file).context("Failed to parse mappings file")?; - - // Open SQLite database - let db_path = &args[1]; - let conn = Connection::open(db_path) - .with_context(|| format!("Failed to open database: {}", db_path))?; - - // Prepare statement to update rows - let mut update_stmt = conn - .prepare("UPDATE id2entry SET data = ? WHERE id = ?") - .context("Failed to prepare update statement")?; - - // Iterate over rows and update secrets - let mut stmt = conn - .prepare("SELECT id, data FROM id2entry") - .context("Failed to prepare SELECT statement")?; - - let rows = stmt - .query_map([], |row| { - let id: i64 = row.get(0)?; - let data: Vec = row.get(1)?; - - Ok((id, data)) - }) - .context("Failed to execute SELECT statement")?; - - for row in rows { - let (id, data) = row?; - let mut json_data: serde_json::Value = serde_json::from_slice(&data)?; - - let mut any_changes = false; - any_changes |= - rewrite_account_credentials(&mut secret_mappings, &mut json_data).unwrap_or(false); - any_changes |= rewrite_oauth2_secret(&mut secret_mappings, &mut json_data).unwrap_or(false); - - // Update the row in the database if necessary - if any_changes { - update_stmt - .execute(params![serde_json::to_vec(&json_data)?, id]) - .context("Failed to update oauth2 service '{oauth2_rs_name}' in database")?; - } - } - - // Print missing items - if !secret_mappings.oauth2_basic_secrets.is_empty() { - eprintln!( - "oauth2: Skipped update of missing services: {}", - secret_mappings - .oauth2_basic_secrets - .keys() - .cloned() - .collect::>() - .join(", ") - ); - } - - Ok(()) -} - -fn hash_argon2_id(password: &str, salt: Option>) -> Result { - let salt_len: usize = 16; - let key_len: usize = 32; - let version: argon2::Version = argon2::Version::V0x13; - let m_cost: u32 = 8192; - let t_cost: u32 = 2; - let p_cost: u32 = 1; - - let params = argon2::Params::new(m_cost, t_cost, p_cost, None).unwrap_or_default(); - let argon = Argon2::new(Algorithm::Argon2id, version, params); - - let mut rng = rand::thread_rng(); - let salt: Vec = salt.unwrap_or_else(|| (0..salt_len).map(|_| rng.gen()).collect()); - let mut key: Vec = (0..key_len).map(|_| 0).collect(); - argon - .hash_password_into(password.as_bytes(), salt.as_slice(), key.as_mut_slice()) - .map_err(|_| anyhow!("Failed to create argon2id hash of password"))?; - - Ok(Argon2IDHash { - m: m_cost, - t: t_cost, - p: p_cost, - v: version as u32, - s: salt.into(), - k: key.into(), - }) -} - -fn rewrite_account_credentials( - secret_mappings: &mut SecretMappings, - json_data: &mut serde_json::Value, -) -> Result { - if let Some(name) = json_data - .pointer(ACCOUNT_NAME_PATH) - .and_then(|value| value.as_str()) - .map(|value| value.to_string()) - { - if let Some(secret_path) = secret_mappings.account_credentials.remove(&name) { - let secret = std::fs::read_to_string(&secret_path).context(format!( - "account: Could not update credential for {name} with {secret_path}", - ))?; - if let Some(value) = json_data.pointer_mut(ACCOUNT_PRIMARY_CREDENTIAL_PATH) { - let secret = secret.trim().to_string(); - let current_hash: Argon2IDHash = serde_json::from_value(value.clone()) - .map_err(|e| anyhow!("Failed to load current argon2id hash: {e}"))?; - let test_hash = hash_argon2_id(&secret, Some(current_hash.s.into()))?; - - if current_hash.k != test_hash.k { - let new_hash = hash_argon2_id(&secret, None)?; - *value = serde_json::to_value(new_hash)?; - println!("account: Updated credential for {name}"); - return Ok(true); - } - } - } - } - Ok(false) -} - -fn rewrite_oauth2_secret( - secret_mappings: &mut SecretMappings, - json_data: &mut serde_json::Value, -) -> Result { - if let Some(name) = json_data - .pointer(OAUTH2_NAME_PATH) - .and_then(|value| value.as_str()) - .map(|value| value.to_string()) - { - if let Some(secret_path) = secret_mappings.oauth2_basic_secrets.remove(&name) { - let secret = std::fs::read_to_string(&secret_path).context(format!( - "oauth2: Could not update basic secret for {name} with {secret_path}" - ))?; - if let Some(value) = json_data.pointer_mut(OAUTH2_BASIC_SECRET_PATH) { - let secret = secret.trim().to_string(); - if *value != secret { - *value = serde_json::Value::String(secret); - println!("oauth2: Updated basic secret for {name}"); - return Ok(true); - } - } - } - } - Ok(false) -}