mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-24 00:00:46 +00:00
feat(snowflake): derive public key from private key when omitted (WIN-1959) (#9251)
* feat(snowflake): derive public key from private key when omitted (WIN-1959) Snowflake key-pair auth needs a SHA256 fingerprint of the public key for the JWT iss claim, but the public key is mathematically derivable from the RSA private key. Other tools (e.g. Power BI) only require the private key, so requiring users to supply both is redundant. When public_key is missing, fall back to deriving it from private_key (PKCS#8 or PKCS#1 PEM) instead of erroring out. Fixes WIN-1959 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(snowflake): treat empty public_key/private_key as missing --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Generated
+1
@@ -15667,6 +15667,7 @@ dependencies = [
|
||||
"regex",
|
||||
"reqwest 0.13.1",
|
||||
"reqwest-middleware",
|
||||
"rsa",
|
||||
"rust_decimal",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<u8> = 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());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user