// Copyright 2024 RustFS Team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. pub mod certs; pub mod config; pub mod coordinator; pub mod debug; pub mod error; pub mod fingerprint; pub mod material; pub mod metrics; pub mod outbound; pub mod server; pub mod source; pub mod state; pub use certs::{ CertDirectoryLoadOptions, TlsCertPairInspection, TlsCertPairStatus, TlsDirectoryInspection, TlsDomainInspection, WebPkiClientVerifierOptions, build_webpki_client_verifier, create_multi_cert_resolver, inspect_cert_directory, load_all_certs_from_directory, load_cert_bundle_der_bytes, load_certs, load_private_key, }; pub use config::{ReloadApplyHint, ReloadDetectMode, TlsReloadOptions}; pub use coordinator::{TlsConsumer, TlsReloadCoordinator}; pub use debug::{TlsConsumerStatusItem, TlsDebugStatusResponse, TlsDebugStatusResponseBuilder}; pub use error::TlsRuntimeError; pub use fingerprint::TlsFingerprint; pub use material::{OutboundTlsMaterial, ServerTlsMaterial, TlsMaterialSnapshot}; pub use metrics::{ TLS_OUTBOUND_GLOBAL_CONSUMER, TLS_RUNTIME_FOUNDATION_CONSUMER, init_tls_metrics, record_tls_consumer_stale_generation, record_tls_generation, record_tls_publication_fail, record_tls_reload_result, record_tls_reload_skipped, }; pub use outbound::{ GlobalOutboundTlsStateSummary, GlobalPublishedOutboundTlsState, load_global_outbound_tls_generation, load_global_outbound_tls_state, publish_global_outbound_tls_state, summarize_global_outbound_tls_state, }; pub use server::{ReloadableServerCertResolver, spawn_server_cert_reload_loop}; pub use source::{TlsFileLayout, TlsSource, TlsSourceKind}; pub use state::OutboundOnlySnapshotArgs; pub use state::{TlsGeneration, TlsPublishedState, TlsReloadRuntimeState, TlsRuntimeStatusSnapshot}; pub use state::{TlsRuntimeConsumerSection, TlsRuntimeOutboundSection, TlsRuntimeRuntimeSection, TlsRuntimeServerSection}; #[cfg(test)] mod tests { use super::*; use rcgen::generate_simple_self_signed; use std::collections::HashMap; #[test] fn tls_source_requires_existing_directory() { let source = TlsSource::from_directory("/definitely/missing/rustfs/tls-runtime-test"); let err = source.validate_directory().expect_err("missing directory should fail"); assert!(matches!(err, TlsRuntimeError::DirectoryNotFound { .. })); } #[test] fn fingerprint_changes_when_server_material_changes() { let cert_a = generate_simple_self_signed(vec!["a.example.com".to_string()]).expect("cert A should generate"); let cert_b = generate_simple_self_signed(vec!["b.example.com".to_string()]).expect("cert B should generate"); let single_a = ServerTlsMaterial::SingleCert { certs: vec![cert_a.cert.der().clone()], key: rustls::pki_types::PrivateKeyDer::try_from(cert_a.signing_key.serialize_der()).expect("key A should convert"), }; let single_b = ServerTlsMaterial::SingleCert { certs: vec![cert_b.cert.der().clone()], key: rustls::pki_types::PrivateKeyDer::try_from(cert_b.signing_key.serialize_der()).expect("key B should convert"), }; let bytes_a = match &single_a { ServerTlsMaterial::SingleCert { certs, key } => { let mut bytes = Vec::new(); for cert in certs { bytes.extend_from_slice(cert.as_ref()); } bytes.extend_from_slice(key.secret_der()); bytes } ServerTlsMaterial::MultiCert { .. } => unreachable!(), }; let bytes_b = match &single_b { ServerTlsMaterial::SingleCert { certs, key } => { let mut bytes = Vec::new(); for cert in certs { bytes.extend_from_slice(cert.as_ref()); } bytes.extend_from_slice(key.secret_der()); bytes } ServerTlsMaterial::MultiCert { .. } => unreachable!(), }; let fp_a = TlsFingerprint::from_optional_bytes(Some(&bytes_a), None, None, None, None); let fp_b = TlsFingerprint::from_optional_bytes(Some(&bytes_b), None, None, None, None); assert_ne!(fp_a, fp_b); } #[tokio::test] async fn coordinator_can_publish_initial_state() { let source = TlsSource::from_directory(std::env::temp_dir()); let coordinator = TlsReloadCoordinator::new(source.clone(), TlsReloadOptions::default()); let snapshot = TlsMaterialSnapshot { source, server: Some(ServerTlsMaterial::MultiCert { cert_key_pairs: HashMap::new(), }), outbound: OutboundTlsMaterial { root_ca_pem: Vec::new(), mtls_identity: None, }, fingerprint: TlsFingerprint::default(), }; let published = coordinator.publish_initial_state(snapshot).await; assert_eq!(published.generation, TlsGeneration(1)); } }