mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-21 08:11:24 +02:00
Use native TLS when certificate validation is disabled for legacy server compatibility
When "Validate TLS certificates" is disabled, use the OS native TLS stack (Secure Transport/SChannel/OpenSSL) instead of rustls. This adds support for TLS 1.0+ connections to legacy servers like IBM WebSphere, which rustls cannot handle since it only implements TLS 1.2+. Ref: https://yaak.app/feedback/posts/tls-handshake-eof-when-connecting-to-private-ibm-websphere-endpoint-works-when-s Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -10536,6 +10536,7 @@ dependencies = [
|
|||||||
"hyper-util",
|
"hyper-util",
|
||||||
"log 0.4.29",
|
"log 0.4.29",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
|
"native-tls",
|
||||||
"regex 1.11.1",
|
"regex 1.11.1",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ zstd = "0.13"
|
|||||||
hyper-util = { version = "0.1.17", default-features = false, features = ["client-legacy"] }
|
hyper-util = { version = "0.1.17", default-features = false, features = ["client-legacy"] }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
|
native-tls = "0.2"
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
reqwest = { workspace = true, features = ["rustls-tls-manual-roots-no-provider", "socks", "http2", "stream"] }
|
reqwest = { workspace = true, features = ["rustls-tls-manual-roots-no-provider", "native-tls", "socks", "http2", "stream"] }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|||||||
@@ -6,6 +6,56 @@ use std::sync::Arc;
|
|||||||
use yaak_models::models::DnsOverride;
|
use yaak_models::models::DnsOverride;
|
||||||
use yaak_tls::{ClientCertificateConfig, get_tls_config};
|
use yaak_tls::{ClientCertificateConfig, get_tls_config};
|
||||||
|
|
||||||
|
/// Build a native-tls connector for maximum compatibility when certificate
|
||||||
|
/// validation is disabled. Unlike rustls, native-tls uses the OS TLS stack
|
||||||
|
/// (Secure Transport on macOS, SChannel on Windows, OpenSSL on Linux) which
|
||||||
|
/// supports TLS 1.0+ for legacy servers.
|
||||||
|
fn build_native_tls_connector(
|
||||||
|
client_cert: Option<ClientCertificateConfig>,
|
||||||
|
) -> Result<native_tls::TlsConnector> {
|
||||||
|
let mut builder = native_tls::TlsConnector::builder();
|
||||||
|
builder.danger_accept_invalid_certs(true);
|
||||||
|
builder.danger_accept_invalid_hostnames(true);
|
||||||
|
builder.min_protocol_version(Some(native_tls::Protocol::Tlsv10));
|
||||||
|
|
||||||
|
if let Some(identity) = build_native_tls_identity(client_cert)? {
|
||||||
|
builder.identity(identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(builder.build()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_native_tls_identity(
|
||||||
|
client_cert: Option<ClientCertificateConfig>,
|
||||||
|
) -> Result<Option<native_tls::Identity>> {
|
||||||
|
let config = match client_cert {
|
||||||
|
None => return Ok(None),
|
||||||
|
Some(c) => c,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try PFX/PKCS12 first
|
||||||
|
if let Some(pfx_path) = &config.pfx_file {
|
||||||
|
if !pfx_path.is_empty() {
|
||||||
|
let pfx_data = std::fs::read(pfx_path)?;
|
||||||
|
let password = config.passphrase.as_deref().unwrap_or("");
|
||||||
|
let identity = native_tls::Identity::from_pkcs12(&pfx_data, password)?;
|
||||||
|
return Ok(Some(identity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try CRT + KEY files
|
||||||
|
if let (Some(crt_path), Some(key_path)) = (&config.crt_file, &config.key_file) {
|
||||||
|
if !crt_path.is_empty() && !key_path.is_empty() {
|
||||||
|
let crt_data = std::fs::read(crt_path)?;
|
||||||
|
let key_data = std::fs::read(key_path)?;
|
||||||
|
let identity = native_tls::Identity::from_pkcs8(&crt_data, &key_data)?;
|
||||||
|
return Ok(Some(identity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HttpConnectionProxySettingAuth {
|
pub struct HttpConnectionProxySettingAuth {
|
||||||
pub user: String,
|
pub user: String,
|
||||||
@@ -51,10 +101,17 @@ impl HttpConnectionOptions {
|
|||||||
// This is needed so we can emit DNS timing events for each request
|
// This is needed so we can emit DNS timing events for each request
|
||||||
.pool_max_idle_per_host(0);
|
.pool_max_idle_per_host(0);
|
||||||
|
|
||||||
// Configure TLS with optional client certificate
|
// Configure TLS
|
||||||
let config =
|
if self.validate_certificates {
|
||||||
get_tls_config(self.validate_certificates, true, self.client_certificate.clone())?;
|
// Use rustls with platform certificate verification (TLS 1.2+ only)
|
||||||
client = client.use_preconfigured_tls(config);
|
let config = get_tls_config(true, true, self.client_certificate.clone())?;
|
||||||
|
client = client.use_preconfigured_tls(config);
|
||||||
|
} else {
|
||||||
|
// Use native TLS for maximum compatibility (supports TLS 1.0+)
|
||||||
|
let connector =
|
||||||
|
build_native_tls_connector(self.client_certificate.clone())?;
|
||||||
|
client = client.use_preconfigured_tls(connector);
|
||||||
|
}
|
||||||
|
|
||||||
// Configure DNS resolver - keep a reference to configure per-request
|
// Configure DNS resolver - keep a reference to configure per-request
|
||||||
let resolver = LocalhostResolver::new(self.dns_overrides.clone());
|
let resolver = LocalhostResolver::new(self.dns_overrides.clone());
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ pub enum Error {
|
|||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
TlsError(#[from] yaak_tls::error::Error),
|
TlsError(#[from] yaak_tls::error::Error),
|
||||||
|
|
||||||
|
#[error("Native TLS error: {0}")]
|
||||||
|
NativeTlsError(#[from] native_tls::Error),
|
||||||
|
|
||||||
|
#[error("IO error: {0}")]
|
||||||
|
IoError(#[from] std::io::Error),
|
||||||
|
|
||||||
#[error("Request failed with {0:?}")]
|
#[error("Request failed with {0:?}")]
|
||||||
RequestError(String),
|
RequestError(String),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user