diff options
Diffstat (limited to 'pingora-rustls')
-rw-r--r-- | pingora-rustls/Cargo.toml | 14 | ||||
-rw-r--r-- | pingora-rustls/src/lib.rs | 145 |
2 files changed, 157 insertions, 2 deletions
diff --git a/pingora-rustls/Cargo.toml b/pingora-rustls/Cargo.toml index 677cf18..1c84d30 100644 --- a/pingora-rustls/Cargo.toml +++ b/pingora-rustls/Cargo.toml @@ -13,3 +13,17 @@ RusTLS async APIs for Pingora. [lib] name = "pingora_rustls" path = "src/lib.rs" + +[dependencies] +log = "0.4.21" +ring = "0.17.8" +rustls = "0.23.12" +rustls-native-certs = "0.7.1" +rustls-pemfile = "2.1.2" +rustls-pki-types = "1.7.0" +tokio-rustls = "0.26.0" +no_debug = "3.1.0" + +[dev-dependencies] +tokio-test = "0.4.3" +tokio = { workspace = true, features = ["full"] } diff --git a/pingora-rustls/src/lib.rs b/pingora-rustls/src/lib.rs index 814bcb9..e4d5de0 100644 --- a/pingora-rustls/src/lib.rs +++ b/pingora-rustls/src/lib.rs @@ -12,6 +12,147 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub fn rustls() { - todo!() +#![warn(clippy::all)] + +use std::fs::File; +use std::io::BufReader; + +use log::{error, warn}; +pub use no_debug::{Ellipses, NoDebug, WithTypeInfo}; +pub use rustls::{version, ClientConfig, RootCertStore, ServerConfig, Stream}; +pub use rustls_native_certs::load_native_certs; +use rustls_pemfile::Item; +pub use rustls_pki_types::{CertificateDer, PrivateKeyDer, ServerName}; +pub use tokio_rustls::client::TlsStream as ClientTlsStream; +pub use tokio_rustls::server::TlsStream as ServerTlsStream; +pub use tokio_rustls::{Accept, Connect, TlsAcceptor, TlsConnector, TlsStream}; + +fn load_file(path: &String) -> BufReader<File> { + let file = File::open(path).expect("io error"); + BufReader::new(file) +} +fn load_pem_file(path: &String) -> Result<Vec<Item>, std::io::Error> { + let iter: Vec<Item> = rustls_pemfile::read_all(&mut load_file(path)) + .filter_map(|f| { + if let Ok(f) = f { + Some(f) + } else { + let err = f.err().unwrap(); + warn!( + "Skipping PEM element in file \"{}\" due to error \"{}\"", + path, err + ); + None + } + }) + .collect(); + Ok(iter) +} + +pub fn load_ca_file_into_store(path: &String, cert_store: &mut RootCertStore) { + let ca_file = load_pem_file(path); + match ca_file { + Ok(cas) => { + cas.into_iter().for_each(|pem_item| { + // only loading certificates, handling a CA file + match pem_item { + Item::X509Certificate(content) => match cert_store.add(content) { + Ok(_) => {} + Err(err) => { + error!("{}", err) + } + }, + Item::Pkcs1Key(_) => {} + Item::Pkcs8Key(_) => {} + Item::Sec1Key(_) => {} + Item::Crl(_) => {} + Item::Csr(_) => {} + _ => {} + } + }); + } + Err(err) => { + error!( + "Failed to load configured ca file located at \"{}\", error: \"{}\"", + path, err + ); + } + } +} + +pub fn load_platform_certs_incl_env_into_store(ca_certs: &mut RootCertStore) { + // this includes handling of ENV vars SSL_CERT_FILE & SSL_CERT_DIR + let native_platform_certs = load_native_certs(); + match native_platform_certs { + Ok(certs) => { + for cert in certs { + ca_certs.add(cert).unwrap(); + } + } + Err(err) => { + error!( + "Failed to load native platform ca-certificates: \"{:?}\". Continuing without ...", + err + ); + } + } +} + +pub fn load_certs_key_file<'a>( + cert: &String, + key: &String, +) -> Option<(Vec<CertificateDer<'a>>, PrivateKeyDer<'a>)> { + let certs_file = load_pem_file(cert) + .unwrap_or_else(|_| panic!("Failed to load configured cert file located at {}.", cert)); + let key_file = load_pem_file(key) + .unwrap_or_else(|_| panic!("Failed to load configured key file located at {}.", cert)); + + let mut certs: Vec<CertificateDer<'a>> = vec![]; + certs_file.into_iter().for_each(|i| { + if let Item::X509Certificate(cert) = i { + certs.push(cert) + } + }); + + let private_key = match key_file.into_iter().next()? { + Item::Pkcs1Key(key) => Some(PrivateKeyDer::from(key)), + Item::Pkcs8Key(key) => Some(PrivateKeyDer::from(key)), + Item::Sec1Key(key) => Some(PrivateKeyDer::from(key)), + _ => None, + }; + + if certs.is_empty() || private_key.is_none() { + None + } else { + Some((certs, private_key?)) + } +} + +pub fn load_pem_file_ca(path: &String) -> Vec<u8> { + let mut reader = load_file(path); + let cas_file = rustls_pemfile::certs(&mut reader); + let ca = cas_file.into_iter().find_map(|pem_item| { + if let Ok(item) = pem_item { + Some(item) + } else { + None + } + }); + match ca { + None => Vec::new(), + Some(ca) => ca.to_vec(), + } +} + +pub fn load_pem_file_private_key(path: &String) -> Vec<u8> { + let key = rustls_pemfile::private_key(&mut load_file(path)); + if let Ok(Some(key)) = key { + return key.secret_der().to_vec(); + } + Vec::new() +} + +pub fn hash_certificate(cert: CertificateDer) -> Vec<u8> { + let hash = ring::digest::digest(&ring::digest::SHA256, cert.as_ref()); + hash.as_ref().to_vec() } |