diff options
author | Kevin Guthrie <[email protected]> | 2024-10-15 13:56:22 -0400 |
---|---|---|
committer | Kevin Guthrie <[email protected]> | 2024-10-28 11:51:38 -0400 |
commit | d445e749f3f4116bd0a1af7ac6050f57838f359a (patch) | |
tree | fbf7dc0bf1a243e320aa668b6da9f1640e58ddd2 /pingora-rustls/src/lib.rs | |
parent | 354a6ee1e99b82e23fc0f27a37d8bf41e62b2dc5 (diff) | |
download | pingora-d445e749f3f4116bd0a1af7ac6050f57838f359a.tar.gz pingora-d445e749f3f4116bd0a1af7ac6050f57838f359a.zip |
Plumb errors through rustls implementation
Diffstat (limited to 'pingora-rustls/src/lib.rs')
-rw-r--r-- | pingora-rustls/src/lib.rs | 225 |
1 files changed, 118 insertions, 107 deletions
diff --git a/pingora-rustls/src/lib.rs b/pingora-rustls/src/lib.rs index e4d5de0..f56cd53 100644 --- a/pingora-rustls/src/lib.rs +++ b/pingora-rustls/src/lib.rs @@ -12,13 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! This module contains all the rustls specific pingora integration for things +//! like loading certificates and private keys + #![warn(clippy::all)] use std::fs::File; use std::io::BufReader; +use std::path::Path; -use log::{error, warn}; +use log::warn; pub use no_debug::{Ellipses, NoDebug, WithTypeInfo}; +use pingora_error::{Error, ErrorType, OrErr, Result}; pub use rustls::{version, ClientConfig, RootCertStore, ServerConfig, Stream}; pub use rustls_native_certs::load_native_certs; use rustls_pemfile::Item; @@ -27,132 +32,138 @@ 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) +/// Load the given file from disk as a buffered reader and use the pingora Error +/// type instead of the std::io version +fn load_file<P>(path: P) -> Result<BufReader<File>> +where + P: AsRef<Path>, +{ + File::open(path) + .or_err(ErrorType::FileReadError, "Failed to load file") + .map(BufReader::new) } -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 - } + +/// Read the pem file at the given path from disk +fn load_pem_file<P>(path: P) -> Result<Vec<Item>> +where + P: AsRef<Path>, +{ + rustls_pemfile::read_all(&mut load_file(path)?) + .map(|item_res| { + item_res.or_err( + ErrorType::InvalidCert, + "Certificate in pem file could not be read", + ) }) - .collect(); - Ok(iter) + .collect() } -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 +/// Load the certificates from the given pem file path into the given +/// certificate store +pub fn load_ca_file_into_store<P>(path: P, cert_store: &mut RootCertStore) -> Result<()> +where + P: AsRef<Path>, +{ + for pem_item in load_pem_file(path)? { + // only loading certificates, handling a CA file + let Item::X509Certificate(content) = pem_item else { + return Error::e_explain( + ErrorType::InvalidCert, + "Pem file contains un-loadable certificate type", ); - } + }; + cert_store.add(content).or_err( + ErrorType::InvalidCert, + "Failed to load X509 certificate into root store", + )?; } + + Ok(()) } -pub fn load_platform_certs_incl_env_into_store(ca_certs: &mut RootCertStore) { +/// Attempt to load the native cas into the given root-certificate store +pub fn load_platform_certs_incl_env_into_store(ca_certs: &mut RootCertStore) -> Result<()> { // 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 - ); - } + for cert in load_native_certs() + .or_err(ErrorType::InvalidCert, "Failed to load native certificates")? + .into_iter() + { + ca_certs.add(cert).or_err( + ErrorType::InvalidCert, + "Failed to load native certificate into root store", + )?; } + + Ok(()) } -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 +/// Load the certificates and private key files +pub fn load_certs_and_key_files<'a>( + cert: &str, + key: &str, +) -> Result<Option<(Vec<CertificateDer<'a>>, PrivateKeyDer<'a>)>> { + let certs_file = load_pem_file(cert)?; + let key_file = load_pem_file(key)?; + + let certs = certs_file + .into_iter() + .filter_map(|item| { + if let Item::X509Certificate(cert) = item { + Some(cert) + } else { + None + } + }) + .collect::<Vec<_>>(); + + // These are the currently supported pk types - + // [https://doc.servo.org/rustls/key/struct.PrivateKey.html] + let private_key_opt = key_file + .into_iter() + .filter_map(|key_item| match key_item { + Item::Pkcs1Key(key) => Some(PrivateKeyDer::from(key)), + Item::Pkcs8Key(key) => Some(PrivateKeyDer::from(key)), + Item::Sec1Key(key) => Some(PrivateKeyDer::from(key)), + _ => None, + }) + .next(); + + if let (Some(private_key), false) = (private_key_opt, certs.is_empty()) { + Ok(Some((certs, private_key))) } else { - Some((certs, private_key?)) + Ok(None) } } -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(), - } +/// Load the certificate +pub fn load_pem_file_ca(path: &String) -> Result<Vec<u8>> { + let mut reader = load_file(path)?; + let cas_file_items = rustls_pemfile::certs(&mut reader) + .map(|item_res| { + item_res.or_err( + ErrorType::InvalidCert, + "Failed to load certificate from file", + ) + }) + .collect::<Result<Vec<_>>>()?; + + Ok(cas_file_items + .first() + .map(|ca| ca.to_vec()) + .unwrap_or_default()) } -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 load_pem_file_private_key(path: &String) -> Result<Vec<u8>> { + Ok(rustls_pemfile::private_key(&mut load_file(path)?) + .or_err( + ErrorType::InvalidCert, + "Failed to load private key from file", + )? + .map(|key| key.secret_der().to_vec()) + .unwrap_or_default()) } -pub fn hash_certificate(cert: CertificateDer) -> Vec<u8> { +pub fn hash_certificate(cert: &CertificateDer) -> Vec<u8> { let hash = ring::digest::digest(&ring::digest::SHA256, cert.as_ref()); hash.as_ref().to_vec() } |