Skip to content

Commit e492342

Browse files
committed
fix(kubernetes): refresh sidecar provider env snapshots
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
1 parent 4904e44 commit e492342

7 files changed

Lines changed: 271 additions & 31 deletions

File tree

.agents/skills/debug-openshell-cluster/SKILL.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,11 @@ sandbox token file, or those credential mounts. Instead, the network sidecar
285285
writes `/run/openshell-sidecar/policy.pb` and
286286
`/run/openshell-sidecar/provider-env.json`, then writes the readiness file. If
287287
the process supervisor fails before launching the workload, inspect those
288-
snapshot files and the network sidecar logs.
288+
snapshot files and the network sidecar logs. If new SSH/exec sessions do not
289+
pick up refreshed provider environment, inspect the provider-env snapshot
290+
revision and network sidecar settings-poll logs; the process container should
291+
consume newer provider-env snapshot revisions without receiving gateway
292+
credentials.
289293

290294
The process container should also publish the workload entrypoint PID to
291295
`OPENSHELL_ENTRYPOINT_PID_FILE`

architecture/compute-runtimes.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,17 @@ session in a dedicated sidecar, while the agent container runs only the
9696
process-supervision leaf and launches the user workload after the sidecar
9797
signals readiness. The sidecar writes local policy and provider-environment
9898
snapshots into shared state so the process leaf can start without gateway
99-
credentials. In sidecar mode, an init container performs the privileged
100-
pod-network nftables setup with `NET_ADMIN` and hands shared state ownership to
101-
the configured proxy UID; the long-running network sidecar runs as that UID and
102-
does not keep `NET_ADMIN`. The agent container runs as the resolved sandbox
103-
UID/GID with no added Linux capabilities. Sidecar mode preserves gateway session
104-
and SSH behavior, but treats the process leaf as network-only by default:
105-
Landlock filesystem policy, process privilege dropping, and process/binary
106-
identity checks are not applied there.
99+
credentials. The network sidecar also refreshes the workload-facing provider
100+
environment snapshot after settings polls so future process sessions see
101+
updated provider env without giving the process leaf gateway access. In sidecar
102+
mode, an init container performs the privileged pod-network nftables setup with
103+
`NET_ADMIN` and hands shared state ownership to the configured proxy UID; the
104+
long-running network sidecar runs as that UID and does not keep `NET_ADMIN`.
105+
The agent container runs as the resolved sandbox UID/GID with no added Linux
106+
capabilities. Sidecar mode preserves gateway session and SSH behavior, but
107+
treats the process leaf as network-only by default: Landlock filesystem policy,
108+
process privilege dropping, and process/binary identity checks are not applied
109+
there.
107110

108111
## Images
109112

crates/openshell-core/src/provider_credentials.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,37 @@ impl ProviderCredentialState {
8989
}
9090
}
9191

92+
/// Install an already-prepared child environment snapshot.
93+
///
94+
/// This is intentionally narrower than [`Self::install_environment`]: it
95+
/// updates only the workload-facing env map and clears resolver state so a
96+
/// process that does not own gateway/provider resolver material can still
97+
/// pick up refreshed provider env for future child processes.
98+
pub fn install_child_env_snapshot(
99+
&self,
100+
revision: u64,
101+
mut child_env: HashMap<String, String>,
102+
) -> usize {
103+
let mut inner = self
104+
.inner
105+
.write()
106+
.expect("provider credential state poisoned");
107+
108+
for key in &inner.suppressed_keys {
109+
child_env.remove(key);
110+
}
111+
112+
inner.current = Arc::new(ProviderCredentialSnapshot {
113+
revision,
114+
child_env,
115+
dynamic_credentials: HashMap::new(),
116+
});
117+
inner.generations.clear();
118+
inner.current_resolver = None;
119+
inner.combined_resolver = None;
120+
inner.current.child_env.len()
121+
}
122+
92123
pub fn snapshot(&self) -> Arc<ProviderCredentialSnapshot> {
93124
self.inner
94125
.read()
@@ -620,6 +651,39 @@ mod tests {
620651
);
621652
}
622653

654+
#[test]
655+
fn child_env_snapshot_install_updates_env_without_resolver_material() {
656+
let state = ProviderCredentialState::from_child_env_snapshot(
657+
1,
658+
HashMap::from([
659+
("GITHUB_TOKEN".to_string(), "old".to_string()),
660+
("GCE_METADATA_HOST".to_string(), "marker".to_string()),
661+
]),
662+
);
663+
state.remove_env_key("GCE_METADATA_HOST");
664+
665+
let env_count = state.install_child_env_snapshot(
666+
2,
667+
HashMap::from([
668+
("GITHUB_TOKEN".to_string(), "new".to_string()),
669+
("GCE_METADATA_HOST".to_string(), "marker".to_string()),
670+
]),
671+
);
672+
673+
let snapshot = state.snapshot();
674+
assert_eq!(snapshot.revision, 2);
675+
assert_eq!(env_count, 1);
676+
assert_eq!(
677+
snapshot.child_env.get("GITHUB_TOKEN").map(String::as_str),
678+
Some("new")
679+
);
680+
assert!(!snapshot.child_env.contains_key("GCE_METADATA_HOST"));
681+
assert!(
682+
state.resolver().is_none(),
683+
"child-env snapshots must not install provider resolver material"
684+
);
685+
}
686+
623687
#[test]
624688
fn stale_generation_falls_back_to_current_credential_after_retention_window() {
625689
let state = ProviderCredentialState::from_environment(

crates/openshell-driver-kubernetes/src/driver.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3169,6 +3169,7 @@ mod tests {
31693169
assert_eq!(agent["securityContext"]["runAsUser"], 1500);
31703170
assert_eq!(agent["securityContext"]["runAsGroup"], 1500);
31713171
assert_eq!(agent["securityContext"]["runAsNonRoot"], true);
3172+
assert_eq!(agent["securityContext"]["allowPrivilegeEscalation"], false);
31723173
assert_eq!(
31733174
agent["securityContext"]["capabilities"],
31743175
serde_json::json!({

0 commit comments

Comments
 (0)