mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-19 19:16:17 +00:00
fix(server): fail fast when configured TLS parsing fails (#2939)
This commit is contained in:
+1
-1
@@ -225,7 +225,7 @@ async fn async_main() -> Result<()> {
|
|||||||
// Initialize TLS outbound material (root CAs, mTLS identity) if configured.
|
// Initialize TLS outbound material (root CAs, mTLS identity) if configured.
|
||||||
// Server-side TLS acceptor is built separately inside start_http_server()
|
// Server-side TLS acceptor is built separately inside start_http_server()
|
||||||
// using the same TlsMaterialSnapshot loading logic.
|
// using the same TlsMaterialSnapshot loading logic.
|
||||||
if let Some(tls_path) = &config.tls_path {
|
if let Some(tls_path) = config.tls_path.as_deref().map(str::trim).filter(|path| !path.is_empty()) {
|
||||||
match rustfs::server::tls_material::TlsMaterialSnapshot::load(tls_path).await {
|
match rustfs::server::tls_material::TlsMaterialSnapshot::load(tls_path).await {
|
||||||
Ok(snapshot) => {
|
Ok(snapshot) => {
|
||||||
snapshot.apply_outbound().await;
|
snapshot.apply_outbound().await;
|
||||||
|
|||||||
@@ -215,17 +215,24 @@ pub async fn start_http_server(
|
|||||||
TcpListener::from_std(socket.into())?
|
TcpListener::from_std(socket.into())?
|
||||||
};
|
};
|
||||||
|
|
||||||
let tls_path = config.tls_path.as_deref().unwrap_or_default();
|
let tls_path = config.tls_path.as_deref().map(str::trim).unwrap_or_default();
|
||||||
|
let tls_path_configured = !tls_path.is_empty();
|
||||||
// Load TLS materials and build server acceptor.
|
// Load TLS materials and build server acceptor.
|
||||||
// Note: outbound material (root CAs, mTLS identity) is already applied in main.rs.
|
// Note: outbound material (root CAs, mTLS identity) is already applied in main.rs.
|
||||||
let tls_snapshot = TlsMaterialSnapshot::load(tls_path)
|
let tls_snapshot = TlsMaterialSnapshot::load(tls_path)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Error::other(e.to_string()))?;
|
.map_err(|e| Error::other(e.to_string()))?;
|
||||||
|
|
||||||
let tls_acceptor = tls_snapshot
|
let tls_acceptor = tls_snapshot.build_tls_acceptor(tls_path).await.map_err(|e| {
|
||||||
.build_tls_acceptor(tls_path)
|
if tls_path_configured {
|
||||||
.await
|
Error::other(format!(
|
||||||
.map_err(|e| Error::other(e.to_string()))?;
|
"TLS is explicitly configured via RUSTFS_TLS_PATH/tls_path='{}' but TLS acceptor initialization failed: {}",
|
||||||
|
tls_path, e
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Error::other(e.to_string())
|
||||||
|
}
|
||||||
|
})?;
|
||||||
let tls_enabled = tls_acceptor.is_some();
|
let tls_enabled = tls_acceptor.is_some();
|
||||||
let protocol = if tls_enabled { "https" } else { "http" };
|
let protocol = if tls_enabled { "https" } else { "http" };
|
||||||
|
|
||||||
|
|||||||
@@ -66,8 +66,6 @@ pub struct OutboundTlsMaterial {
|
|||||||
pub struct TlsMaterialSnapshot {
|
pub struct TlsMaterialSnapshot {
|
||||||
/// Material for outbound client connections.
|
/// Material for outbound client connections.
|
||||||
pub outbound: OutboundTlsMaterial,
|
pub outbound: OutboundTlsMaterial,
|
||||||
/// Whether any server certificates were found.
|
|
||||||
pub has_server_certs: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TlsMaterialSnapshot {
|
impl TlsMaterialSnapshot {
|
||||||
@@ -86,13 +84,7 @@ impl TlsMaterialSnapshot {
|
|||||||
// Load outbound material (root CAs + mTLS identity)
|
// Load outbound material (root CAs + mTLS identity)
|
||||||
let outbound = load_outbound_material(&tls_dir).await?;
|
let outbound = load_outbound_material(&tls_dir).await?;
|
||||||
|
|
||||||
// Check if server certs exist (actual loading happens in build_tls_acceptor)
|
Ok(Self { outbound })
|
||||||
let has_server_certs = has_server_certificates(tls_path).await;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
outbound,
|
|
||||||
has_server_certs,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply outbound material to global state (root CAs, mTLS identity).
|
/// Apply outbound material to global state (root CAs, mTLS identity).
|
||||||
@@ -110,7 +102,7 @@ impl TlsMaterialSnapshot {
|
|||||||
/// handling both multi-cert (SNI resolver) and single-cert fallback.
|
/// handling both multi-cert (SNI resolver) and single-cert fallback.
|
||||||
/// Returns `None` if no TLS certificates are available.
|
/// Returns `None` if no TLS certificates are available.
|
||||||
pub(crate) async fn build_tls_acceptor(&self, tls_path: &str) -> Result<Option<Arc<TlsAcceptorHolder>>, TlsMaterialError> {
|
pub(crate) async fn build_tls_acceptor(&self, tls_path: &str) -> Result<Option<Arc<TlsAcceptorHolder>>, TlsMaterialError> {
|
||||||
if tls_path.is_empty() || !self.has_server_certs {
|
if tls_path.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,21 +114,25 @@ impl TlsMaterialSnapshot {
|
|||||||
.map_err(|e| TlsMaterialError::Io(format!("build mTLS verifier: {e}")))?;
|
.map_err(|e| TlsMaterialError::Io(format!("build mTLS verifier: {e}")))?;
|
||||||
|
|
||||||
// Try multi-cert (SNI) first
|
// Try multi-cert (SNI) first
|
||||||
match rustfs_utils::load_all_certs_from_directory(
|
let multi_cert_error = match rustfs_utils::load_all_certs_from_directory(
|
||||||
rustfs_utils::CertDirectoryLoadOptions::builder(tls_path, RUSTFS_TLS_CERT, RUSTFS_TLS_KEY).build(),
|
rustfs_utils::CertDirectoryLoadOptions::builder(tls_path, RUSTFS_TLS_CERT, RUSTFS_TLS_KEY).build(),
|
||||||
) {
|
) {
|
||||||
Ok(cert_key_pairs) if !cert_key_pairs.is_empty() => match rustfs_utils::create_multi_cert_resolver(cert_key_pairs) {
|
Ok(cert_key_pairs) => match rustfs_utils::create_multi_cert_resolver(cert_key_pairs) {
|
||||||
Ok(resolver) => {
|
Ok(resolver) => {
|
||||||
let config = build_server_config(ServerCertSource::Resolver(Arc::new(resolver)), mtls_verifier)?;
|
let config = build_server_config(ServerCertSource::Resolver(Arc::new(resolver)), mtls_verifier)?;
|
||||||
info!("Created TLS acceptor with SNI resolver");
|
info!("Created TLS acceptor with SNI resolver");
|
||||||
let acceptor = Arc::new(TlsAcceptor::from(Arc::new(config)));
|
let acceptor = Arc::new(TlsAcceptor::from(Arc::new(config)));
|
||||||
return Ok(Some(Arc::new(TlsAcceptorHolder::new(acceptor))));
|
return Ok(Some(Arc::new(TlsAcceptorHolder::new(acceptor))));
|
||||||
}
|
}
|
||||||
Err(e) => warn!("Failed to build multi-cert resolver: {}, falling back to single-cert", e),
|
Err(e) => {
|
||||||
|
return Err(TlsMaterialError::Parse(format!("failed to build multi-cert resolver: {e}")));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Ok(_) => debug!("No valid multi-cert directory structure found"),
|
Err(e) => {
|
||||||
Err(_) => debug!("load_all_certs_from_directory failed, trying single-cert fallback"),
|
debug!("load_all_certs_from_directory failed, trying single-cert fallback");
|
||||||
}
|
Some(e.to_string())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Fallback: single cert
|
// Fallback: single cert
|
||||||
let key_path = format!("{tls_path}/{RUSTFS_TLS_KEY}");
|
let key_path = format!("{tls_path}/{RUSTFS_TLS_KEY}");
|
||||||
@@ -151,6 +147,13 @@ impl TlsMaterialSnapshot {
|
|||||||
return Ok(Some(Arc::new(TlsAcceptorHolder::new(acceptor))));
|
return Ok(Some(Arc::new(TlsAcceptorHolder::new(acceptor))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(err) = multi_cert_error {
|
||||||
|
return Err(TlsMaterialError::Io(format!(
|
||||||
|
"failed to discover TLS certificates under '{}': {}",
|
||||||
|
tls_path, err
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
debug!("No valid TLS certificates found, starting with HTTP");
|
debug!("No valid TLS certificates found, starting with HTTP");
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@@ -161,7 +164,6 @@ impl TlsMaterialSnapshot {
|
|||||||
root_ca_pem: Vec::new(),
|
root_ca_pem: Vec::new(),
|
||||||
mtls_identity: None,
|
mtls_identity: None,
|
||||||
},
|
},
|
||||||
has_server_certs: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,24 +278,6 @@ async fn load_outbound_material(tls_dir: &Path) -> Result<OutboundTlsMaterial, T
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Quick check whether server certificate files exist in the TLS directory.
|
|
||||||
async fn has_server_certificates(tls_path: &str) -> bool {
|
|
||||||
if tokio::fs::metadata(tls_path).await.is_err() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Check for multi-cert directory structure OR single cert files
|
|
||||||
if rustfs_utils::load_all_certs_from_directory(
|
|
||||||
rustfs_utils::CertDirectoryLoadOptions::builder(tls_path, RUSTFS_TLS_CERT, RUSTFS_TLS_KEY).build(),
|
|
||||||
)
|
|
||||||
.is_ok_and(|p| !p.is_empty())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
let key_path = format!("{tls_path}/{RUSTFS_TLS_KEY}");
|
|
||||||
let cert_path = format!("{tls_path}/{RUSTFS_TLS_CERT}");
|
|
||||||
tokio::try_join!(tokio::fs::metadata(&key_path), tokio::fs::metadata(&cert_path)).is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load mTLS client identity from the TLS directory.
|
/// Load mTLS client identity from the TLS directory.
|
||||||
async fn load_mtls_identity(tls_dir: &Path) -> Result<Option<MtlsIdentityPem>, TlsMaterialError> {
|
async fn load_mtls_identity(tls_dir: &Path) -> Result<Option<MtlsIdentityPem>, TlsMaterialError> {
|
||||||
let client_cert_path = match get_env_opt_str(ENV_MTLS_CLIENT_CERT) {
|
let client_cert_path = match get_env_opt_str(ENV_MTLS_CLIENT_CERT) {
|
||||||
|
|||||||
Reference in New Issue
Block a user