aboutsummaryrefslogtreecommitdiffhomepage
path: root/pingora-rustls/src/lib.rs
diff options
context:
space:
mode:
authorKevin Guthrie <[email protected]>2024-10-15 13:56:22 -0400
committerKevin Guthrie <[email protected]>2024-10-28 11:51:38 -0400
commitd445e749f3f4116bd0a1af7ac6050f57838f359a (patch)
treefbf7dc0bf1a243e320aa668b6da9f1640e58ddd2 /pingora-rustls/src/lib.rs
parent354a6ee1e99b82e23fc0f27a37d8bf41e62b2dc5 (diff)
downloadpingora-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.rs225
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()
}