Skip to content

Commit d445e74

Browse files
committed
Plumb errors through rustls implementation
1 parent 354a6ee commit d445e74

File tree

14 files changed

+342
-299
lines changed

14 files changed

+342
-299
lines changed

.bleep

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3919de32fe7a184847dd6ce7da98247ec9e1eb86
1+
7c7f575484b512ad279084c427adf66069ec2619

pingora-core/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ tokio-test = "0.4"
6868
zstd = "0"
6969
httpdate = "1"
7070
x509-parser = { version = "0.16.0", optional = true }
71+
ouroboros = { version = "0.18.4", optional = true }
7172

7273
[target.'cfg(unix)'.dependencies]
7374
daemonize = "0.5.0"
@@ -92,7 +93,7 @@ jemallocator = "0.5"
9293
default = []
9394
openssl = ["pingora-openssl", "openssl_derived",]
9495
boringssl = ["pingora-boringssl", "openssl_derived",]
95-
rustls = ["pingora-rustls", "any_tls", "dep:x509-parser"]
96+
rustls = ["pingora-rustls", "any_tls", "dep:x509-parser", "ouroboros"]
9697
patched_http1 = ["pingora-http/patched_http1"]
9798
openssl_derived = ["any_tls"]
9899
any_tls = []

pingora-core/src/connectors/tls/rustls/mod.rs

+47-59
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use pingora_error::{
2121
OrErr, Result,
2222
};
2323
use pingora_rustls::{
24-
load_ca_file_into_store, load_certs_key_file, load_platform_certs_incl_env_into_store, version,
25-
CertificateDer, ClientConfig as RusTlsClientConfig, PrivateKeyDer, RootCertStore,
24+
load_ca_file_into_store, load_certs_and_key_files, load_platform_certs_incl_env_into_store,
25+
version, CertificateDer, ClientConfig as RusTlsClientConfig, PrivateKeyDer, RootCertStore,
2626
TlsConnector as RusTlsConnector,
2727
};
2828

@@ -37,18 +37,21 @@ pub struct Connector {
3737
}
3838

3939
impl Connector {
40+
/// Create a new connector based on the optional configurations. If no
41+
/// configurations are provided, no customized certificates or keys will be
42+
/// used
4043
pub fn new(config_opt: Option<ConnectorOptions>) -> Self {
41-
TlsConnector::build_connector(config_opt)
44+
TlsConnector::build_connector(config_opt).unwrap()
4245
}
4346
}
4447

4548
pub(crate) struct TlsConnector {
46-
config: RusTlsClientConfig,
47-
ca_certs: RootCertStore,
49+
config: Arc<RusTlsClientConfig>,
50+
ca_certs: Arc<RootCertStore>,
4851
}
4952

5053
impl TlsConnector {
51-
pub(crate) fn build_connector(options: Option<ConnectorOptions>) -> Connector
54+
pub(crate) fn build_connector(options: Option<ConnectorOptions>) -> Result<Connector>
5255
where
5356
Self: Sized,
5457
{
@@ -65,16 +68,16 @@ impl TlsConnector {
6568

6669
if let Some(conf) = options.as_ref() {
6770
if let Some(ca_file_path) = conf.ca_file.as_ref() {
68-
load_ca_file_into_store(ca_file_path, &mut ca_certs);
71+
load_ca_file_into_store(ca_file_path, &mut ca_certs)?;
6972
} else {
70-
load_platform_certs_incl_env_into_store(&mut ca_certs);
73+
load_platform_certs_incl_env_into_store(&mut ca_certs)?;
7174
}
7275
if let Some((cert, key)) = conf.cert_key_file.as_ref() {
73-
certs_key = load_certs_key_file(cert, key);
76+
certs_key = load_certs_and_key_files(cert, key)?;
7477
}
7578
// TODO: support SSLKEYLOGFILE
7679
} else {
77-
load_platform_certs_incl_env_into_store(&mut ca_certs);
80+
load_platform_certs_incl_env_into_store(&mut ca_certs)?;
7881
}
7982

8083
(ca_certs, certs_key)
@@ -92,19 +95,19 @@ impl TlsConnector {
9295
Err(err) => {
9396
// TODO: is there a viable alternative to the panic?
9497
// falling back to no client auth... does not seem to be reasonable.
95-
panic!(
96-
"{}",
97-
format!("Failed to configure client auth cert/key. Error: {}", err)
98-
);
98+
panic!("Failed to configure client auth cert/key. Error: {}", err);
9999
}
100100
}
101101
}
102102
None => builder.with_no_client_auth(),
103103
};
104104

105-
Connector {
106-
ctx: Arc::new(TlsConnector { config, ca_certs }),
107-
}
105+
Ok(Connector {
106+
ctx: Arc::new(TlsConnector {
107+
config: Arc::new(config),
108+
ca_certs: Arc::new(ca_certs),
109+
}),
110+
})
108111
}
109112
}
110113

