fix: grpc use native/webpki trust store (#11083)

This commit is contained in:
udev
2026-03-31 11:28:53 +05:30
committed by GitHub
parent 881cdfb03f
commit 9525aad6fe
5 changed files with 85 additions and 31 deletions
Generated
+1
View File
@@ -11169,6 +11169,7 @@ dependencies = [
"hyper-util",
"percent-encoding",
"pin-project",
"rustls-native-certs 0.8.3",
"socket2 0.6.2",
"sync_wrapper",
"tokio",
+1 -1
View File
@@ -446,7 +446,7 @@ tokio-rustls = { version = "0.26", default-features = false, features = [
] }
tokio-util = { version = "0.7", features = ["compat"] }
tokio-stream = "0.1"
tonic = { version = "0.14", features = ["gzip", "tls-webpki-roots"] }
tonic = { version = "0.14", features = ["gzip", "tls-webpki-roots", "tls-native-roots"] }
tonic-prost = "0.14"
tracing = "0.1.44"
tracing-appender = "0.2.4"
+41 -1
View File
@@ -715,10 +715,11 @@ pub struct Http {
pub tls_min_version: String,
#[env_config(
name = "ZO_HTTP_TLS_ROOT_CERTIFICATES",
parse,
default = "webpki",
help = "this value must use webpki or native. it means use standard root certificates from webpki-roots or native-roots as a rustls certificate store"
)]
pub tls_root_certificates: String,
pub tls_root_certificates: TlsRootCertificates,
#[env_config(
name = "ZO_HTTP_ACCESS_LOG_FORMAT",
default = "",
@@ -757,6 +758,45 @@ pub struct Grpc {
pub tls_cert_path: String,
#[env_config(name = "ZO_GRPC_TLS_KEY_PATH", default = "")]
pub tls_key_path: String,
#[env_config(
name = "ZO_GRPC_TLS_ROOT_CERTIFICATES",
parse,
default = "webpki",
help = "this value can be set to webpki or native. Using webpki means client will trust a preset CA bundle. Using native means client will trust the certificates in OS trust store"
)]
pub tls_root_certificates: TlsRootCertificates,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum TlsRootCertificates {
#[default]
Webpki,
Native,
}
impl std::fmt::Display for TlsRootCertificates {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Webpki => write!(f, "webpki"),
Self::Native => write!(f, "native"),
}
}
}
impl std::str::FromStr for TlsRootCertificates {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"webpki" => Ok(Self::Webpki),
"native" => Ok(Self::Native),
_ => Err(anyhow::anyhow!(
"Invalid tls_root_certificates value: '{}'. Must be 'webpki' or 'native'",
s
)),
}
}
}
#[derive(Serialize, PartialEq, Default)]
+14 -5
View File
@@ -77,11 +77,20 @@ pub async fn create_channel(grpc_addr: &str) -> Result<Channel, tonic::Status> {
Status::internal("parse gRPC node error".to_string())
})?;
if cfg.grpc.tls_enabled {
let pem = std::fs::read_to_string(&cfg.grpc.tls_cert_path)?;
let cert = Certificate::from_pem(pem);
let tls = ClientTlsConfig::new()
.ca_certificate(cert)
.domain_name(&cfg.grpc.tls_cert_domain);
let mut tls = ClientTlsConfig::new().domain_name(&cfg.grpc.tls_cert_domain);
match cfg.grpc.tls_root_certificates {
config::TlsRootCertificates::Native => {
tls = tls.with_native_roots();
}
config::TlsRootCertificates::Webpki => {
tls = tls.with_webpki_roots();
if !cfg.grpc.tls_cert_path.is_empty() {
let pem = std::fs::read_to_string(&cfg.grpc.tls_cert_path)?;
let cert = Certificate::from_pem(pem);
tls = tls.ca_certificates([cert]);
}
}
}
channel = channel.tls_config(tls).map_err(|err| {
log::error!("gRPC node: {}, tls err: {:?}", &grpc_addr, err);
Status::internal("tls gRPC node error".to_string())
+28 -24
View File
@@ -62,32 +62,36 @@ pub fn http_tls_config() -> Result<rustls::ServerConfig, anyhow::Error> {
pub fn client_tls_config() -> Result<rustls::ClientConfig, anyhow::Error> {
let cfg = config::get_config();
let cert_store = if cfg.http.tls_root_certificates.as_str().to_lowercase() == "native" {
// Load native system certificates
let mut cert_store = rustls::RootCertStore::empty();
let certs = rustls_native_certs::load_native_certs();
for cert in certs.certs {
cert_store.add(cert)?;
let cert_store = match cfg.http.tls_root_certificates {
config::TlsRootCertificates::Native => {
let mut cert_store = rustls::RootCertStore::empty();
let native_certs = rustls_native_certs::load_native_certs();
for cert in native_certs.certs {
cert_store.add(cert)?;
}
if let Some(err) = native_certs.errors.first() {
log::warn!("Failed to load some native certificates: {}", err);
}
cert_store
}
if let Some(err) = certs.errors.first() {
log::warn!("Failed to load some native certificates: {}", err);
config::TlsRootCertificates::Webpki => {
let mut cert_store = rustls::RootCertStore::empty();
cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
if !cfg.http.tls_cert_path.is_empty() {
let cert_file = &mut BufReader::new(
std::fs::File::open(&cfg.http.tls_cert_path).map_err(|e| {
anyhow::anyhow!(
"Failed to open TLS certificate file {}: {}",
&cfg.http.tls_cert_path,
e
)
})?,
);
let cert_chain = certs(cert_file);
cert_store.add_parsable_certificates(cert_chain.try_collect::<_, Vec<_>, _>()?);
}
cert_store
}
cert_store
} else {
// default use webpki, and add custom ca certificates
let mut cert_store = rustls::RootCertStore::empty();
cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let cert_file =
&mut BufReader::new(std::fs::File::open(&cfg.http.tls_cert_path).map_err(|e| {
anyhow::anyhow!(
"Failed to open TLS certificate file {}: {}",
&cfg.http.tls_cert_path,
e
)
})?);
let cert_chain = certs(cert_file);
cert_store.add_parsable_certificates(cert_chain.try_collect::<_, Vec<_>, _>()?);
cert_store
};
let mut config = rustls::ClientConfig::builder()