Skip to content

Commit fa74adf

Browse files
authored
fix: OpenSSL and prepare for more SSL libs (#47)
Signed-off-by: Jeremy HERGAULT <jeremy@hergault.fr>
1 parent 42e4247 commit fa74adf

16 files changed

Lines changed: 1024 additions & 632 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,6 @@ jobs:
8787
with:
8888
components: llvm-tools, rustc-dev
8989
- run: >
90-
cargo test --all-features --release
91-
--tests --verbose --color always
90+
cargo test --release --tests
91+
--verbose --color always
9292
--workspace --no-fail-fast

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ include = [
2222
]
2323

2424
[workspace.dependencies]
25-
prosa-utils = { version = "0.3.0", path = "prosa_utils" }
25+
prosa-utils = { version = "0.3.0", path = "prosa_utils", default-features = false }
2626
prosa-macros = { version = "0.3.0", path = "prosa_macros" }
2727
thiserror = "2"
2828
aquamarine = "0.6"
@@ -46,6 +46,9 @@ prometheus = { version = "0.14" }
4646
opentelemetry-prometheus = { version = "0.29" }
4747
opentelemetry-appender-log = "0.29"
4848

49+
# Config SSL
50+
openssl = { version = "0.10" }
51+
4952
# Web
5053
hyper = { version = "1", features = ["server", "http1", "http2"] }
5154
http-body-util = "0.1"

cargo-prosa/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ path = "src/main.rs"
1515

1616
[dependencies]
1717
thiserror.workspace = true
18-
prosa-utils = { workspace = true, features = ["msg", "config", "config-observability"] }
1918
aquamarine.workspace = true
2019
clap = "4"
2120
clap_complete = "4"

prosa/Cargo.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ license.workspace = true
1010
include.workspace = true
1111

1212
[features]
13-
default = ["http-proxy"]
13+
default = ["http-proxy", "openssl"]
1414
http-proxy = ["dep:async-http-proxy"]
15+
openssl = ["dep:openssl", "dep:tokio-openssl", "prosa-utils/config-openssl"]
16+
openssl-vendored = ["openssl", "openssl/vendored", "prosa-utils/config-openssl-vendored"]
1517

1618
[[example]]
1719
name = "proc"
@@ -35,7 +37,7 @@ settings = "stub::proc::StubSettings"
3537
adaptor = ["stub::adaptor::StubParotAdaptor"]
3638

3739
[dependencies]
38-
prosa-utils = { workspace = true, features = ["msg", "config", "config-observability"] }
40+
prosa-utils = { workspace = true, features = ["msg", "config", "config-observability", "config-observability-prometheus"] }
3941
prosa-macros.workspace = true
4042
bytes.workspace = true
4143
tracing = "0.1"
@@ -46,9 +48,9 @@ rlimit = "0.10"
4648

4749
aquamarine.workspace = true
4850

49-
openssl = { version = "0.10" }
51+
openssl = { workspace = true, optional = true }
5052
tokio.workspace = true
51-
tokio-openssl = "0.6"
53+
tokio-openssl = { version = "0.6", optional = true }
5254
async-http-proxy = { version = "1", optional = true, features = ["runtime-tokio","basic-auth"] }
5355

5456
serde = { version = "1", features = ["derive"] }

prosa/src/core/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ impl ProcError for std::io::Error {
5454
}
5555
}
5656

