TLS Configuration
Extensions that make outbound HTTPS connections (e.g., fetching remote data, calling REST APIs) need a way to inject TLS configuration — client certificates for mTLS, custom CA bundles, or restricted cipher suites.
The tls module
provides the TlsConfigProvider trait so that extensions can supply their TLS
setup through a uniform interface, regardless of which TLS library they use
(rustls, native-tls, etc.).
Design
The trait is type-erased via Arc<dyn Any + Send + Sync> so that quack-rs
does not depend on any specific TLS library. The code that consumes the provider
downcasts the returned Arc to the concrete config type, after checking
config_type_name(). TlsConfigProvider requires Send + Sync.
Implementing a TLS Provider
#![allow(unused)] fn main() { use quack_rs::tls::{TlsConfigProvider, TlsVersion}; use quack_rs::error::ExtensionError; use std::any::Any; use std::sync::Arc; struct MyTlsProvider { // In practice: Arc<rustls::ClientConfig> config: Arc<String>, mtls_enabled: bool, } impl TlsConfigProvider for MyTlsProvider { fn client_config(&self) -> Result<Arc<dyn Any + Send + Sync>, ExtensionError> { Ok(self.config.clone()) } fn provider_name(&self) -> &str { "my-extension-tls" } fn config_type_name(&self) -> &str { "String" } // in practice: "rustls::ClientConfig" fn min_tls_version(&self) -> TlsVersion { TlsVersion::Tls12 // Minimum recommended } fn supports_mtls(&self) -> bool { self.mtls_enabled } fn accepts_invalid_certs(&self) -> bool { false // MUST default to false } } }
Security Requirements
Implementations must:
- Return
falsefromaccepts_invalid_certs()unless explicitly configured otherwise by the user. Certificate validation bypass (CWE-295) should never be the default. - Return
TlsVersion::Tls12or higher frommin_tls_version(). TLS 1.0 and 1.1 are deprecated per RFC 8996. - Emit an
ExtensionWarningviaWarningCollectorwhen certificate validation is disabled or when using a TLS version below 1.2.
Auditing a Provider
audit_tls_provider() checks a provider for two common misconfigurations and
returns one ExtensionWarning per problem found:
- Certificate verification bypass (CWE-295): code
TLS_NO_VERIFY, severityHigh - A deprecated minimum version, TLS 1.0 or 1.1 (CWE-327): code
TLS_DEPRECATED_VERSION, severityMedium
Feed the result into a WarningCollector:
#![allow(unused)] fn main() { use quack_rs::error::ExtensionError; use quack_rs::tls::{audit_tls_provider, TlsConfigProvider, TlsVersion}; use quack_rs::warning::WarningCollector; use std::any::Any; use std::sync::Arc; struct InsecureProvider; impl TlsConfigProvider for InsecureProvider { fn client_config(&self) -> Result<Arc<dyn Any + Send + Sync>, ExtensionError> { Ok(Arc::new(())) } fn provider_name(&self) -> &str { "insecure" } fn config_type_name(&self) -> &str { "()" } fn min_tls_version(&self) -> TlsVersion { TlsVersion::Tls10 } fn supports_mtls(&self) -> bool { false } fn accepts_invalid_certs(&self) -> bool { true } } let collector = WarningCollector::new(); for w in audit_tls_provider(&InsecureProvider) { collector.emit(w); } let codes: Vec<&str> = collector.snapshot().iter().map(|w| w.code).collect(); assert_eq!(codes, ["TLS_NO_VERIFY", "TLS_DEPRECATED_VERSION"]); }
Downcasting Safely
Never use .unwrap() or .expect() when downcasting in FFI callback contexts
(see Pitfall L3).
Handle a failed downcast as an error:
#![allow(unused)] fn main() { use std::any::Any; use std::sync::Arc; use quack_rs::error::ExtensionError; // With rustls this would be `downcast_ref::<rustls::ClientConfig>()`. fn use_config(config: &Arc<dyn Any + Send + Sync>) -> Result<(), ExtensionError> { let text = config .downcast_ref::<String>() .ok_or_else(|| ExtensionError::new("expected a String TLS config"))?; let _ = text; Ok(()) } let config: Arc<dyn Any + Send + Sync> = Arc::new(String::from("pem bundle")); assert!(use_config(&config).is_ok()); let wrong: Arc<dyn Any + Send + Sync> = Arc::new(42_u32); assert!(use_config(&wrong).is_err()); }