difftreelog
feat postgres secret generator
10 files changed
Cargo.lockdiffbeforeafterboth1045 "ed25519-dalek",1045 "ed25519-dalek",1046 "fleet-shared",1046 "fleet-shared",1047 "hex",1047 "hex",1048 "hmac",1049 "pbkdf2",1048 "rand 0.10.1",1050 "rand 0.10.1",1051 "sha2",1049 "x25519-dalek",1052 "x25519-dalek",1050]1053]10511054Cargo.tomldiffbeforeafterboth31futures = "0.3.31"31futures = "0.3.31"32futures-util = { version = "0.3.31", features = ["sink"] }32futures-util = { version = "0.3.31", features = ["sink"] }33hex = "0.4.3"33hex = "0.4.3"34hmac = "0.12"34hostname = "0.4.1"35hostname = "0.4.1"35human-repr = "1.1"36human-repr = "1.1"36hyper = "1.8.1"37hyper = "1.8.1"40linked-hash-map = "0.5.6"41linked-hash-map = "0.5.6"41nix = { version = "0.31.2", features = ["fs", "user"] }42nix = { version = "0.31.2", features = ["fs", "user"] }42nom = "8.0.0"43nom = "8.0.0"44openssh = "0.11.5"43opentelemetry = "0.31.0"45opentelemetry = "0.31.0"46opentelemetry-appender-tracing = "0.31.1"44opentelemetry-otlp = { version = "0.31.0", features = ["grpc-tonic", "gzip-tonic", "http-json", "reqwest-rustls"] }47opentelemetry-otlp = { version = "0.31.0", features = ["grpc-tonic", "gzip-tonic", "http-json", "reqwest-rustls"] }45opentelemetry_sdk = "0.31.0"48opentelemetry_sdk = "0.31.0"46opentelemetry-appender-tracing = "0.31.1"47openssh = "0.11.5"49pbkdf2 = "0.12"48peg = "0.8.5"50peg = "0.8.5"49pkg-config = "0.3.30"51pkg-config = "0.3.30"50rand = "0.10.0"52rand = "0.10.0"51regex = "1.11"53regex = "1.11"52serde = { version = "1.0", features = ["derive"] }54serde = { version = "1.0", features = ["derive"] }53serde-transcode = "1.1.1"55serde-transcode = "1.1.1"54serde_json = "1.0"56serde_json = "1.0"57sha2 = "0.10"55shlex = "1.3"58shlex = "1.3"56tabled = "0.20.0"59tabled = "0.20.0"57tempfile = "3.20"60tempfile = "3.20"cmds/fleet/Cargo.tomldiffbeforeafterboth44indicatif = { workspace = true, optional = true }44indicatif = { workspace = true, optional = true }45nom.workspace = true45nom.workspace = true46opentelemetry.workspace = true46opentelemetry.workspace = true47opentelemetry-appender-tracing.workspace = true48opentelemetry-exporter-env.workspace = true47opentelemetry_sdk.workspace = true49opentelemetry_sdk.workspace = true48thiserror.workspace = true50thiserror.workspace = true49tracing-indicatif = { workspace = true, optional = true }51tracing-indicatif = { workspace = true, optional = true }50tracing-opentelemetry.workspace = true52tracing-opentelemetry.workspace = true51opentelemetry-exporter-env.workspace = true52opentelemetry-appender-tracing.workspace = true535354[features]54[features]55default = ["indicatif"]55default = ["indicatif"]cmds/generator-helper/Cargo.tomldiffbeforeafterboth13base64.workspace = true13base64.workspace = true14ed25519-dalek.workspace = true14ed25519-dalek.workspace = true15hex.workspace = true15hex.workspace = true16hmac.workspace = true17pbkdf2.workspace = true16rand.workspace = true18rand.workspace = true19sha2.workspace = true17x25519-dalek.workspace = true20x25519-dalek.workspace = true1821cmds/generator-helper/src/main.rsdiffbeforeafterboth10 ssh::{ParseRecipientKeyError, Recipient as SshRecipient},10 ssh::{ParseRecipientKeyError, Recipient as SshRecipient},11};11};12use anyhow::{Context, Result, anyhow, bail, ensure};12use anyhow::{Context, Result, anyhow, bail, ensure};13use base64::{Engine as _, engine::general_purpose::STANDARD, write::EncoderWriter};13use clap::{Parser, ValueEnum};14use clap::{Parser, ValueEnum};14use ed25519_dalek::SecretKey;15use ed25519_dalek::SecretKey;15use fleet_shared::SecretData;16use fleet_shared::SecretData;17use hmac::Mac as _;16use rand::{18use rand::{17 Rng as _,19 Rng as _,18 distr::{Alphanumeric, Distribution, SampleString, Uniform},20 distr::{Alphanumeric, Distribution, SampleString, Uniform},19 rng,21 rng,20};22};23use sha2::Digest as _;2425fn gen_password(rng: &mut impl rand::Rng, size: usize, no_symbols: bool) -> String {26 if no_symbols {27 Alphanumeric.sample_string(rng, size)28 } else {29 const GEN_ASCII_SYMBOLS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";30 let uniform = Uniform::new(0, GEN_ASCII_SYMBOLS.len()).expect("range is valid");31 (0..size)32 .map(|_| uniform.sample(rng))33 .map(|i| GEN_ASCII_SYMBOLS[i] as char)34 .collect::<String>()35 }36}213722fn write_output_file(out: &str) -> Result<File> {38fn write_output_file(out: &str) -> Result<File> {23 let file = OpenOptions::new()39 let file = OpenOptions::new()105 match encoding {121 match encoding {106 OutputEncoding::Raw => coerce(w),122 OutputEncoding::Raw => coerce(w),107 OutputEncoding::Base64 => {123 OutputEncoding::Base64 => {108 use base64::{engine::general_purpose::STANDARD, write::EncoderWriter};109110 let writer = EncoderWriter::new(w, &STANDARD);124 let writer = EncoderWriter::new(w, &STANDARD);111 coerce(writer)125 coerce(writer)177 #[arg(long, short = 'e', value_enum, default_value_t)]191 #[arg(long, short = 'e', value_enum, default_value_t)]178 encoding: OutputEncoding,192 encoding: OutputEncoding,179 },193 },194 PostgresPassword {195 #[arg(long, short = 's')]196 secret: String,197 #[arg(long, short = 'H')]198 hash: String,199 #[arg(long, default_value_t = 24)]200 size: usize,201 #[arg(long, default_value_t = 4096)]202 iterations: u32,203 #[arg(long, short = 'n')]204 no_symbols: bool,205 },180 Bytes {206 Bytes {181 #[arg(long, short = 'o')]207 #[arg(long, short = 'o')]182 output: String,208 output: String,280 no_symbols,306 no_symbols,281 output,307 output,282 encoding,308 encoding,283 } => {309 } => {310 ensure!(311 size >= 6,312 "misconfiguration? password is shorter than 6 chars"313 );314 let recipients = load_identities()?;315 let out = gen_password(&mut rng, size, no_symbols);316 write_private(&recipients, &output, out.as_bytes(), encoding)?;317 }318 Generate::PostgresPassword {319 secret,320 hash,321 size,322 iterations,323 no_symbols,324 } => {284 ensure!(325 ensure!(285 size >= 6,326 size >= 6,286 "misconfiguration? password is shorter than 6 chars"327 "misconfiguration? password is shorter than 6 chars"287 );328 );288 let recipients = load_identities()?;329 let recipients = load_identities()?;289 let out = if no_symbols {330 let password = gen_password(&mut rng, size, no_symbols);290 Alphanumeric.sample_string(&mut rng, size)331291 } else {332 let mut salt = [0u8; 16];292 // Alphabet of Alphanumberic + symbols333 rng.fill_bytes(&mut salt);293 const GEN_ASCII_SYMBOLS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";334 let salted = pbkdf2::pbkdf2_hmac_array::<sha2::Sha256, 32>(335 password.as_bytes(),336 &salt,337 iterations,338 );339340 type HmacSha256 = hmac::Hmac<sha2::Sha256>;294 let uniform =341 let mut mac = <HmacSha256 as hmac::Mac>::new_from_slice(&salted)295 Uniform::new(0, GEN_ASCII_SYMBOLS.len()).expect("range is valid");342 .expect("HMAC accepts any key length");343 mac.update(b"Client Key");344 let client_key = mac.finalize().into_bytes();345296 (0..size)346 let mut hasher = sha2::Sha256::new();297 .map(|_| uniform.sample(&mut rng))347 hasher.update(client_key);298 .map(|i| GEN_ASCII_SYMBOLS[i] as char)348 let stored_key = hasher.finalize();349350 let mut mac = <HmacSha256 as hmac::Mac>::new_from_slice(&salted)351 .expect("HMAC accepts any key length");352 mac.update(b"Server Key");299 .collect::<String>()353 let server_key = mac.finalize().into_bytes();300 };354355 let hash_str = format!(356 "SCRAM-SHA-256${}:{}${}:{}",357 iterations,358 STANDARD.encode(salt),359 STANDARD.encode(stored_key),360 STANDARD.encode(server_key),361 );362301 write_private(&recipients, &output, out.as_bytes(), encoding)?;363 write_private(364 &recipients,365 &secret,366 password.as_bytes(),367 OutputEncoding::Raw,368 )?;369 write_public(&hash, hash_str.as_bytes(), OutputEncoding::Raw)?;302 }370 }303 Generate::Bytes {371 Generate::Bytes {304 output,372 output,crates/nix-eval/src/lib.rsdiffbeforeafterbothno syntactic changes
crates/nix-eval/src/logging.rsdiffbeforeafterbothno syntactic changes
crates/opentelemetry-exporter-env/src/lib.rsdiffbeforeafterbothno syntactic changes
crates/opentelemetry-exporter-env/src/otlp.rsdiffbeforeafterboth55 }55 }56 builder.build()56 builder.build()57 }57 }58 OtlpProtocol::HttpProtobuf | OtlpProtocol::HttpJson => {58 OtlpProtocol::HttpProtobuf | OtlpProtocol::HttpJson => <$exporter>::builder()59 <$exporter>::builder()60 .with_http()59 .with_http()61 .with_endpoint(&s.endpoint)60 .with_endpoint(&s.endpoint)62 .with_headers(to_hashmap(s.headers.as_deref()))61 .with_headers(to_hashmap(s.headers.as_deref()))63 .with_protocol(s.protocol.into())62 .with_protocol(s.protocol.into())64 .with_timeout(s.timeout)63 .with_timeout(s.timeout)65 .build()64 .build(),66 }67 }65 }68 }};66 }};69}67}lib/default.nixdiffbeforeafterboth88 }88 }89 );89 );9091 /**92 Generate a random password suitable for PostgreSQL role authentication.9394 Options:95 size: generated password length in ascii characters (bytes).96 iterations: PBKDF2 iteration count (PG default: 4096).97 noSymbols: by default, character set includes various special characters ($ , ! + * : ~), and might98 not be accepted in some contexts, this option switches charset to just [A-Za-z0-9].99100 Output:101 secret: encrypted password.102 hash: SCRAM-SHA-256 hash of the password to be used by postgres.103 */104 # It is not possible to extract it into a scram-sha-256 generic generator because there is no estabilished format105 # for it, and mongodb for example encodes it differently.106 mkPostgresPassword =107 {108 size ? 32,109 iterations ? 4096,110 noSymbols ? false,111 }:112 (113 { mkSecretGenerator }:114 mkSecretGenerator {115 script = ''116 mkdir $out117 gh generate postgres-password \118 -s $out/secret \119 -H $out/hash \120 --size ${toString size} \121 --iterations ${toString iterations} \122 ${optionalString noSymbols "--no-symbols"}123 '';124 parts.secret.encrypted = true;125 parts.hash.encrypted = false;126 }127 );9012891 /**129 /**92 Generate a random ed25519 keypair130 Generate a random ed25519 keypair306343307 inherit (secrets)344 inherit (secrets)308 mkPassword345 mkPassword346 mkPostgresPassword309 mkEd25519347 mkEd25519310 mkX25519348 mkX25519311 mkRsa349 mkRsa