57+
#[cfg(feature = "openssl")]
5758
impl ProcError for openssl::error::Error {
5859
fn recoverable(&self) -> bool {
5960
if let Some(reason) = self.reason() {
@@ -65,6 +66,7 @@ impl ProcError for openssl::error::Error {
6566
}
6667
}
6768

69+
#[cfg(feature = "openssl")]
6870
impl ProcError for openssl::error::ErrorStack {
6971
fn recoverable(&self) -> bool {
7072
for error in self.errors() {

prosa/src/io.rs

Lines changed: 103 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,13 @@ impl From<tokio::net::unix::SocketAddr> for SocketAddr {
143143
mod tests {
144144
use futures_util::future;
145145
use listener::{ListenerSetting, StreamListener};
146-
use openssl::ssl::SslVerifyMode;
147-
use prosa_utils::config::ssl::{SslConfig, Store};
146+
147+
#[cfg(feature = "openssl")]
148+
use prosa_utils::config::ssl::{SslConfig, SslConfigContext as _, Store};
149+
148150
use std::{env, os::fd::AsRawFd as _};
149151
use stream::{Stream, TargetSetting};
150-
use tokio::{
151-
fs::File,
152-
io::{AsyncReadExt as _, AsyncWriteExt},
153-
};
152+
use tokio::io::{AsyncReadExt as _, AsyncWriteExt};
154153

155154
use super::*;
156155

@@ -198,7 +197,7 @@ mod tests {
198197

199198
stream.write_all(b"ProSA").await.unwrap();
200199

201-
let mut buf = vec![];
200+
let mut buf = Vec::new();
202201
stream.read_to_end(&mut buf).await.unwrap();
203202
assert_eq!(buf, b"Worldline");
204203

@@ -253,7 +252,7 @@ mod tests {
253252

254253
stream.write_all(b"ProSA").await.unwrap();
255254

256-
let mut buf = vec![];
255+
let mut buf = Vec::new();
257256
stream.read_to_end(&mut buf).await.unwrap();
258257
assert_eq!(buf, b"Worldline");
259258

@@ -263,20 +262,29 @@ mod tests {
263262
future::join(server, client).await;
264263
}
265264

265+
#[cfg(feature = "openssl")]
266266
#[tokio::test]
267-
async fn ssl_client_server() {
267+
async fn openssl_client_server() {
268268
let addr = "localhost:41443";
269269
let addr_url = Url::parse(format!("tls://{addr}").as_str()).unwrap();
270-
271-
let ssl_config = SslConfig::default();
272-
let ssl_acceptor = ssl_config
273-
.init_tls_server_context(addr_url.host_str())
270+
let cert_path = env::temp_dir()
271+
.join("test_openssl_client_server.pem")
272+
.to_str()
274273
.unwrap()
275-
.build();
276-
let listener = StreamListener::bind(addr)
277-
.await
278-
.unwrap()
279-
.ssl_acceptor(ssl_acceptor, Some(ssl_config.get_ssl_timeout()));
274+
.to_string();
275+
276+
let mut ssl_config = SslConfig::new_self_cert(cert_path.clone());
277+
let listener = {
278+
let ssl_acceptor_builder: ::openssl::ssl::SslAcceptorBuilder = ssl_config
279+
.init_tls_server_context(addr_url.host_str())
280+
.unwrap();
281+
let ssl_acceptor = ssl_acceptor_builder.build();
282+
StreamListener::bind(addr)
283+
.await
284+
.unwrap()
285+
.ssl_acceptor(ssl_acceptor, Some(ssl_config.get_ssl_timeout()))
286+
};
287+
280288
assert!(listener.as_raw_fd() > 0);
281289
assert!(
282290
format!("{listener:?}").contains("Ssl"),
@@ -297,15 +305,21 @@ mod tests {
297305
assert_eq!(&buf, b"ProSA");
298306

299307
client_stream.write_all(b"Worldline").await.unwrap();
308+
309+
let _ = client_stream.shutdown().await;
300310
};
301311

312+
ssl_config.set_store(Store::File { path: cert_path });
302313
let client = async {
303-
let mut ssl_client_context = ssl_config.init_tls_client_context().unwrap();
304-
ssl_client_context.set_verify(SslVerifyMode::NONE);
314+
let mut stream = {
315+
let ssl_client_context: ::openssl::ssl::SslConnectorBuilder =
316+
ssl_config.init_tls_client_context().unwrap();
317+
318+
Stream::connect_openssl(&addr_url, &ssl_client_context.build())
319+
.await
320+
.unwrap()
321+
};
305322

306-
let mut stream = Stream::connect_ssl(&addr_url, &ssl_client_context.build())
307-
.await
308-
.unwrap();
309323
assert!(stream.as_raw_fd() > 0);
310324
assert!(
311325
format!("{stream:?}").contains("Ssl"),
@@ -315,7 +329,7 @@ mod tests {
315329

316330
stream.write_all(b"ProSA").await.unwrap();
317331

318-
let mut buf = vec![];
332+
let mut buf = Vec::new();
319333
stream.read_to_end(&mut buf).await.unwrap();
320334
assert_eq!(buf, b"Worldline");
321335

@@ -325,20 +339,29 @@ mod tests {
325339
future::join(server, client).await;
326340
}
327341

342+
#[cfg(feature = "openssl")]
328343
#[tokio::test]
329-
async fn ssl_client_server_raw() {
344+
async fn openssl_client_server_raw() {
330345
let addr = "localhost:41453";
331346
let addr_url = Url::parse(format!("tls://{addr}").as_str()).unwrap();
332-
333-
let ssl_config = SslConfig::default();
334-
let ssl_acceptor = ssl_config
335-
.init_tls_server_context(addr_url.host_str())
347+
let cert_path = env::temp_dir()
348+
.join("test_openssl_client_server_raw.pem")
349+
.to_str()
336350
.unwrap()
337-
.build();
338-
let listener = StreamListener::bind(addr)
339-
.await
340-
.unwrap()
341-
.ssl_acceptor(ssl_acceptor, Some(ssl_config.get_ssl_timeout()));
351+
.to_string();
352+
353+
let mut ssl_config = SslConfig::new_self_cert(cert_path.clone());
354+
let listener = {
355+
let ssl_acceptor_builder: ::openssl::ssl::SslAcceptorBuilder = ssl_config
356+
.init_tls_server_context(addr_url.host_str())
357+
.unwrap();
358+
let ssl_acceptor = ssl_acceptor_builder.build();
359+
StreamListener::bind(addr)
360+
.await
361+
.unwrap()
362+
.ssl_acceptor(ssl_acceptor, Some(ssl_config.get_ssl_timeout()))
363+
};
364+
342365
assert!(listener.as_raw_fd() > 0);
343366
assert!(
344367
format!("{listener:?}").contains("Ssl"),
@@ -360,15 +383,21 @@ mod tests {
360383
assert_eq!(&buf, b"ProSA");
361384

362385
client_stream.write_all(b"Worldline").await.unwrap();
386+
387+
let _ = client_stream.shutdown().await;
363388
};
364389

390+
ssl_config.set_store(Store::File { path: cert_path });
365391
let client = async {
366-
let mut ssl_client_context = ssl_config.init_tls_client_context().unwrap();
367-
ssl_client_context.set_verify(SslVerifyMode::NONE);
392+
let mut stream = {
393+
let ssl_client_context: ::openssl::ssl::SslConnectorBuilder =
394+
ssl_config.init_tls_client_context().unwrap();
395+
396+
Stream::connect_openssl(&addr_url, &ssl_client_context.build())
397+
.await
398+
.unwrap()
399+
};
368400

369-
let mut stream = Stream::connect_ssl(&addr_url, &ssl_client_context.build())
370-
.await
371-
.unwrap();
372401
assert!(stream.as_raw_fd() > 0);
373402
assert!(
374403
format!("{stream:?}").contains("Ssl"),
@@ -378,7 +407,7 @@ mod tests {
378407

379408
stream.write_all(b"ProSA").await.unwrap();
380409

381-
let mut buf = vec![];
410+
let mut buf = Vec::new();
382411
stream.read_to_end(&mut buf).await.unwrap();
383412
assert_eq!(buf, b"Worldline");
384413

@@ -388,13 +417,18 @@ mod tests {
388417
future::join(server, client).await;
389418
}
390419

420+
#[cfg(feature = "openssl")]
391421
#[tokio::test]
392422
async fn ssl_client_server_with_config() {
393-
let temp_cert_dir = env::temp_dir();
394423
let addr_str = "tls://localhost:41463";
395424
let addr = Url::parse(addr_str).unwrap();
425+
let cert_path = env::temp_dir()
426+
.join("test_ssl_client_server_with_config.pem")
427+
.to_str()
428+
.unwrap()
429+
.to_string();
396430

397-
let mut server_ssl_config = SslConfig::default();
431+
let mut server_ssl_config = SslConfig::new_self_cert(cert_path.clone());
398432
server_ssl_config.set_alpn(vec!["prosa/1".into(), "h2".into()]);
399433

400434
let listener_settings = ListenerSetting::new(addr.clone(), Some(server_ssl_config));
@@ -411,16 +445,6 @@ mod tests {
411445
assert!(listener_settings.to_string().starts_with(addr_str));
412446

413447
let listener = listener_settings.bind().await.unwrap();
414-
if let StreamListener::Ssl(_, acceptor, _) = &listener {
415-
let server_cert = acceptor.context().certificate().unwrap();
416-
let mut server_cert_file = File::create(temp_cert_dir.join("prosa_test_server.pem"))
417-
.await
418-
.unwrap();
419-
server_cert_file
420-
.write_all(&server_cert.to_pem().unwrap())
421-
.await
422-
.unwrap();
423-
}
424448
assert!(listener.as_raw_fd() > 0);
425449
assert!(
426450
format!("{listener:?}").contains("Ssl"),
@@ -443,36 +467,32 @@ mod tests {
443467
client_stream = listener.handshake(client_stream).await.unwrap();
444468

445469
client_stream.write_all(b"Worldline").await.unwrap();
470+
471+
let _ = client_stream.shutdown().await;
446472
};
447473

448474
let mut client_ssl_config = SslConfig::default();
449475
client_ssl_config.set_alpn(vec!["http/1.1".into(), "prosa/1".into()]);
450-
let ssl_store = Store::File {
451-
path: temp_cert_dir.to_str().unwrap().to_string(),
452-
};
453-
client_ssl_config.set_store(ssl_store);
476+
client_ssl_config.set_store(Store::File { path: cert_path });
454477
let target_settings = TargetSetting::new(addr, Some(client_ssl_config), None);
455478
assert_eq!(addr_str, target_settings.to_string());
456479

457480
let client = async {
458481
let mut stream = target_settings.connect().await.unwrap();
459482
assert!(stream.as_raw_fd() > 0);
460483
assert!(
461-
format!("{stream:?}").contains("Ssl"),
462-
"stream `{stream:?}` don't contain Ssl"
484+
format!("{stream:?}").contains("Ssl") || format!("{stream:?}").contains("Tls"),
485+
"stream `{stream:?}` don't contain Ssl or Tls"
463486
);
464-
if let Stream::Ssl(s) = &stream {
465-
assert_eq!(
466-
Some(b"prosa/1".as_slice()),
467-
s.ssl().selected_alpn_protocol()
468-
);
487+
if stream.is_ssl() {
488+
assert!(stream.selected_alpn_check(|alpn| { alpn == b"prosa/1".as_slice() }));
469489
} else {
470490
panic!("Should be an SSL stream for client");
471491
}
472492

473493
stream.write_all(b"ProSA").await.unwrap();
474494

475-
let mut buf = vec![];
495+
let mut buf = Vec::new();
476496
stream.read_to_end(&mut buf).await.unwrap();
477497
assert_eq!(buf, b"Worldline");
478498

@@ -481,4 +501,23 @@ mod tests {
481501

482502
future::join(server, client).await;
483503
}
504+
505+
#[cfg(all(feature = "openssl", not(feature = "openssl-vendored")))]
506+
#[tokio::test]
507+
async fn ssl_client_public_with_config() {
508+
let addr_str = "https://worldline.com/";
509+
let addr = Url::parse(addr_str).unwrap();
510+
511+
let target_settings = TargetSetting::new(addr, Some(SslConfig::default()), None);
512+
assert_eq!(addr_str, target_settings.to_string());
513+
514+
let mut stream = target_settings.connect().await.unwrap();
515+
assert!(stream.as_raw_fd() > 0);
516+
assert!(
517+
format!("{stream:?}").contains("Ssl") || format!("{stream:?}").contains("Tls"),
518+
"stream `{stream:?}` don't contain Ssl or Tls"
519+
);
520+
521+
let _ = stream.shutdown().await;
522+
}
484523
}

0 commit comments

Comments
 (0)