Post-1.0: Localhost seccomp on K8s, warm sandbox pool, server HPA

- K8sDriver.with_localhost_seccomp(profile): sandbox pods run under the
  STRICT allowlist instead of the runtime default. Proven live on kind:
  the harness installs the profile onto the node, a pod runs ordinary
  work as uid 10001, and unshare is kernel-denied inside the pod — the
  same probe the Docker suite uses, now passing on both targets
- Helm: sandbox.seccomp=localhost renders a DaemonSet that installs the
  chart-shipped profile into /var/lib/kubelet/seccomp on every node
  (ConfigMap + hostPath); ci/check-helm.sh enforces the chart copy stays
  byte-identical to images/seccomp/agent-profile.json and asserts the
  hardened render (DaemonSet + profile + HPA)
- server HPA (autoscaling/v2, CPU target) behind
  server.autoscaling.enabled
- SandboxManager.warm(n): a background warmer keeps n pre-provisioned
  sandboxes ready so an agent's first exec skips container startup;
  unhealthy pool entries are discarded, reuse never drains the pool,
  shutdown destroys assigned AND pooled. [sandbox] warm_pool config
  (default 0). Real-Docker test: prefill -> assign -> refill -> reuse ->
  clean shutdown

160 Rust tests + 4 live kind tests.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-10 11:15:49 -05:00
co-authored by Claude Fable 5
parent a26939d7bc
commit 05e9612688
11 changed files with 1055 additions and 19 deletions
+17 -1
View File
@@ -28,6 +28,10 @@ fn engine_err(e: impl std::fmt::Display) -> SandboxError {
pub struct K8sDriver {
client: kube::Client,
namespace: String,
/// Kubelet-relative path of the strict allowlist profile when the
/// nodes carry it (installed by the Helm DaemonSet); RuntimeDefault
/// otherwise.
localhost_seccomp: Option<String>,
}
impl K8sDriver {
@@ -41,6 +45,7 @@ impl K8sDriver {
let driver = K8sDriver {
client,
namespace: namespace.to_owned(),
localhost_seccomp: None,
};
driver.ensure_namespace().await?;
Ok(driver)
@@ -100,7 +105,18 @@ impl K8sDriver {
Ok(())
}
/// Uses the node-installed strict allowlist profile instead of the
/// runtime default (see deploy/helm templates/seccomp-installer).
pub fn with_localhost_seccomp(mut self, profile: &str) -> K8sDriver {
self.localhost_seccomp = Some(profile.to_owned());
self
}
fn pod_spec(&self, spec: &SandboxSpec) -> Pod {
let seccomp = match &self.localhost_seccomp {
Some(profile) => json!({ "type": "Localhost", "localhostProfile": profile }),
None => json!({ "type": "RuntimeDefault" }),
};
serde_json::from_value(json!({
"apiVersion": "v1",
"kind": "Pod",
@@ -116,7 +132,7 @@ impl K8sDriver {
"runAsNonRoot": true,
"runAsUser": 10001,
"runAsGroup": 10001,
"seccompProfile": { "type": "RuntimeDefault" }
"seccompProfile": seccomp
},
"containers": [{
"name": "sandbox",
+57
View File
@@ -145,3 +145,60 @@ async fn destroy_removes_the_pod_and_health_reflects_it() {
}
panic!("pod never disappeared");
}
/// With the strict allowlist installed on the node (the Helm DaemonSet's
/// job; the harness drops it into the kind node), pods run under
/// `Localhost` seccomp and the kernel refuses what the profile removed.
#[tokio::test]
async fn localhost_seccomp_profile_denies_unshare_inside_pods() {
ensure_image_in_kind();
let status = Command::new("docker")
.args([
"exec",
"teamclaw-test-control-plane",
"mkdir",
"-p",
"/var/lib/kubelet/seccomp",
])
.status()
.expect("kind node reachable");
assert!(status.success());
let root = env!("CARGO_MANIFEST_DIR");
let status = Command::new("docker")
.args([
"cp",
&format!("{root}/../../images/seccomp/agent-profile.json"),
"teamclaw-test-control-plane:/var/lib/kubelet/seccomp/teamclaw-agent-profile.json",
])
.status()
.expect("docker cp");
assert!(status.success());
let driver = K8sDriver::connect(NAMESPACE)
.await
.expect("cluster reachable")
.with_localhost_seccomp("teamclaw-agent-profile.json");
let spec = SandboxSpec {
name: format!("tc-k8s-seccomp-{}", std::process::id()),
image: IMAGE.into(),
memory_bytes: 256 * 1024 * 1024,
nano_cpus: 1_000_000_000,
pids_limit: 128,
egress: false,
};
let handle = driver.provision(&spec).await.expect("pod provisions");
// Ordinary work runs...
let ok = driver.exec(&handle, &["id", "-u"]).await.unwrap();
assert_eq!(ok.stdout.trim(), "10001");
// ...but the syscalls stripped from the allowlist are gone — the
// same kernel probe the Docker suite uses.
let unshare = driver
.exec(&handle, &["unshare", "--user", "true"])
.await
.unwrap();
assert_ne!(unshare.exit_code, 0, "unshare must be denied: {unshare:?}");
driver.destroy(&handle).await.unwrap();
}