Keep managed MITM CA private keys in proxy memory (#29013)

## Why

The managed MITM trust bundle must be readable by sandboxed commands.
Persisting its sibling CA private key under `$CODEX_HOME/proxy`
therefore requires a deny-read sandbox rule, but the Windows unelevated
backend rejects deny-read paths and WSL1's legacy Landlock path cannot
enforce that rule.

A persistent OS credential store also does not provide the same
cross-platform boundary from other processes running as the same user.
Keeping the signer inside the network proxy process avoids both
problems: ordinary sandbox setup stays independent of CA-key state, and
no private signing key is exposed through the filesystem or a persistent
credential record.

## What

- generate one managed CA per proxy process and retain its private
signer only in proxy memory
- emit only content-addressed public CA certificates and trust bundles
under `$CODEX_HOME/proxy`
- hold a cross-process lease for each active public certificate and
prune artifacts from inactive proxy processes
- keep all CA ownership in `codex-network-proxy`; no `codex-core` or
sandbox-policy changes
- validate generated trust-bundle paths by their content hash
- keep the public bundle readable by sandboxed commands on Windows,
WSL1, macOS, and Linux

The independent startup custom-CA follow-up is #29014.

## Validation

- `CODEX_HOME=/private/tmp/codex-test-home-network-proxy just test -p
codex-network-proxy` (179 tests)
- `just bazel-lock-check`
- `just fix -p codex-network-proxy`
- `just fmt`

---------

Co-authored-by: viyatb-oai <viyatb@openai.com>
This commit is contained in:
Winston Howes
2026-06-23 12:20:51 -07:00
committed by GitHub
Unverified
parent 354807920e
commit c5a9a95ab6
3 changed files with 346 additions and 159 deletions
+3 -3
View File
@@ -34,9 +34,9 @@ allow_upstream_proxy = true
dangerously_allow_non_loopback_proxy = false
mode = "full" # default when unset; use "limited" for read-only mode
# HTTPS MITM is enabled automatically when `mode = "limited"` or when MITM hooks are configured.
# CA cert/key are managed internally under $CODEX_HOME/proxy/ (ca.pem + ca.key).
# When MITM is active, spawned commands receive CA bundle env vars pointing at
# immutable bundles under $CODEX_HOME/proxy/ so common HTTPS clients trust the managed CA.
# The CA private key remains in proxy memory. When MITM is active, spawned commands receive CA
# bundle env vars pointing at immutable public files under $CODEX_HOME/proxy/ so common HTTPS
# clients trust the managed CA.
# If false, local/private networking is rejected. Explicit allowlisting of local IP literals
# (or `localhost`) is required to permit them.
+341 -154
View File
@@ -32,6 +32,8 @@ use std::net::IpAddr;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::LazyLock;
use std::sync::Mutex;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
use tracing::info;
@@ -39,15 +41,60 @@ use tracing::warn;
pub(super) struct ManagedMitmCa {
issuer: Issuer<'static, KeyPair>,
certificate_path: PathBuf,
_artifact_lease: File,
}
static MANAGED_MITM_CAS: LazyLock<Mutex<HashMap<PathBuf, Arc<ManagedMitmCa>>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
impl ManagedMitmCa {
pub(super) fn load_or_create() -> Result<Self> {
let (ca_cert_pem, ca_key_pem) = load_or_create_ca()?;
let ca_key = KeyPair::from_pem(&ca_key_pem).context("failed to parse CA key")?;
let issuer: Issuer<'static, KeyPair> =
Issuer::from_ca_cert_pem(&ca_cert_pem, ca_key).context("failed to parse CA cert")?;
Ok(Self { issuer })
pub(super) fn load_or_create() -> Result<Arc<Self>> {
let proxy_dir = managed_ca_dir()?;
let mut managed_cas = MANAGED_MITM_CAS
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(ca) = managed_cas.get(&proxy_dir) {
return Ok(ca.clone());
}
let ca = Arc::new(Self::create(&proxy_dir)?);
managed_cas.insert(proxy_dir, ca.clone());
Ok(ca)
}
fn create(proxy_dir: &Path) -> Result<Self> {
fs::create_dir_all(proxy_dir)
.with_context(|| format!("failed to create {}", proxy_dir.display()))?;
let (certificate_pem, private_key) = generate_ca()?;
let artifact_lock = match lock_managed_ca_artifacts(proxy_dir) {
Ok(lock) => Some(lock),
Err(err) => {
warn!("failed to lock managed MITM CA artifacts; skipping pruning: {err}");
None
}
};
let certificate_path = persist_managed_ca_certificate(proxy_dir, &certificate_pem)?;
let issuer = Issuer::from_ca_cert_pem(&certificate_pem, private_key)
.context("failed to parse managed MITM CA certificate")?;
let artifact_lease = lock_managed_ca_certificate(&certificate_path)?;
if artifact_lock.is_some() {
prune_managed_ca_artifacts(proxy_dir);
}
info!(
cert_path = %certificate_path.display(),
"generated process-local MITM CA"
);
Ok(Self {
issuer,
certificate_path,
_artifact_lease: artifact_lease,
})
}
fn certificate_path(&self) -> &Path {
&self.certificate_path
}
pub(super) fn tls_acceptor_data_for_host(&self, host: &str) -> Result<TlsAcceptorData> {
@@ -100,8 +147,8 @@ fn issue_host_certificate_pem(
}
const MANAGED_MITM_CA_DIR: &str = "proxy";
const MANAGED_MITM_CA_CERT: &str = "ca.pem";
const MANAGED_MITM_CA_KEY: &str = "ca.key";
const MANAGED_MITM_CA_ARTIFACT_LOCK: &str = ".artifacts.lock";
const MANAGED_MITM_CA_CERT_PREFIX: &str = "ca";
const MANAGED_MITM_CA_TRUST_BUNDLE_PREFIX: &str = "ca-bundle";
pub(crate) const SSL_CERT_DIR_ENV_KEY: &str = "SSL_CERT_DIR";
@@ -135,22 +182,17 @@ pub(crate) struct ManagedMitmCaTrustBundle {
pub(crate) startup_env_values: HashMap<&'static str, String>,
}
fn managed_ca_paths() -> Result<(PathBuf, PathBuf)> {
fn managed_ca_dir() -> Result<PathBuf> {
let codex_home =
find_codex_home().context("failed to resolve CODEX_HOME for managed MITM CA")?;
let proxy_dir = codex_home.join(MANAGED_MITM_CA_DIR);
Ok((
proxy_dir.join(MANAGED_MITM_CA_CERT).to_path_buf(),
proxy_dir.join(MANAGED_MITM_CA_KEY).to_path_buf(),
))
Ok(codex_home.join(MANAGED_MITM_CA_DIR).to_path_buf())
}
pub(crate) fn managed_ca_trust_bundle(
env: &HashMap<&'static str, String>,
) -> Result<ManagedMitmCaTrustBundle> {
load_or_create_ca()?;
let (cert_path, _) = managed_ca_paths()?;
managed_ca_trust_bundle_for_cert_path(&cert_path, env)
let ca = ManagedMitmCa::load_or_create()?;
managed_ca_trust_bundle_for_cert_path(ca.certificate_path(), env)
}
fn managed_ca_trust_bundle_for_cert_path(
@@ -175,8 +217,8 @@ fn managed_ca_trust_bundle_for_cert_path(
pub(crate) fn upstream_tls_root_store(
env: &HashMap<&'static str, String>,
) -> Result<Arc<rustls::RootCertStore>> {
let (managed_ca_cert_path, _) = managed_ca_paths()?;
upstream_tls_root_store_for_cert_path(&managed_ca_cert_path, env)
let ca = ManagedMitmCa::load_or_create()?;
upstream_tls_root_store_for_cert_path(ca.certificate_path(), env)
}
pub(crate) fn upstream_tls_root_store_for_cert_path(
@@ -360,6 +402,9 @@ fn is_current_generated_trust_bundle_path(path: &Path, managed_ca_cert_path: &Pa
let Some(proxy_dir) = managed_ca_cert_path.parent() else {
return false;
};
if is_generated_trust_bundle_path(path, proxy_dir) {
return true;
}
let Some(file_name) = path.file_name().and_then(|file_name| file_name.to_str()) else {
return false;
};
@@ -381,12 +426,39 @@ fn is_current_generated_trust_bundle_path(path: &Path, managed_ca_cert_path: &Pa
.any(|window| window == managed_ca_cert)
}
/// Returns whether `path` points at a current Codex-generated MITM CA bundle.
pub fn is_managed_mitm_ca_trust_bundle_path(path: &str) -> bool {
let Ok((managed_ca_cert_path, _)) = managed_ca_paths() else {
fn is_generated_trust_bundle_path(path: &Path, proxy_dir: &Path) -> bool {
is_generated_managed_ca_artifact_path(path, proxy_dir, MANAGED_MITM_CA_TRUST_BUNDLE_PREFIX)
}
fn is_generated_managed_ca_artifact_path(path: &Path, proxy_dir: &Path, prefix: &str) -> bool {
let Some(file_name) = path.file_name().and_then(|file_name| file_name.to_str()) else {
return false;
};
is_current_generated_trust_bundle_path(Path::new(path), &managed_ca_cert_path)
let Some(expected_hash) = file_name
.strip_prefix(prefix)
.and_then(|suffix| suffix.strip_prefix('-'))
.and_then(|suffix| suffix.strip_suffix(".pem"))
else {
return false;
};
if path.parent() != Some(proxy_dir)
|| expected_hash.len() != 64
|| !expected_hash.bytes().all(|byte| byte.is_ascii_hexdigit())
{
return false;
}
let Ok(trust_bundle) = fs::read(path) else {
return false;
};
format!("{:x}", Sha256::digest(trust_bundle)) == expected_hash
}
/// Returns whether `path` points at a current Codex-generated MITM CA bundle.
pub fn is_managed_mitm_ca_trust_bundle_path(path: &str) -> bool {
let Ok(proxy_dir) = managed_ca_dir() else {
return false;
};
is_generated_trust_bundle_path(Path::new(path), &proxy_dir)
}
fn persist_managed_ca_trust_bundle(
@@ -439,56 +511,160 @@ fn push_certificate_pem(bundle: &mut String, der: &[u8]) {
bundle.push_str("-----END CERTIFICATE-----\n");
}
fn load_or_create_ca() -> Result<(String, String)> {
let (cert_path, key_path) = managed_ca_paths()?;
if cert_path.exists() || key_path.exists() {
if !cert_path.exists() || !key_path.exists() {
return Err(anyhow!(
"both managed MITM CA files must exist (cert={}, key={})",
cert_path.display(),
key_path.display()
));
}
validate_existing_ca_key_file(&key_path)?;
let cert_pem = fs::read_to_string(&cert_path)
.with_context(|| format!("failed to read CA cert {}", cert_path.display()))?;
let key_pem = fs::read_to_string(&key_path)
.with_context(|| format!("failed to read CA key {}", key_path.display()))?;
return Ok((cert_pem, key_pem));
}
if let Some(parent) = cert_path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("failed to create {}", parent.display()))?;
}
if let Some(parent) = key_path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("failed to create {}", parent.display()))?;
}
let (cert_pem, key_pem) = generate_ca()?;
// The CA key is a high-value secret. Create it atomically with restrictive permissions.
// The cert can be world-readable, but we still write it atomically to avoid partial writes.
//
// We intentionally use create-new semantics: if a key already exists, we should not overwrite
// it silently (that would invalidate previously-trusted cert chains).
write_atomic_create_new(&key_path, key_pem.as_bytes(), /*mode*/ 0o600)
.with_context(|| format!("failed to persist CA key {}", key_path.display()))?;
if let Err(err) = write_atomic_create_new(&cert_path, cert_pem.as_bytes(), /*mode*/ 0o644)
.with_context(|| format!("failed to persist CA cert {}", cert_path.display()))
{
// Avoid leaving a partially-created CA around (cert missing) if the second write fails.
let _ = fs::remove_file(&key_path);
return Err(err);
}
let cert_path = cert_path.display();
let key_path = key_path.display();
info!("generated MITM CA (cert_path={cert_path}, key_path={key_path})");
Ok((cert_pem, key_pem))
fn persist_managed_ca_certificate(proxy_dir: &Path, cert_pem: &str) -> Result<PathBuf> {
let hash = Sha256::digest(cert_pem.as_bytes());
let cert_path = proxy_dir.join(format!("{MANAGED_MITM_CA_CERT_PREFIX}-{hash:x}.pem"));
write_atomic_create_new_or_reuse(&cert_path, cert_pem.as_bytes(), /*mode*/ 0o644)
.with_context(|| {
format!(
"failed to persist managed MITM CA certificate {}",
cert_path.display()
)
})?;
Ok(cert_path)
}
fn generate_ca() -> Result<(String, String)> {
fn lock_managed_ca_certificate(certificate_path: &Path) -> Result<File> {
let lock_path = managed_ca_certificate_lock_path(certificate_path)
.ok_or_else(|| anyhow!("managed MITM CA certificate path is missing a file name"))?;
let file = open_managed_ca_lock(&lock_path)?;
file.lock_shared()
.with_context(|| format!("failed to lock {}", lock_path.display()))?;
Ok(file)
}
fn lock_managed_ca_artifacts(proxy_dir: &Path) -> Result<File> {
let lock_path = proxy_dir.join(MANAGED_MITM_CA_ARTIFACT_LOCK);
let file = open_managed_ca_lock(&lock_path)?;
file.lock()
.with_context(|| format!("failed to lock {}", lock_path.display()))?;
Ok(file)
}
fn managed_ca_certificate_lock_path(certificate_path: &Path) -> Option<PathBuf> {
let file_name = certificate_path.file_name()?.to_string_lossy();
Some(certificate_path.with_file_name(format!(".{file_name}.lock")))
}
fn open_managed_ca_lock(path: &Path) -> Result<File> {
if fs::symlink_metadata(path)
.ok()
.is_some_and(|metadata| metadata.file_type().is_symlink())
{
return Err(anyhow!(
"refusing to use symlink lock file {}",
path.display()
));
}
#[cfg(unix)]
use std::os::unix::fs::OpenOptionsExt;
let mut options = OpenOptions::new();
options.read(true).write(true).create(true).truncate(false);
#[cfg(unix)]
options.mode(0o600);
options
.open(path)
.with_context(|| format!("failed to open {}", path.display()))
}
fn prune_managed_ca_artifacts(proxy_dir: &Path) {
for certificate_path in
generated_managed_ca_artifact_paths(proxy_dir, MANAGED_MITM_CA_CERT_PREFIX)
{
remove_inactive_managed_ca_certificate(&certificate_path);
}
let remaining_certificates =
generated_managed_ca_artifact_paths(proxy_dir, MANAGED_MITM_CA_CERT_PREFIX)
.into_iter()
.filter_map(|path| fs::read(path).ok())
.filter(|certificate| !certificate.is_empty())
.collect::<Vec<_>>();
let bundle_paths =
generated_managed_ca_artifact_paths(proxy_dir, MANAGED_MITM_CA_TRUST_BUNDLE_PREFIX);
for bundle_path in bundle_paths {
let Ok(contents) = fs::read(&bundle_path) else {
continue;
};
if remaining_certificates.iter().any(|certificate| {
contents
.windows(certificate.len())
.any(|window| window == certificate)
}) {
continue;
}
if let Err(err) = fs::remove_file(&bundle_path)
&& err.kind() != std::io::ErrorKind::NotFound
{
warn!(
path = %bundle_path.display(),
"failed to prune stale managed MITM CA trust bundle: {err}"
);
}
}
}
fn generated_managed_ca_artifact_paths(proxy_dir: &Path, prefix: &str) -> Vec<PathBuf> {
let Ok(entries) = fs::read_dir(proxy_dir) else {
return Vec::new();
};
entries
.filter_map(std::result::Result::ok)
.filter_map(|entry| {
let path = entry.path();
if !is_generated_managed_ca_artifact_path(&path, proxy_dir, prefix) {
return None;
}
Some(path)
})
.collect()
}
fn remove_inactive_managed_ca_certificate(certificate_path: &Path) {
let Some(lock_path) = managed_ca_certificate_lock_path(certificate_path) else {
return;
};
let Ok(lock_file) = open_managed_ca_lock(&lock_path) else {
return;
};
match lock_file.try_lock() {
Ok(()) => {}
Err(std::fs::TryLockError::WouldBlock) => return,
Err(err) => {
warn!(
path = %lock_path.display(),
"failed to inspect managed MITM CA artifact lease: {err}"
);
return;
}
}
let removed = match fs::remove_file(certificate_path) {
Ok(()) => true,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => true,
Err(err) => {
warn!(
path = %certificate_path.display(),
"failed to prune stale managed MITM CA certificate: {err}"
);
false
}
};
drop(lock_file);
if removed
&& let Err(err) = fs::remove_file(&lock_path)
&& err.kind() != std::io::ErrorKind::NotFound
{
warn!(
path = %lock_path.display(),
"failed to prune stale managed MITM CA artifact lease: {err}"
);
}
}
fn generate_ca() -> Result<(String, KeyPair)> {
let mut params = CertificateParams::default();
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
params.key_usages = vec![
@@ -505,7 +681,7 @@ fn generate_ca() -> Result<(String, String)> {
let cert = params
.self_signed(&key_pair)
.map_err(|err| anyhow!("failed to generate CA cert: {err}"))?;
Ok((cert.pem(), key_pair.serialize_pem()))
Ok((cert.pem(), key_pair))
}
fn write_atomic_create_new(path: &Path, contents: &[u8], mode: u32) -> Result<()> {
@@ -604,41 +780,6 @@ fn write_atomic_create_new_or_reuse(path: &Path, contents: &[u8], mode: u32) ->
}
}
#[cfg(unix)]
fn validate_existing_ca_key_file(path: &Path) -> Result<()> {
use std::os::unix::fs::PermissionsExt;
let metadata = fs::symlink_metadata(path)
.with_context(|| format!("failed to stat CA key {}", path.display()))?;
if metadata.file_type().is_symlink() {
return Err(anyhow!(
"refusing to use symlink for managed MITM CA key {}",
path.display()
));
}
if !metadata.is_file() {
return Err(anyhow!(
"managed MITM CA key is not a regular file: {}",
path.display()
));
}
let mode = metadata.permissions().mode() & 0o777;
if mode & 0o077 != 0 {
return Err(anyhow!(
"managed MITM CA key {} must not be group/world accessible (mode={mode:o}; expected <= 600)",
path.display()
));
}
Ok(())
}
#[cfg(not(unix))]
fn validate_existing_ca_key_file(_path: &Path) -> Result<()> {
Ok(())
}
#[cfg(unix)]
fn open_create_new_with_mode(path: &Path, mode: u32) -> Result<File> {
use std::os::unix::fs::OpenOptionsExt;
@@ -664,12 +805,83 @@ fn open_create_new_with_mode(path: &Path, _mode: u32) -> Result<File> {
mod tests {
use super::*;
#[cfg(unix)]
use codex_utils_rustls_provider::ensure_rustls_crypto_provider;
use pretty_assertions::assert_eq;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use tempfile::tempdir;
#[test]
fn managed_ca_private_key_is_not_persisted() {
ensure_rustls_crypto_provider();
let dir = tempdir().unwrap();
let ca = ManagedMitmCa::create(dir.path()).unwrap();
ca.tls_acceptor_data_for_host("example.com").unwrap();
let mut persisted_files = fs::read_dir(dir.path())
.unwrap()
.map(|entry| entry.unwrap().path())
.collect::<Vec<_>>();
persisted_files.sort();
let mut expected_files = vec![
ca.certificate_path().to_path_buf(),
managed_ca_certificate_lock_path(ca.certificate_path()).unwrap(),
dir.path().join(MANAGED_MITM_CA_ARTIFACT_LOCK),
];
expected_files.sort();
assert_eq!(persisted_files, expected_files);
assert_eq!(
fs::read(managed_ca_certificate_lock_path(ca.certificate_path()).unwrap()).unwrap(),
Vec::<u8>::new()
);
}
#[test]
fn managed_ca_artifact_pruning_preserves_only_active_certificates() {
let dir = tempdir().unwrap();
let mut artifacts = Vec::new();
let mut active_lease = None;
for index in 0..3 {
let certificate = format!("certificate {index}\n");
let certificate_path =
persist_managed_ca_certificate(dir.path(), &certificate).unwrap();
let lease = lock_managed_ca_certificate(&certificate_path).unwrap();
if index == 0 {
active_lease = Some(lease);
} else {
drop(lease);
}
let bundle_path = persist_managed_ca_trust_bundle(
&certificate_path,
&format!("roots\n{certificate}"),
)
.unwrap();
artifacts.push((certificate_path, bundle_path));
}
let unrelated_path = dir.path().join("ca-user.pem");
fs::write(&unrelated_path, "user managed").unwrap();
prune_managed_ca_artifacts(dir.path());
let remaining_certificate_count =
generated_managed_ca_artifact_paths(dir.path(), MANAGED_MITM_CA_CERT_PREFIX).len();
assert_eq!(remaining_certificate_count, 1);
assert!(artifacts[0].0.exists());
assert!(artifacts[0].1.exists());
assert!(!artifacts[1].0.exists());
assert!(!artifacts[1].1.exists());
assert!(!artifacts[2].0.exists());
assert!(!artifacts[2].1.exists());
assert!(unrelated_path.exists());
drop(active_lease.take());
prune_managed_ca_artifacts(dir.path());
let remaining_certificates =
generated_managed_ca_artifact_paths(dir.path(), MANAGED_MITM_CA_CERT_PREFIX);
assert!(remaining_certificates.is_empty());
assert!(!artifacts[0].0.exists());
assert!(!artifacts[0].1.exists());
}
#[test]
fn current_generated_trust_bundle_path_rejects_stale_bundle() {
let dir = tempdir().unwrap();
@@ -683,6 +895,24 @@ mod tests {
));
}
#[test]
fn generated_trust_bundle_path_requires_matching_content_hash() {
let dir = tempdir().unwrap();
let managed_ca_cert_path = dir.path().join("ca.pem");
let trust_bundle_path =
persist_managed_ca_trust_bundle(&managed_ca_cert_path, "trusted roots").unwrap();
assert!(is_generated_trust_bundle_path(
&trust_bundle_path,
dir.path()
));
fs::write(&trust_bundle_path, "tampered roots").unwrap();
assert!(!is_generated_trust_bundle_path(
&trust_bundle_path,
dir.path()
));
}
#[test]
fn managed_ca_trust_bundle_appends_startup_file_and_directory_certificates() {
let dir = tempdir().unwrap();
@@ -691,6 +921,7 @@ mod tests {
let startup_ca_dir = dir.path().join("startup-certs");
let (managed_ca_cert, _) = generate_ca().unwrap();
let (startup_ca_cert, startup_ca_key) = generate_ca().unwrap();
let startup_ca_key = startup_ca_key.serialize_pem();
let (directory_ca_cert, _) = generate_ca().unwrap();
let mut trusted_ca_der = CertificateDer::from_pem_slice(startup_ca_cert.as_bytes())
.unwrap()
@@ -760,50 +991,6 @@ mod tests {
assert_eq!(baseline_bundle.matches(&managed_ca_cert).count(), 1);
}
#[cfg(unix)]
#[test]
fn validate_existing_ca_key_file_rejects_group_world_permissions() {
let dir = tempdir().unwrap();
let key_path = dir.path().join("ca.key");
fs::write(&key_path, "key").unwrap();
fs::set_permissions(&key_path, fs::Permissions::from_mode(0o644)).unwrap();
let err = validate_existing_ca_key_file(&key_path).unwrap_err();
assert!(
err.to_string().contains("group/world accessible"),
"unexpected error: {err:#}"
);
}
#[cfg(unix)]
#[test]
fn validate_existing_ca_key_file_rejects_symlink() {
use std::os::unix::fs::symlink;
let dir = tempdir().unwrap();
let target = dir.path().join("real.key");
let link = dir.path().join("ca.key");
fs::write(&target, "key").unwrap();
symlink(&target, &link).unwrap();
let err = validate_existing_ca_key_file(&link).unwrap_err();
assert!(
err.to_string().contains("symlink"),
"unexpected error: {err:#}"
);
}
#[cfg(unix)]
#[test]
fn validate_existing_ca_key_file_allows_private_permissions() {
let dir = tempdir().unwrap();
let key_path = dir.path().join("ca.key");
fs::write(&key_path, "key").unwrap();
fs::set_permissions(&key_path, fs::Permissions::from_mode(0o600)).unwrap();
validate_existing_ca_key_file(&key_path).unwrap();
}
#[cfg(unix)]
#[test]
fn write_atomic_create_new_or_reuse_rejects_matching_symlink_target() {
+2 -2
View File
@@ -53,7 +53,7 @@ use tracing::warn;
/// State needed to terminate a CONNECT tunnel and enforce policy on inner HTTPS requests.
pub struct MitmState {
ca: ManagedMitmCa,
ca: Arc<ManagedMitmCa>,
upstream: UpstreamClient,
inspect: bool,
max_body_bytes: usize,
@@ -104,7 +104,7 @@ impl MitmState {
// MITM exists when HTTPS policy depends on the inner request: limited-mode method clamps
// and host-specific hooks both need visibility after CONNECT is established. We
// generate/load a local CA and issue per-host leaf certs so we can terminate TLS and
// generate a process-local CA and issue per-host leaf certs so we can terminate TLS and
// apply policy.
let ca = ManagedMitmCa::load_or_create()?;
let upstream_tls_root_store =