Skip to content

Commit 0224f94

Browse files
committed
fix(network): harden additional CA propagation
Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>
1 parent e6295ff commit 0224f94

21 files changed

Lines changed: 1041 additions & 3224 deletions

File tree

‎.rpi/tasks/network-supervisor-additional-ca/01-research-questions.md‎

Lines changed: 0 additions & 37 deletions
This file was deleted.

‎.rpi/tasks/network-supervisor-additional-ca/02-research.md‎

Lines changed: 0 additions & 155 deletions
This file was deleted.

‎.rpi/tasks/network-supervisor-additional-ca/03-design-discussion.md‎

Lines changed: 0 additions & 527 deletions
This file was deleted.

‎.rpi/tasks/network-supervisor-additional-ca/04-structure-outline.md‎

Lines changed: 0 additions & 1114 deletions
This file was deleted.

‎.rpi/tasks/network-supervisor-additional-ca/05-implementation.md‎

Lines changed: 0 additions & 1244 deletions
This file was deleted.

‎.rpi/tasks/network-supervisor-additional-ca/state.json‎

Lines changed: 0 additions & 30 deletions
This file was deleted.

‎.rpi/tasks/network-supervisor-additional-ca/task.md‎

Lines changed: 0 additions & 46 deletions
This file was deleted.

‎crates/openshell-core/src/network_trust.rs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ use std::fmt;
1212
use std::path::{Path, PathBuf};
1313
use std::sync::Arc;
1414

15+
/// Maximum number of normalized PEM bytes accepted for destination trust.
16+
///
17+
/// This bound is shared by the gateway normalization boundary and all compute
18+
/// driver consumers. Kubernetes counts the `ca.crt` key and value toward its
19+
/// 1 MiB `ConfigMap` data limit, so reserve the six key bytes here to keep every
20+
/// supported driver on the same deployable boundary.
21+
pub const MAX_NETWORK_SUPERVISOR_TRUST_BUNDLE_BYTES: usize = 1024 * 1024 - "ca.crt".len();
22+
1523
/// Normalized, gateway-owned trust material for sandbox destination TLS.
1624
///
1725
/// The PEM bytes contain only canonical X.509 certificate blocks. The type is

‎crates/openshell-gateway/src/lib.rs‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ impl openshell_server::ComputeDriverFactory for KubernetesFactory {
240240
true
241241
}
242242

