diff options
author | Justin Chase <justin.m.chase@gmail.com> | 2021-08-07 07:49:38 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-07 14:49:38 +0200 |
commit | 02c74fb70970fcadb7d1e6dab857eeb2cea20e09 (patch) | |
tree | 03a1490e063bca34be660eee73bccc8342b0bff2 /extensions | |
parent | fddeb4cea2687b32a32f7829f336b7cf5092c714 (diff) |
feat(tls): Optionally support loading native certs (#11491)
This commit adds "DENO_TLS_CA_STORE" env variable to support
optionally loading certificates from the users local certificate store.
This will allow them to successfully connect via tls with corporate
and self signed certs provided they have them installed in their keystore.
It also allows them to deal with revoked certs by simply updating
their keystore without having to upgrade Deno.
Currently supported values are "mozilla", "system" or empty value.
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/fetch/Cargo.toml | 2 | ||||
-rw-r--r-- | extensions/fetch/lib.rs | 78 | ||||
-rw-r--r-- | extensions/net/Cargo.toml | 4 | ||||
-rw-r--r-- | extensions/net/lib.rs | 12 | ||||
-rw-r--r-- | extensions/net/ops_tls.rs | 128 | ||||
-rw-r--r-- | extensions/tls/Cargo.toml | 24 | ||||
-rw-r--r-- | extensions/tls/lib.rs | 129 | ||||
-rw-r--r-- | extensions/websocket/Cargo.toml | 3 | ||||
-rw-r--r-- | extensions/websocket/lib.rs | 31 |
9 files changed, 236 insertions, 175 deletions
diff --git a/extensions/fetch/Cargo.toml b/extensions/fetch/Cargo.toml index 9c9a64222..211071236 100644 --- a/extensions/fetch/Cargo.toml +++ b/extensions/fetch/Cargo.toml @@ -17,8 +17,10 @@ path = "lib.rs" bytes = "1.0.1" data-url = "0.1.0" deno_core = { version = "0.95.0", path = "../../core" } +deno_tls = { version = "0.1.0", path = "../tls" } deno_web = { version = "0.44.0", path = "../web" } http = "0.2.4" +lazy_static = "1.4.0" reqwest = { version = "0.11.4", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli"] } serde = { version = "1.0.126", features = ["derive"] } tokio = { version = "1.8.1", features = ["full"] } diff --git a/extensions/fetch/lib.rs b/extensions/fetch/lib.rs index ad599b87c..0ac853cbc 100644 --- a/extensions/fetch/lib.rs +++ b/extensions/fetch/lib.rs @@ -1,7 +1,7 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use data_url::DataUrl; use deno_core::error::bad_resource_id; -use deno_core::error::generic_error; use deno_core::error::null_opbuf; use deno_core::error::type_error; use deno_core::error::AnyError; @@ -24,16 +24,14 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ZeroCopyBuf; - -use data_url::DataUrl; +use deno_tls::create_http_client; +use deno_tls::rustls::RootCertStore; +use deno_tls::Proxy; use deno_web::BlobStore; use http::header::CONTENT_LENGTH; -use reqwest::header::HeaderMap; use reqwest::header::HeaderName; use reqwest::header::HeaderValue; use reqwest::header::HOST; -use reqwest::header::USER_AGENT; -use reqwest::redirect::Policy; use reqwest::Body; use reqwest::Client; use reqwest::Method; @@ -59,7 +57,7 @@ pub use reqwest; // Re-export reqwest pub fn init<P: FetchPermissions + 'static>( user_agent: String, - ca_data: Option<Vec<u8>>, + root_cert_store: Option<RootCertStore>, proxy: Option<Proxy>, request_builder_hook: Option<fn(RequestBuilder) -> RequestBuilder>, ) -> Extension { @@ -84,12 +82,17 @@ pub fn init<P: FetchPermissions + 'static>( ]) .state(move |state| { state.put::<reqwest::Client>({ - create_http_client(user_agent.clone(), ca_data.clone(), proxy.clone()) - .unwrap() + create_http_client( + user_agent.clone(), + root_cert_store.clone(), + None, + proxy.clone(), + ) + .unwrap() }); state.put::<HttpClientDefaults>(HttpClientDefaults { - ca_data: ca_data.clone(), user_agent: user_agent.clone(), + root_cert_store: root_cert_store.clone(), proxy: proxy.clone(), request_builder_hook, }); @@ -100,7 +103,7 @@ pub fn init<P: FetchPermissions + 'static>( pub struct HttpClientDefaults { pub user_agent: String, - pub ca_data: Option<Vec<u8>>, + pub root_cert_store: Option<RootCertStore>, pub proxy: Option<Proxy>, pub request_builder_hook: Option<fn(RequestBuilder) -> RequestBuilder>, } @@ -501,26 +504,12 @@ impl HttpClientResource { #[serde(rename_all = "camelCase")] #[serde(default)] pub struct CreateHttpClientOptions { + ca_stores: Option<Vec<String>>, ca_file: Option<String>, ca_data: Option<ByteString>, proxy: Option<Proxy>, } -#[derive(Deserialize, Default, Debug, Clone)] -#[serde(rename_all = "camelCase")] -#[serde(default)] -pub struct Proxy { - pub url: String, - pub basic_auth: Option<BasicAuth>, -} - -#[derive(Deserialize, Default, Debug, Clone)] -#[serde(default)] -pub struct BasicAuth { - pub username: String, - pub password: String, -} - pub fn op_create_http_client<FP>( state: &mut OpState, args: CreateHttpClientOptions, @@ -541,12 +530,12 @@ where } let defaults = state.borrow::<HttpClientDefaults>(); - let cert_data = get_cert_data(args.ca_file.as_deref(), args.ca_data.as_deref())?; let client = create_http_client( defaults.user_agent.clone(), - cert_data.or_else(|| defaults.ca_data.clone()), + defaults.root_cert_store.clone(), + cert_data, args.proxy, ) .unwrap(); @@ -569,36 +558,3 @@ fn get_cert_data( Ok(None) } } - -/// Create new instance of async reqwest::Client. This client supports -/// proxies and doesn't follow redirects. -pub fn create_http_client( - user_agent: String, - ca_data: Option<Vec<u8>>, - proxy: Option<Proxy>, -) -> Result<Client, AnyError> { - let mut headers = HeaderMap::new(); - headers.insert(USER_AGENT, user_agent.parse().unwrap()); - let mut builder = Client::builder() - .redirect(Policy::none()) - .default_headers(headers) - .use_rustls_tls(); - - if let Some(ca_data) = ca_data { - let cert = reqwest::Certificate::from_pem(&ca_data)?; - builder = builder.add_root_certificate(cert); - } - - if let Some(proxy) = proxy { - let mut reqwest_proxy = reqwest::Proxy::all(&proxy.url)?; - if let Some(basic_auth) = &proxy.basic_auth { - reqwest_proxy = - reqwest_proxy.basic_auth(&basic_auth.username, &basic_auth.password); - } - builder = builder.proxy(reqwest_proxy); - } - - builder - .build() - .map_err(|e| generic_error(format!("Unable to build http client: {}", e))) -} diff --git a/extensions/net/Cargo.toml b/extensions/net/Cargo.toml index d774acf88..58363fd2e 100644 --- a/extensions/net/Cargo.toml +++ b/extensions/net/Cargo.toml @@ -15,13 +15,11 @@ path = "lib.rs" [dependencies] deno_core = { version = "0.95.0", path = "../../core" } +deno_tls = { version = "0.1.0", path = "../tls" } lazy_static = "1.4.0" log = "0.4.14" -rustls = "0.19.0" serde = { version = "1.0.126", features = ["derive"] } tokio = { version = "1.8.1", features = ["full"] } trust-dns-proto = "0.20.3" trust-dns-resolver = { version = "0.20.3", features = ["tokio-runtime", "serde-config"] } -webpki = "0.21.4" -webpki-roots = "0.21.1" diff --git a/extensions/net/lib.rs b/extensions/net/lib.rs index 11d0b4493..6b0b728b1 100644 --- a/extensions/net/lib.rs +++ b/extensions/net/lib.rs @@ -11,6 +11,7 @@ use deno_core::error::AnyError; use deno_core::include_js_files; use deno_core::Extension; use deno_core::OpState; +use deno_tls::rustls::RootCertStore; use std::cell::RefCell; use std::path::Path; use std::path::PathBuf; @@ -90,20 +91,17 @@ pub fn get_unstable_declaration() -> PathBuf { #[derive(Clone)] pub struct DefaultTlsOptions { - pub ca_data: Option<Vec<u8>>, + pub root_cert_store: Option<RootCertStore>, } pub fn init<P: NetPermissions + 'static>( - ca_data: Option<Vec<u8>>, + root_cert_store: Option<RootCertStore>, unstable: bool, ) -> Extension { let mut ops_to_register = vec![]; ops_to_register.extend(io::init()); ops_to_register.extend(ops::init::<P>()); ops_to_register.extend(ops_tls::init::<P>()); - - let default_tls_options = DefaultTlsOptions { ca_data }; - Extension::builder() .js(include_js_files!( prefix "deno:extensions/net", @@ -113,7 +111,9 @@ pub fn init<P: NetPermissions + 'static>( )) .ops(ops_to_register) .state(move |state| { - state.put(default_tls_options.clone()); + state.put(DefaultTlsOptions { + root_cert_store: root_cert_store.clone(), + }); state.put(UnstableChecker { unstable }); Ok(()) }) diff --git a/extensions/net/ops_tls.rs b/extensions/net/ops_tls.rs index a082f7f62..124da2f03 100644 --- a/extensions/net/ops_tls.rs +++ b/extensions/net/ops_tls.rs @@ -1,8 +1,5 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -pub use rustls; -pub use webpki; - use crate::io::TcpStreamResource; use crate::io::TlsStreamResource; use crate::ops::IpAddr; @@ -38,30 +35,29 @@ use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; +use deno_tls::create_client_config; +use deno_tls::rustls::internal::pemfile::certs; +use deno_tls::rustls::internal::pemfile::pkcs8_private_keys; +use deno_tls::rustls::internal::pemfile::rsa_private_keys; +use deno_tls::rustls::Certificate; +use deno_tls::rustls::ClientConfig; +use deno_tls::rustls::ClientSession; +use deno_tls::rustls::NoClientAuth; +use deno_tls::rustls::PrivateKey; +use deno_tls::rustls::ServerConfig; +use deno_tls::rustls::ServerSession; +use deno_tls::rustls::Session; +use deno_tls::webpki::DNSNameRef; use io::Error; use io::Read; use io::Write; -use rustls::internal::pemfile::certs; -use rustls::internal::pemfile::pkcs8_private_keys; -use rustls::internal::pemfile::rsa_private_keys; -use rustls::Certificate; -use rustls::ClientConfig; -use rustls::ClientSession; -use rustls::NoClientAuth; -use rustls::PrivateKey; -use rustls::ServerConfig; -use rustls::ServerSession; -use rustls::Session; -use rustls::StoresClientSessions; use serde::Deserialize; use std::borrow::Cow; use std::cell::RefCell; -use std::collections::HashMap; use std::convert::From; use std::fs::File; use std::io; use std::io::BufReader; -use std::io::Cursor; use std::io::ErrorKind; use std::ops::Deref; use std::ops::DerefMut; @@ -76,32 +72,6 @@ use tokio::io::ReadBuf; use tokio::net::TcpListener; use tokio::net::TcpStream; use tokio::task::spawn_local; -use webpki::DNSNameRef; - -lazy_static::lazy_static! { - static ref CLIENT_SESSION_MEMORY_CACHE: Arc<ClientSessionMemoryCache> = - Arc::new(ClientSessionMemoryCache::default()); -} - -#[derive(Default)] -struct ClientSessionMemoryCache(Mutex<HashMap<Vec<u8>, Vec<u8>>>); - -impl StoresClientSessions for ClientSessionMemoryCache { - fn get(&self, key: &[u8]) -> Option<Vec<u8>> { - self.0.lock().get(key).cloned() - } - - fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool { - let mut sessions = self.0.lock(); - // TODO(bnoordhuis) Evict sessions LRU-style instead of arbitrarily. - while sessions.len() >= 1024 { - let key = sessions.keys().next().unwrap().clone(); - sessions.remove(&key); - } - sessions.insert(key, value); - true - } -} #[derive(Debug)] enum TlsSession { @@ -703,8 +673,6 @@ where n => n, }; let cert_file = args.cert_file.as_deref(); - - let default_tls_options; { super::check_unstable2(&state, "Deno.startTls"); let mut s = state.borrow_mut(); @@ -713,12 +681,28 @@ where if let Some(path) = cert_file { permissions.check_read(Path::new(path))?; } - default_tls_options = s.borrow::<DefaultTlsOptions>().clone(); } + let ca_data = match cert_file { + Some(path) => { + let mut buf = Vec::new(); + File::open(path)?.read_to_end(&mut buf)?; + Some(buf) + } + _ => None, + }; + let hostname_dns = DNSNameRef::try_from_ascii_str(hostname) .map_err(|_| invalid_hostname(hostname))?; + // TODO(@justinmchase): Ideally the certificate store is created once + // and not cloned. The store should be wrapped in Arc<T> to reduce + // copying memory unnecessarily. + let root_cert_store = state + .borrow() + .borrow::<DefaultTlsOptions>() + .root_cert_store + .clone(); let resource_rc = state .borrow_mut() .resource_table @@ -732,22 +716,7 @@ where let local_addr = tcp_stream.local_addr()?; let remote_addr = tcp_stream.peer_addr()?; - let mut tls_config = ClientConfig::new(); - tls_config.set_persistence(CLIENT_SESSION_MEMORY_CACHE.clone()); - tls_config - .root_store - .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); - if let Some(ca_data) = default_tls_options.ca_data { - let reader = &mut Cursor::new(ca_data); - tls_config.root_store.add_pem_file(reader).unwrap(); - }; - if let Some(path) = cert_file { - let key_file = File::open(path)?; - let reader = &mut BufReader::new(key_file); - tls_config.root_store.add_pem_file(reader).unwrap(); - } - let tls_config = Arc::new(tls_config); - + let tls_config = Arc::new(create_client_config(root_cert_store, ca_data)?); let tls_stream = TlsStream::new_client_side(tcp_stream, &tls_config, hostname_dns); @@ -786,8 +755,6 @@ where }; let port = args.port; let cert_file = args.cert_file.as_deref(); - - let default_tls_options; { let mut s = state.borrow_mut(); let permissions = s.borrow_mut::<NP>(); @@ -795,9 +762,22 @@ where if let Some(path) = cert_file { permissions.check_read(Path::new(path))?; } - default_tls_options = s.borrow::<DefaultTlsOptions>().clone(); } + let ca_data = match cert_file { + Some(path) => { + let mut buf = Vec::new(); + File::open(path)?.read_to_end(&mut buf)?; + Some(buf) + } + _ => None, + }; + + let root_cert_store = state + .borrow() + .borrow::<DefaultTlsOptions>() + .root_cert_store + .clone(); let hostname_dns = DNSNameRef::try_from_ascii_str(hostname) .map_err(|_| invalid_hostname(hostname))?; @@ -808,23 +788,7 @@ where let tcp_stream = TcpStream::connect(connect_addr).await?; let local_addr = tcp_stream.local_addr()?; let remote_addr = tcp_stream.peer_addr()?; - - let mut tls_config = ClientConfig::new(); - tls_config.set_persistence(CLIENT_SESSION_MEMORY_CACHE.clone()); - tls_config - .root_store - .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); - if let Some(ca_data) = default_tls_options.ca_data { - let reader = &mut Cursor::new(ca_data); - tls_config.root_store.add_pem_file(reader).unwrap(); - }; - if let Some(path) = cert_file { - let key_file = File::open(path)?; - let reader = &mut BufReader::new(key_file); - tls_config.root_store.add_pem_file(reader).unwrap(); - } - let tls_config = Arc::new(tls_config); - + let tls_config = Arc::new(create_client_config(root_cert_store, ca_data)?); let tls_stream = TlsStream::new_client_side(tcp_stream, &tls_config, hostname_dns); diff --git a/extensions/tls/Cargo.toml b/extensions/tls/Cargo.toml new file mode 100644 index 000000000..ee7be04dc --- /dev/null +++ b/extensions/tls/Cargo.toml @@ -0,0 +1,24 @@ +# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +[package] +name = "deno_tls" +version = "0.1.0" +authors = ["the Deno authors"] +edition = "2018" +license = "MIT" +readme = "README.md" +repository = "https://github.com/denoland/deno" +description = "TLS for Deno" + +[lib] +path = "lib.rs" + +[dependencies] +deno_core = { version = "0.95.0", path = "../../core" } +lazy_static = "1.4.0" +reqwest = { version = "0.11.4", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli"] } +rustls = "0.19.0" +rustls-native-certs = "0.5.0" +serde = { version = "1.0.126", features = ["derive"] } +webpki = "0.21.4" +webpki-roots = "0.21.1" diff --git a/extensions/tls/lib.rs b/extensions/tls/lib.rs new file mode 100644 index 000000000..f91249792 --- /dev/null +++ b/extensions/tls/lib.rs @@ -0,0 +1,129 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +pub use reqwest; +pub use rustls; +pub use rustls_native_certs; +pub use webpki; +pub use webpki_roots; + +use deno_core::error::anyhow; +use deno_core::error::generic_error; +use deno_core::error::AnyError; +use deno_core::parking_lot::Mutex; +use deno_core::Extension; + +use reqwest::header::HeaderMap; +use reqwest::header::USER_AGENT; +use reqwest::redirect::Policy; +use reqwest::Client; +use rustls::ClientConfig; +use rustls::RootCertStore; +use rustls::StoresClientSessions; +use serde::Deserialize; +use std::collections::HashMap; +use std::io::BufReader; +use std::io::Cursor; +use std::sync::Arc; + +/// This extension has no runtime apis, it only exports some shared native functions. +pub fn init() -> Extension { + Extension::builder().build() +} + +#[derive(Deserialize, Default, Debug, Clone)] +#[serde(rename_all = "camelCase")] +#[serde(default)] +pub struct Proxy { + pub url: String, + pub basic_auth: Option<BasicAuth>, +} + +#[derive(Deserialize, Default, Debug, Clone)] +#[serde(default)] +pub struct BasicAuth { + pub username: String, + pub password: String, +} + +lazy_static::lazy_static! { + static ref CLIENT_SESSION_MEMORY_CACHE: Arc<ClientSessionMemoryCache> = + Arc::new(ClientSessionMemoryCache::default()); +} + +#[derive(Default)] +struct ClientSessionMemoryCache(Mutex<HashMap<Vec<u8>, Vec<u8>>>); + +impl StoresClientSessions for ClientSessionMemoryCache { + fn get(&self, key: &[u8]) -> Option<Vec<u8>> { + self.0.lock().get(key).cloned() + } + + fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool { + let mut sessions = self.0.lock(); + // TODO(bnoordhuis) Evict sessions LRU-style instead of arbitrarily. + while sessions.len() >= 1024 { + let key = sessions.keys().next().unwrap().clone(); + sessions.remove(&key); + } + sessions.insert(key, value); + true + } +} + +pub fn create_default_root_cert_store() -> RootCertStore { + let mut root_cert_store = RootCertStore::empty(); + // TODO(@justinmchase): Consider also loading the system keychain here + root_cert_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); + root_cert_store +} + +pub fn create_client_config( + root_cert_store: Option<RootCertStore>, + ca_data: Option<Vec<u8>>, +) -> Result<ClientConfig, AnyError> { + let mut tls_config = ClientConfig::new(); + tls_config.set_persistence(CLIENT_SESSION_MEMORY_CACHE.clone()); + tls_config.root_store = + root_cert_store.unwrap_or_else(create_default_root_cert_store); + + // If a custom cert is specified, add it to the store + if let Some(cert) = ca_data { + let reader = &mut BufReader::new(Cursor::new(cert)); + // This function does not return specific errors, if it fails give a generic message. + if let Err(_err) = tls_config.root_store.add_pem_file(reader) { + return Err(anyhow!("Unable to add pem file to certificate store")); + } + } + + Ok(tls_config) +} + +/// Create new instance of async reqwest::Client. This client supports +/// proxies and doesn't follow redirects. +pub fn create_http_client( + user_agent: String, + root_cert_store: Option<RootCertStore>, + ca_data: Option<Vec<u8>>, + proxy: Option<Proxy>, +) -> Result<Client, AnyError> { + let tls_config = create_client_config(root_cert_store, ca_data)?; + let mut headers = HeaderMap::new(); + headers.insert(USER_AGENT, user_agent.parse().unwrap()); + let mut builder = Client::builder() + .redirect(Policy::none()) + .default_headers(headers) + .use_preconfigured_tls(tls_config); + + if let Some(proxy) = proxy { + let mut reqwest_proxy = reqwest::Proxy::all(&proxy.url)?; + if let Some(basic_auth) = &proxy.basic_auth { + reqwest_proxy = + reqwest_proxy.basic_auth(&basic_auth.username, &basic_auth.password); + } + builder = builder.proxy(reqwest_proxy); + } + + builder + .build() + .map_err(|e| generic_error(format!("Unable to build http client: {}", e))) +} diff --git a/extensions/websocket/Cargo.toml b/extensions/websocket/Cargo.toml index 3e1705950..1e01c2691 100644 --- a/extensions/websocket/Cargo.toml +++ b/extensions/websocket/Cargo.toml @@ -15,11 +15,10 @@ path = "lib.rs" [dependencies] deno_core = { version = "0.95.0", path = "../../core" } +deno_tls = { version = "0.1.0", path = "../tls" } http = "0.2.4" hyper = { version = "0.14.9" } serde = { version = "1.0.126", features = ["derive"] } tokio = { version = "1.8.1", features = ["full"] } tokio-rustls = "0.22.0" tokio-tungstenite = { version = "0.14.0", features = ["rustls-tls"] } -webpki = "0.21.4" -webpki-roots = "0.21.1" diff --git a/extensions/websocket/lib.rs b/extensions/websocket/lib.rs index f5bf15c79..01f0a523d 100644 --- a/extensions/websocket/lib.rs +++ b/extensions/websocket/lib.rs @@ -22,31 +22,31 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ZeroCopyBuf; +use deno_tls::create_client_config; +use deno_tls::webpki::DNSNameRef; use http::{Method, Request, Uri}; use serde::Deserialize; use serde::Serialize; use std::borrow::Cow; use std::cell::RefCell; -use std::io::BufReader; -use std::io::Cursor; use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; use tokio::net::TcpStream; -use tokio_rustls::{rustls::ClientConfig, TlsConnector}; +use tokio_rustls::rustls::RootCertStore; +use tokio_rustls::TlsConnector; use tokio_tungstenite::tungstenite::{ handshake::client::Response, protocol::frame::coding::CloseCode, protocol::CloseFrame, Message, }; use tokio_tungstenite::MaybeTlsStream; use tokio_tungstenite::{client_async, WebSocketStream}; -use webpki::DNSNameRef; pub use tokio_tungstenite; // Re-export tokio_tungstenite #[derive(Clone)] -pub struct WsCaData(pub Vec<u8>); +pub struct WsRootStore(pub Option<RootCertStore>); #[derive(Clone)] pub struct WsUserAgent(pub String); @@ -197,7 +197,7 @@ where ); } - let ws_ca_data = state.borrow().try_borrow::<WsCaData>().cloned(); + let root_cert_store = state.borrow().borrow::<WsRootStore>().0.clone(); let user_agent = state.borrow().borrow::<WsUserAgent>().0.clone(); let uri: Uri = args.url.parse()?; let mut request = Request::builder().method(Method::GET).uri(&uri); @@ -221,17 +221,8 @@ where let socket: MaybeTlsStream<TcpStream> = match uri.scheme_str() { Some("ws") => MaybeTlsStream::Plain(tcp_socket), Some("wss") => { - let mut config = ClientConfig::new(); - config - .root_store - .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); - - if let Some(ws_ca_data) = ws_ca_data { - let reader = &mut BufReader::new(Cursor::new(ws_ca_data.0)); - config.root_store.add_pem_file(reader).unwrap(); - } - - let tls_connector = TlsConnector::from(Arc::new(config)); + let tls_config = create_client_config(root_cert_store, None)?; + let tls_connector = TlsConnector::from(Arc::new(tls_config)); let dnsname = DNSNameRef::try_from_ascii_str(domain) .map_err(|_| invalid_hostname(domain))?; let tls_socket = tls_connector.connect(dnsname, tcp_socket).await?; @@ -385,7 +376,7 @@ pub async fn op_ws_next_event( pub fn init<P: WebSocketPermissions + 'static>( user_agent: String, - ca_data: Option<Vec<u8>>, + root_cert_store: Option<RootCertStore>, ) -> Extension { Extension::builder() .js(include_js_files!( @@ -404,9 +395,7 @@ pub fn init<P: WebSocketPermissions + 'static>( ]) .state(move |state| { state.put::<WsUserAgent>(WsUserAgent(user_agent.clone())); - if let Some(ca_data) = ca_data.clone() { - state.put::<WsCaData>(WsCaData(ca_data)); - } + state.put::<WsRootStore>(WsRootStore(root_cert_store.clone())); Ok(()) }) .build() |