diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 1a3efc3e11..b8b0ab8625 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -15667,6 +15667,7 @@ dependencies = [ "regex", "reqwest 0.13.1", "reqwest-middleware", + "rsa", "rust_decimal", "serde", "serde_json", diff --git a/backend/windmill-worker/Cargo.toml b/backend/windmill-worker/Cargo.toml index 5ebcb0b370..2e75ac3632 100644 --- a/backend/windmill-worker/Cargo.toml +++ b/backend/windmill-worker/Cargo.toml @@ -13,7 +13,7 @@ default = [] private = ["windmill-worker-volumes/private", "windmill-queue/private", "windmill-common/private", "windmill-dep-map/private", "windmill-runtime-nativets?/private"] mcp = ["windmill-ai/mcp", "dep:windmill-mcp"] prometheus = ["dep:prometheus", "windmill-common/prometheus"] -enterprise = ["windmill-queue/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker-volumes/enterprise", "windmill-runtime-nativets?/enterprise", "dep:pem", "dep:tokio-util", "dep:opentelemetry-proto", "dep:prost", "dep:hudsucker", "dep:rcgen", "dep:hyper-http-proxy", "dep:hyper-tls", "dep:hyper-util"] +enterprise = ["windmill-queue/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker-volumes/enterprise", "windmill-runtime-nativets?/enterprise", "dep:pem", "dep:rsa", "dep:tokio-util", "dep:opentelemetry-proto", "dep:prost", "dep:hudsucker", "dep:rcgen", "dep:hyper-http-proxy", "dep:hyper-tls", "dep:hyper-util"] mssql = ["dep:tiberius"] mssql-kerberos = ["mssql", "tiberius/integrated-auth-gssapi"] # Linux/Unix integrated auth mssql-winauth = ["mssql", "tiberius/winauth"] # Windows integrated auth @@ -112,6 +112,7 @@ jsonwebtoken.workspace = true sha2.workspace = true hmac.workspace = true pem = { workspace = true, optional = true } +rsa = { workspace = true, optional = true } urlencoding.workspace = true nix.workspace = true bytes.workspace = true diff --git a/backend/windmill-worker/src/snowflake_executor.rs b/backend/windmill-worker/src/snowflake_executor.rs index 839609d9da..35051aee98 100644 --- a/backend/windmill-worker/src/snowflake_executor.rs +++ b/backend/windmill-worker/src/snowflake_executor.rs @@ -630,14 +630,50 @@ pub async fn do_snowflake( ) .to_uppercase(); - let public_key = match database.public_key.as_deref() { - Some(key) => pem::parse(key.as_bytes()).map_err(|e| { - Error::ExecutionErr(format!("Failed to parse public key: {}", e.to_string())) - })?, - None => return Err(Error::ExecutionErr("Public key is missing".to_string())), + let public_key_der: Vec = match database + .public_key + .as_deref() + .map(str::trim) + .filter(|s| !s.is_empty()) + { + Some(key) => pem::parse(key.as_bytes()) + .map_err(|e| Error::ExecutionErr(format!("Failed to parse public key: {e}")))? + .into_contents(), + None => { + // Derive the public key from the private key — RSA private keys + // contain the public components (n, e). + use rsa::pkcs8::{DecodePrivateKey, EncodePublicKey}; + let pk_pem = database + .private_key + .as_deref() + .map(str::trim) + .filter(|s| !s.is_empty()) + .ok_or_else(|| { + Error::ExecutionErr( + "Either public_key or private_key must be provided".to_string(), + ) + })?; + let rsa_priv = rsa::RsaPrivateKey::from_pkcs8_pem(pk_pem) + .or_else(|_| { + use rsa::pkcs1::DecodeRsaPrivateKey; + rsa::RsaPrivateKey::from_pkcs1_pem(pk_pem) + }) + .map_err(|e| { + Error::ExecutionErr(format!( + "Failed to parse private key to derive public key: {e}" + )) + })?; + let rsa_pub = rsa::RsaPublicKey::from(&rsa_priv); + rsa_pub + .to_public_key_der() + .map_err(|e| { + Error::ExecutionErr(format!("Failed to encode derived public key: {e}")) + })? + .to_vec() + } }; let mut public_key_hash = Sha256::new(); - public_key_hash.update(public_key.contents()); + public_key_hash.update(&public_key_der); let public_key_fp = engine::general_purpose::STANDARD.encode(public_key_hash.finalize());