Skip to main content

rustfs_tls_runtime/
lib.rs

1// Copyright 2024 RustFS Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15pub mod certs;
16pub mod config;
17pub mod coordinator;
18pub mod debug;
19pub mod error;
20pub mod fingerprint;
21pub mod material;
22pub mod metrics;
23pub mod outbound;
24pub mod server;
25pub mod source;
26pub mod state;
27
28pub use certs::{
29    CertDirectoryLoadOptions, TlsCertPairInspection, TlsCertPairStatus, TlsDirectoryInspection, TlsDomainInspection,
30    WebPkiClientVerifierOptions, build_webpki_client_verifier, create_multi_cert_resolver, inspect_cert_directory,
31    load_all_certs_from_directory, load_cert_bundle_der_bytes, load_certs, load_private_key,
32};
33pub use config::{ReloadApplyHint, ReloadDetectMode, TlsReloadOptions};
34pub use coordinator::{TlsConsumer, TlsReloadCoordinator};
35pub use debug::{TlsConsumerStatusItem, TlsDebugStatusResponse, TlsDebugStatusResponseBuilder};
36pub use error::TlsRuntimeError;
37pub use fingerprint::TlsFingerprint;
38pub use material::{OutboundTlsMaterial, ServerTlsMaterial, TlsMaterialSnapshot};
39pub use metrics::{
40    TLS_OUTBOUND_GLOBAL_CONSUMER, TLS_RUNTIME_FOUNDATION_CONSUMER, init_tls_metrics, record_tls_consumer_stale_generation,
41    record_tls_generation, record_tls_publication_fail, record_tls_reload_result, record_tls_reload_skipped,
42};
43pub use outbound::{
44    GlobalOutboundTlsStateSummary, GlobalPublishedOutboundTlsState, load_global_outbound_tls_generation,
45    load_global_outbound_tls_state, publish_global_outbound_tls_state, summarize_global_outbound_tls_state,
46};
47pub use server::{ReloadableServerCertResolver, spawn_server_cert_reload_loop};
48pub use source::{TlsFileLayout, TlsSource, TlsSourceKind};
49pub use state::OutboundOnlySnapshotArgs;
50pub use state::{TlsGeneration, TlsPublishedState, TlsReloadRuntimeState, TlsRuntimeStatusSnapshot};
51pub use state::{TlsRuntimeConsumerSection, TlsRuntimeOutboundSection, TlsRuntimeRuntimeSection, TlsRuntimeServerSection};
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use rcgen::generate_simple_self_signed;
57    use std::collections::HashMap;
58
59    #[test]
60    fn tls_source_requires_existing_directory() {
61        let source = TlsSource::from_directory("/definitely/missing/rustfs/tls-runtime-test");
62        let err = source.validate_directory().expect_err("missing directory should fail");
63        assert!(matches!(err, TlsRuntimeError::DirectoryNotFound { .. }));
64    }
65
66    #[test]
67    fn fingerprint_changes_when_server_material_changes() {
68        let cert_a = generate_simple_self_signed(vec!["a.example.com".to_string()]).expect("cert A should generate");
69        let cert_b = generate_simple_self_signed(vec!["b.example.com".to_string()]).expect("cert B should generate");
70
71        let single_a = ServerTlsMaterial::SingleCert {
72            certs: vec![cert_a.cert.der().clone()],
73            key: rustls::pki_types::PrivateKeyDer::try_from(cert_a.signing_key.serialize_der()).expect("key A should convert"),
74        };
75        let single_b = ServerTlsMaterial::SingleCert {
76            certs: vec![cert_b.cert.der().clone()],
77            key: rustls::pki_types::PrivateKeyDer::try_from(cert_b.signing_key.serialize_der()).expect("key B should convert"),
78        };
79
80        let bytes_a = match &single_a {
81            ServerTlsMaterial::SingleCert { certs, key } => {
82                let mut bytes = Vec::new();
83                for cert in certs {
84                    bytes.extend_from_slice(cert.as_ref());
85                }
86                bytes.extend_from_slice(key.secret_der());
87                bytes
88            }
89            ServerTlsMaterial::MultiCert { .. } => unreachable!(),
90        };
91        let bytes_b = match &single_b {
92            ServerTlsMaterial::SingleCert { certs, key } => {
93                let mut bytes = Vec::new();
94                for cert in certs {
95                    bytes.extend_from_slice(cert.as_ref());
96                }
97                bytes.extend_from_slice(key.secret_der());
98                bytes
99            }
100            ServerTlsMaterial::MultiCert { .. } => unreachable!(),
101        };
102
103        let fp_a = TlsFingerprint::from_optional_bytes(Some(&bytes_a), None, None, None, None);
104        let fp_b = TlsFingerprint::from_optional_bytes(Some(&bytes_b), None, None, None, None);
105        assert_ne!(fp_a, fp_b);
106    }
107
108    #[tokio::test]
109    async fn coordinator_can_publish_initial_state() {
110        let source = TlsSource::from_directory(std::env::temp_dir());
111        let coordinator = TlsReloadCoordinator::new(source.clone(), TlsReloadOptions::default());
112        let snapshot = TlsMaterialSnapshot {
113            source,
114            server: Some(ServerTlsMaterial::MultiCert {
115                cert_key_pairs: HashMap::new(),
116            }),
117            outbound: OutboundTlsMaterial {
118                root_ca_pem: Vec::new(),
119                mtls_identity: None,
120            },
121            fingerprint: TlsFingerprint::default(),
122        };
123
124        let published = coordinator.publish_initial_state(snapshot).await;
125        assert_eq!(published.generation, TlsGeneration(1));
126    }
127}