@@ -118,96 +121,81 @@ where
118121
T: IO,
119122
P: Peer + Send + Sync,
120123
{
121-
let mut config = tls_ctx.config.clone();
124+
let config = &tls_ctx.config;
122125

123126
// TODO: setup CA/verify cert store from peer
124-
// looks like the fields are always None
125-
// peer.get_ca()
126-
127+
// peer.get_ca() returns None by default. It must be replaced by the
128+
// implementation of `peer`
127129
let key_pair = peer.get_client_cert_key();
128-
let updated_config: Option<RusTlsClientConfig> = match key_pair {
130+
let mut updated_config_opt: Option<RusTlsClientConfig> = match key_pair {
129131
None => None,
130132
Some(key_arc) => {
131133
debug!("setting client cert and key");
132134

133135
let mut cert_chain = vec![];
134136
debug!("adding leaf certificate to mTLS cert chain");
135-
cert_chain.push(key_arc.leaf().to_owned());
137+
cert_chain.push(key_arc.leaf());
136138

137139
debug!("adding intermediate certificates to mTLS cert chain");
138140
key_arc
139141
.intermediates()
140142
.to_owned()
141143
.iter()
142-
.map(|i| i.to_vec())
144+
.copied()
143145
.for_each(|i| cert_chain.push(i));
144146

145-
let certs: Vec<CertificateDer> = cert_chain
146-
.into_iter()
147-
.map(|c| c.as_slice().to_owned().into())
148-
.collect();
147+
let certs: Vec<CertificateDer> = cert_chain.into_iter().map(|c| c.into()).collect();
149148
let private_key: PrivateKeyDer =
150149
key_arc.key().as_slice().to_owned().try_into().unwrap();
151150

152151
let builder = RusTlsClientConfig::builder_with_protocol_versions(&[
153152
&version::TLS12,
154153
&version::TLS13,
155154
])
156-
.with_root_certificates(tls_ctx.ca_certs.clone());
157-
158-
let updated_config = builder
159-
.with_client_auth_cert(certs, private_key)
160-
.explain_err(InvalidCert, |e| {
161-
format!(
162-
"Failed to use peer cert/key to update Rustls config: {:?}",
163-
e
164-
)
165-
})?;
155+
.with_root_certificates(Arc::clone(&tls_ctx.ca_certs));
156+
debug!("added root ca certificates");
157+
158+
let updated_config = builder.with_client_auth_cert(certs, private_key).or_err(
159+
InvalidCert,
160+
"Failed to use peer cert/key to update Rustls config",
161+
)?;
166162
Some(updated_config)
167163
}
168164
};
169165

170166
if let Some(alpn) = alpn_override.as_ref().or(peer.get_alpn()) {
171-
config.alpn_protocols = alpn.to_wire_protocols();
167+
let alpn_protocols = alpn.to_wire_protocols();
168+
if let Some(updated_config) = updated_config_opt.as_mut() {
169+
updated_config.alpn_protocols = alpn_protocols;
170+
} else {
171+
let mut updated_config = RusTlsClientConfig::clone(config);
172+
updated_config.alpn_protocols = alpn_protocols;
173+
updated_config_opt = Some(updated_config);
174+
}
172175
}
173176

174177
// TODO: curve setup from peer
175178
// - second key share from peer, currently only used in boringssl with PQ features
176179

177-
let tls_conn = if let Some(cfg) = updated_config {
180+
let tls_conn = if let Some(cfg) = updated_config_opt {
178181
RusTlsConnector::from(Arc::new(cfg))
179182
} else {
180-
RusTlsConnector::from(Arc::new(config))
183+
RusTlsConnector::from(Arc::clone(config))
181184
};
182185

183-
// TODO: for consistent behaviour between TLS providers some additions are required
186+
// TODO: for consistent behavior between TLS providers some additions are required
184187
// - allowing to disable verification
185-
// - the validation/replace logic would need adjustments to match the boringssl/openssl behaviour
186-
// implementing a custom certificate_verifier could be used to achieve matching behaviour
188+
// - the validation/replace logic would need adjustments to match the boringssl/openssl behavior
189+
// implementing a custom certificate_verifier could be used to achieve matching behavior
187190
//let d_conf = config.dangerous();
188191
//d_conf.set_certificate_verifier(...);
189192

190193
let mut domain = peer.sni().to_string();
191-
if peer.sni().is_empty() {
192-
// use ip in case SNI is not present
193-
// TODO: disable validation
194-
domain = peer.address().as_inet().unwrap().ip().to_string()
195-
}
196-
197194
if peer.verify_cert() && peer.verify_hostname() {
198195
// TODO: streamline logic with replacing first underscore within TLS implementations
199196
if let Some(sni_s) = replace_leftmost_underscore(peer.sni()) {
200197
domain = sni_s;
201198
}
202-
if let Some(alt_cn) = peer.alternative_cn() {
203-
if !alt_cn.is_empty() {
204-
domain = alt_cn.to_string();
205-
// TODO: streamline logic with replacing first underscore within TLS implementations
206-
if let Some(alt_cn_s) = replace_leftmost_underscore(alt_cn) {
207-
domain = alt_cn_s;
208-
}
209-
}
210-
}
211199
}
212200

213201
let connect_future = handshake(&tls_conn, &domain, stream);

pingora-core/src/listeners/tls/rustls/mod.rs

+21-17
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use crate::listeners::TlsAcceptCallbacks;
1818
use crate::protocols::tls::{server::handshake, server::handshake_with_callback, TlsStream};
1919
use log::debug;
2020
use pingora_error::ErrorType::InternalError;
21-
use pingora_error::{Error, ErrorSource, ImmutStr, OrErr, Result};
22-
use pingora_rustls::load_certs_key_file;
21+
use pingora_error::{Error, OrErr, Result};
22+
use pingora_rustls::load_certs_and_key_files;
2323
use pingora_rustls::ServerConfig;
2424
use pingora_rustls::{version, TlsAcceptor as RusTlsAcceptor};
2525

@@ -38,21 +38,29 @@ pub struct Acceptor {
3838
}
3939

4040
impl TlsSettings {
41+
/// Create a Rustls acceptor based on the current setting for certificates,
42+
/// keys, and protocols.
43+
///
44+
/// _NOTE_ This function will panic if there is an error in loading
45+
/// certificate files or constructing the builder
46+
///
47+
/// Todo: Return a result instead of panicking XD
4148
pub fn build(self) -> Acceptor {
42-
let (certs, key) =
43-
load_certs_key_file(&self.cert_path, &self.key_path).unwrap_or_else(|| {
44-
panic!(
45-
"Failed to load provided certificates \"{}\" or key \"{}\".",
46-
self.cert_path, self.key_path
47-
)
48-
});
49+
let Ok(Some((certs, key))) = load_certs_and_key_files(&self.cert_path, &self.key_path)
50+
else {
51+
panic!(
52+
"Failed to load provided certificates \"{}\" or key \"{}\".",
53+
self.cert_path, self.key_path
54+
)
55+
};
4956

57+
// TODO - Add support for client auth & custom CA support
5058
let mut config =
5159
ServerConfig::builder_with_protocol_versions(&[&version::TLS12, &version::TLS13])
5260
.with_no_client_auth()
5361
.with_single_cert(certs, key)
5462
.explain_err(InternalError, |e| {
55-
format!("Failed to create server listener config: {}", e)
63+
format!("Failed to create server listener config: {e}")
5664
})
5765
.unwrap();
5866

@@ -92,14 +100,10 @@ impl TlsSettings {
92100
Self: Sized,
93101
{
94102
// TODO: verify if/how callback in handshake can be done using Rustls
95-
Err(Error::create(
103+
Error::e_explain(
96104
InternalError,
97-
ErrorSource::Internal,
98-
Some(ImmutStr::from(
99-
"Certificate callbacks are not supported with feature \"rustls\".",
100-
)),
101-
None,
102-
))
105+
"Certificate callbacks are not supported with feature \"rustls\".",
106+
)
103107
}
104108
}
105109

pingora-core/src/protocols/tls/rustls/client.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ pub async fn handshake<S: IO>(
2828
) -> Result<TlsStream<S>> {
2929
let mut stream = TlsStream::from_connector(connector, domain, io)
3030
.await
31-
.explain_err(TLSHandshakeFailure, |e| {
32-
format!("tip: tls stream error: {e}")
33-
})?;
31+
.or_err(TLSHandshakeFailure, "tls stream error")?;
3432

3533
let handshake_result = stream.connect().await;
3634
match handshake_result {

pingora-core/src/protocols/tls/rustls/mod.rs

+2-13
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,9 @@ pub mod server;
1717
mod stream;
1818

1919
pub use stream::*;
20-
use x509_parser::prelude::FromDer;
2120

22-
pub type CaType = [Box<CertWrapper>];
21+
use crate::utils::tls::WrappedX509;
2322

24-
#[derive(Debug)]
25-
#[repr(transparent)]
26-
pub struct CertWrapper(pub [u8]);
27-
28-
impl CertWrapper {
29-
pub fn not_after(&self) -> String {
30-
let (_, x509cert) = x509_parser::certificate::X509Certificate::from_der(&self.0)
31-
.expect("Failed to parse certificate from DER format.");
32-
x509cert.validity.not_after.to_string()
33-
}
34-
}
23+
pub type CaType = [WrappedX509];
3524

3625
pub struct TlsRef;

pingora-core/src/protocols/tls/rustls/server.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,9 @@ pub async fn handshake_with_callback<S: IO>(
8080
.resume_accept()
8181
.await
8282
.explain_err(TLSHandshakeFailure, |e| format!("TLS accept() failed: {e}"))?;
83-
Ok(tls_stream)
84-
} else {
85-
Ok(tls_stream)
8683
}
84+
85+
Ok(tls_stream)
8786
}
8887

8988
#[async_trait]

0 commit comments

Comments
 (0)