mirror of
https://github.com/denoland/deno.git
synced 2024-11-01 09:24:20 -04:00
02c74fb709
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.
129 lines
3.8 KiB
Rust
129 lines
3.8 KiB
Rust
// 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)))
|
|
}
|