243+
fn supports_network_supervisor_trust(&self) -> bool {
244+
true
245+
}
246+
243247
fn validate_config(
244248
&self,
245249
context: openshell_server::ComputeDriverConfigContext<'_>,
@@ -294,6 +298,10 @@ impl openshell_server::ComputeDriverFactory for DockerFactory {
294298
true
295299
}
296300

301+
fn supports_network_supervisor_trust(&self) -> bool {
302+
true
303+
}
304+
297305
fn validate_config(
298306
&self,
299307
context: openshell_server::ComputeDriverConfigContext<'_>,
@@ -342,6 +350,10 @@ impl openshell_server::ComputeDriverFactory for PodmanFactory {
342350
true
343351
}
344352

353+
fn supports_network_supervisor_trust(&self) -> bool {
354+
true
355+
}
356+
345357
fn validate_config(
346358
&self,
347359
context: openshell_server::ComputeDriverConfigContext<'_>,
@@ -407,6 +419,10 @@ impl openshell_server::ComputeDriverFactory for VmFactory {
407419
true
408420
}
409421

422+
fn supports_network_supervisor_trust(&self) -> bool {
423+
true
424+
}
425+
410426
fn validate_config(
411427
&self,
412428
context: openshell_server::ComputeDriverConfigContext<'_>,
@@ -642,4 +658,17 @@ mod tests {
642658
expected
643659
);
644660
}
661+
662+
#[cfg(not(target_os = "windows"))]
663+
#[test]
664+
fn first_party_network_supervisor_drivers_opt_into_trust_propagation() {
665+
#[cfg(feature = "compute-driver-docker")]
666+
assert!(<DockerFactory as openshell_server::ComputeDriverFactory>::supports_network_supervisor_trust(&DockerFactory));
667+
#[cfg(feature = "compute-driver-kubernetes")]
668+
assert!(<KubernetesFactory as openshell_server::ComputeDriverFactory>::supports_network_supervisor_trust(&KubernetesFactory));
669+
#[cfg(feature = "compute-driver-podman")]
670+
assert!(<PodmanFactory as openshell_server::ComputeDriverFactory>::supports_network_supervisor_trust(&PodmanFactory));
671+
#[cfg(feature = "compute-driver-vm")]
672+
assert!(<VmFactory as openshell_server::ComputeDriverFactory>::supports_network_supervisor_trust(&VmFactory));
673+
}
645674
}

‎crates/openshell-sandbox/src/process.rs‎

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ impl ProcessHandle {
478478
interactive: bool,
479479
policy: &SandboxPolicy,
480480
ca_paths: Option<&(PathBuf, PathBuf)>,
481+
tls_environment_mode: child_env::TlsEnvironmentMode,
481482
provider_env: &HashMap<String, String>,
482483
) -> Result<Self> {
483484
Self::spawn_impl(
@@ -488,6 +489,7 @@ impl ProcessHandle {
488489
interactive,
489490
policy,
490491
ca_paths,
492+
tls_environment_mode,
491493
provider_env,
492494
)
493495
}
@@ -506,6 +508,7 @@ impl ProcessHandle {
506508
interactive: bool,
507509
policy: &SandboxPolicy,
508510
ca_paths: Option<&(PathBuf, PathBuf)>,
511+
tls_environment_mode: child_env::TlsEnvironmentMode,
509512
provider_env: &HashMap<String, String>,
510513
) -> Result<Self> {
511514
Self::spawn_impl(
@@ -515,6 +518,7 @@ impl ProcessHandle {
515518
interactive,
516519
policy,
517520
ca_paths,
521+
tls_environment_mode,
518522
provider_env,
519523
)
520524
}
@@ -529,6 +533,7 @@ impl ProcessHandle {
529533
interactive: bool,
530534
policy: &SandboxPolicy,
531535
ca_paths: Option<&(PathBuf, PathBuf)>,
536+
tls_environment_mode: child_env::TlsEnvironmentMode,
532537
provider_env: &HashMap<String, String>,
533538
) -> Result<Self> {
534539
let mut cmd = Command::new(program);
@@ -563,12 +568,13 @@ impl ProcessHandle {
563568
// inherited environment. The entrypoint drops to the sandbox user
564569
// before `exec`; without this strip, sandbox code could recover
565570
// supervisor credentials from its inherited environment.
571+
let user_environment = configured_user_environment();
566572
apply_canonical_process_environment(
567573
&mut cmd,
568574
policy,
569575
workspace,
570576
interactive,
571-
&configured_user_environment(),
577+
&user_environment,
572578
);
573579
strip_supervisor_only_env(&mut cmd);
574580

@@ -580,9 +586,28 @@ impl ProcessHandle {
580586

581587
strip_proxy_env(&mut cmd);
582588

583-
// Set TLS trust store env vars so sandbox processes trust the ephemeral CA
589+
// In interception mode OpenShell trust is authoritative. In direct
590+
// additional-CA mode, retain all caller values and only fill gaps.
584591
if let Some((ca_cert_path, combined_bundle_path)) = ca_paths {
585-
for (key, value) in child_env::tls_env_vars(ca_cert_path, combined_bundle_path) {
592+
// Provider credentials are injected before this point. Reassert
593+
// caller TLS values in additive mode so a provider environment
594+
// cannot accidentally replace a caller-selected trust store.
595+
if matches!(
596+
tls_environment_mode,
597+
child_env::TlsEnvironmentMode::FillMissing
598+
) {
599+
for (key, value) in &user_environment {
600+
if child_env::is_tls_environment_variable(key) {
601+
cmd.env(key, value);
602+
}
603+
}
604+
}
605+
for (key, value) in child_env::tls_env_vars_for_mode(
606+
ca_cert_path,
607+
combined_bundle_path,
608+
tls_environment_mode,
609+
&user_environment,
610+
) {
586611
cmd.env(key, value);
587612
}
588613
}
@@ -694,6 +719,7 @@ impl ProcessHandle {
694719
interactive: bool,
695720
policy: &SandboxPolicy,
696721
ca_paths: Option<&(PathBuf, PathBuf)>,
722+
tls_environment_mode: child_env::TlsEnvironmentMode,
697723
provider_env: &HashMap<String, String>,
698724
) -> Result<Self> {
699725
let mut cmd = Command::new(program);
@@ -728,12 +754,13 @@ impl ProcessHandle {
728754

729755
// Strip supervisor-only identity material from the entrypoint's
730756
// inherited environment.
757+
let user_environment = configured_user_environment();
731758
apply_canonical_process_environment(
732759
&mut cmd,
733760
policy,
734761
workspace,
735762
interactive,
736-
&configured_user_environment(),
763+
&user_environment,
737764
);
738765
strip_supervisor_only_env(&mut cmd);
739766

@@ -745,9 +772,28 @@ impl ProcessHandle {
745772

746773
strip_proxy_env(&mut cmd);
747774

748-
// Set TLS trust store env vars so sandbox processes trust the ephemeral CA
775+
// In interception mode OpenShell trust is authoritative. In direct
776+
// additional-CA mode, retain all caller values and only fill gaps.
749777
if let Some((ca_cert_path, combined_bundle_path)) = ca_paths {
750-
for (key, value) in child_env::tls_env_vars(ca_cert_path, combined_bundle_path) {
778+
// Provider credentials are injected before this point. Reassert
779+
// caller TLS values in additive mode so a provider environment
780+
// cannot accidentally replace a caller-selected trust store.
781+
if matches!(
782+
tls_environment_mode,
783+
child_env::TlsEnvironmentMode::FillMissing
784+
) {
785+
for (key, value) in &user_environment {
786+
if child_env::is_tls_environment_variable(key) {
787+
cmd.env(key, value);
788+
}
789+
}
790+
}
791+
for (key, value) in child_env::tls_env_vars_for_mode(
792+
ca_cert_path,
793+
combined_bundle_path,
794+
tls_environment_mode,
795+
&user_environment,
796+
) {
751797
cmd.env(key, value);
752798
}
753799
}

0 commit comments

Comments
 (0)