mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
## Why Concurrent TLS client builds can independently load the same platform trust store. Share overlapping loads while preserving fresh trust reads for later builds. ## What changed - Share one in-flight native root load without reusing completed loads. - Bypass sharing when `SSL_CERT_FILE` or `SSL_CERT_DIR` is set, and continue reading custom CA bundles independently for every build. ## Testing Add tests for overlapping loads, fresh trust after completion, and recovery from a panicking loader. Extend TLS handshake coverage to verify that replacing a custom CA file affects new configurations while existing configurations retain their trust, and that empty or removed files produce errors. GitOrigin-RevId: 1465ef3faf82f53651d9dbfe314341b17346580b
101 lines
3.5 KiB
Rust
101 lines
3.5 KiB
Rust
//! Handshake and fresh-file-read coverage for shared custom trust configuration.
|
|
|
|
use super::CODEX_CA_CERT_ENV;
|
|
use super::ConfiguredCaBundle;
|
|
use super::build_rustls_client_config;
|
|
use pretty_assertions::assert_eq;
|
|
use rcgen::BasicConstraints;
|
|
use rcgen::CertificateParams;
|
|
use rcgen::CertifiedIssuer;
|
|
use rcgen::IsCa;
|
|
use rcgen::KeyPair;
|
|
use std::sync::Arc;
|
|
|
|
#[test]
|
|
fn custom_intermediate_trust_preserves_hostname_validation() {
|
|
let mut params = CertificateParams::default();
|
|
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
|
|
let root = CertifiedIssuer::self_signed(params.clone(), KeyPair::generate().unwrap()).unwrap();
|
|
let intermediate =
|
|
CertifiedIssuer::signed_by(params, KeyPair::generate().unwrap(), &root).unwrap();
|
|
let leaf_key = KeyPair::generate().unwrap();
|
|
let leaf = CertificateParams::new(vec!["localhost".to_string()])
|
|
.unwrap()
|
|
.signed_by(&leaf_key, &intermediate)
|
|
.unwrap();
|
|
let temp = tempfile::TempDir::new().unwrap();
|
|
let path = temp.path().join("intermediate.pem");
|
|
std::fs::write(&path, intermediate.pem()).unwrap();
|
|
let bundle = ConfiguredCaBundle {
|
|
source_env: CODEX_CA_CERT_ENV,
|
|
path,
|
|
};
|
|
let config = build_rustls_client_config(Some(&bundle)).unwrap();
|
|
let server = Arc::new(
|
|
rustls::ServerConfig::builder()
|
|
.with_no_client_auth()
|
|
.with_single_cert(vec![leaf.der().clone()], leaf_key.into())
|
|
.unwrap(),
|
|
);
|
|
|
|
assert_eq!(
|
|
handshake(config.clone(), server.clone(), "localhost"),
|
|
Ok(())
|
|
);
|
|
let error = handshake(config.clone(), server.clone(), "wrong.example").unwrap_err();
|
|
assert!(
|
|
matches!(
|
|
error,
|
|
rustls::Error::InvalidCertificate(
|
|
rustls::CertificateError::NotValidForName
|
|
| rustls::CertificateError::NotValidForNameContext { .. }
|
|
)
|
|
),
|
|
"{error:?}"
|
|
);
|
|
|
|
std::fs::write(&bundle.path, include_str!("../tests/fixtures/test-ca.pem")).unwrap();
|
|
let replaced = build_rustls_client_config(Some(&bundle)).unwrap();
|
|
assert!(matches!(
|
|
handshake(replaced, server.clone(), "localhost"),
|
|
Err(rustls::Error::InvalidCertificate(
|
|
rustls::CertificateError::UnknownIssuer
|
|
))
|
|
));
|
|
assert_eq!(handshake(config, server, "localhost"), Ok(()));
|
|
|
|
std::fs::write(&bundle.path, "").unwrap();
|
|
assert!(matches!(
|
|
build_rustls_client_config(Some(&bundle)),
|
|
Err(super::BuildCustomCaTransportError::InvalidCaFile { .. })
|
|
));
|
|
std::fs::remove_file(&bundle.path).unwrap();
|
|
assert!(matches!(
|
|
build_rustls_client_config(Some(&bundle)),
|
|
Err(super::BuildCustomCaTransportError::ReadCaFile { .. })
|
|
));
|
|
}
|
|
|
|
fn handshake(
|
|
config: Arc<rustls::ClientConfig>,
|
|
server: Arc<rustls::ServerConfig>,
|
|
hostname: &'static str,
|
|
) -> Result<(), rustls::Error> {
|
|
let mut client = rustls::ClientConnection::new(config, hostname.try_into().unwrap()).unwrap();
|
|
let mut server = rustls::ServerConnection::new(server).unwrap();
|
|
for _ in 0..10 {
|
|
let mut bytes = Vec::new();
|
|
client.write_tls(&mut bytes).unwrap();
|
|
server.read_tls(&mut bytes.as_slice()).unwrap();
|
|
server.process_new_packets()?;
|
|
bytes.clear();
|
|
server.write_tls(&mut bytes).unwrap();
|
|
client.read_tls(&mut bytes.as_slice()).unwrap();
|
|
client.process_new_packets()?;
|
|
if !client.is_handshaking() {
|
|
return Ok(());
|
|
}
|
|
}
|
|
panic!("handshake did not complete");
|
|
}
|