Post-1.0 finale: Calico-enforced egress denial + visual-regression lock

- scripts/netpol-cluster.sh: a kind cluster with Calico (default CNI
  disabled) — the only way to PROVE the §15 default-deny NetworkPolicy,
  since kindnet accepts the object but never enforces it. New live test
  on that cluster: outbound connect to 1.1.1.1 dropped, DNS egress
  dropped, while API-server exec keeps working (not pod network).
  Kernel-level enforcement of the sandbox egress claim, demonstrated
- K8sDriver::connect_with_context: pin a kubeconfig context instead of
  ambient. The whole k8s suite now pins its cluster explicitly — the
  netpol cluster's creation had silently switched the current context
  and stranded the seccomp test on the wrong cluster (fixed and made
  impossible to recur)
- CI: netpol-cluster up + calico egress test in the sandbox-k8s job
- Visual-regression lock (plan P6): @visual Playwright spec with
  animation-disabled, masked-dynamic-region screenshots of login,
  workspace home, chat welcome, computer panel, credits; darwin
  baselines committed (5 PNGs); CI excludes @visual until linux
  baselines are generated there. Full local suite: 33 journeys

165 Rust tests + 5 live kind tests (2 clusters) + 33 journeys.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-10 13:30:10 -05:00
co-authored by Claude Fable 5
parent 5407111a89
commit e65bcd4130
11 changed files with 214 additions and 4 deletions
+76 -3
View File
@@ -11,6 +11,7 @@ use cm_sandbox::{K8sDriver, SandboxDriver, SandboxSpec};
const IMAGE: &str = "clawmates/agent-base:dev";
const CLUSTER: &str = "clawmates-test";
const CONTEXT: &str = "kind-clawmates-test";
const NAMESPACE: &str = "clawmates-sandboxes-test";
fn ensure_image_in_kind() {
@@ -42,9 +43,18 @@ fn ensure_image_in_kind() {
assert!(status.success(), "kind load failed");
}
fn ensure_image_in_cluster(cluster: &str) {
ensure_image_in_kind();
let status = Command::new("kind")
.args(["load", "docker-image", IMAGE, "--name", cluster])
.status()
.expect("kind available");
assert!(status.success(), "kind load into {cluster} failed");
}
async fn spawn(suffix: &str) -> (K8sDriver, cm_sandbox::SandboxHandle) {
ensure_image_in_kind();
let driver = K8sDriver::connect(NAMESPACE)
let driver = K8sDriver::connect_with_context(NAMESPACE, CONTEXT)
.await
.expect("cluster reachable");
let spec = SandboxSpec {
@@ -106,7 +116,12 @@ async fn namespace_carries_pss_restricted_and_default_deny_policy() {
// Assert through the API: PSS label + the deny-all NetworkPolicy.
// (Kernel-level egress enforcement needs a NetworkPolicy-capable CNI;
// kind's default kindnet does not enforce — production clusters do.)
let client = kube::Client::try_default().await.unwrap();
let options = kube::config::KubeConfigOptions {
context: Some(CONTEXT.to_owned()),
..Default::default()
};
let config = kube::Config::from_kubeconfig(&options).await.unwrap();
let client = kube::Client::try_from(config).unwrap();
let namespaces: kube::Api<k8s_openapi::api::core::v1::Namespace> =
kube::Api::all(client.clone());
let ns = namespaces.get(NAMESPACE).await.unwrap();
@@ -174,7 +189,7 @@ async fn localhost_seccomp_profile_denies_unshare_inside_pods() {
.expect("docker cp");
assert!(status.success());
let driver = K8sDriver::connect(NAMESPACE)
let driver = K8sDriver::connect_with_context(NAMESPACE, CONTEXT)
.await
.expect("cluster reachable")
.with_localhost_seccomp("clawmates-agent-profile.json");
@@ -202,3 +217,61 @@ async fn localhost_seccomp_profile_denies_unshare_inside_pods() {
driver.destroy(&handle).await.unwrap();
}
/// The §15 egress claim, ENFORCED: on a NetworkPolicy-capable CNI
/// (Calico via scripts/netpol-cluster.sh) the namespace's default-deny
/// actually drops packets in the kernel — outbound connects and DNS both
/// fail inside the pod, while API-server exec still works (it is not pod
/// network). kindnet (the default suite's cluster) accepts the policy
/// object but never enforces it; this is the cluster where it bites.
#[tokio::test]
async fn calico_enforces_the_default_deny_egress() {
const NETPOL_CONTEXT: &str = "kind-clawmates-netpol-test";
const NETPOL_CLUSTER: &str = "clawmates-netpol-test";
let have_cluster = Command::new("kind")
.args(["get", "clusters"])
.output()
.map(|out| String::from_utf8_lossy(&out.stdout).contains(NETPOL_CLUSTER))
.unwrap_or(false);
if !have_cluster {
eprintln!("skipped: run scripts/netpol-cluster.sh up first");
return;
}
ensure_image_in_cluster(NETPOL_CLUSTER);
let driver = K8sDriver::connect_with_context(NAMESPACE, NETPOL_CONTEXT)
.await
.expect("calico cluster reachable");
let spec = SandboxSpec {
name: format!("tc-netpol-{}", 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");
// Exec works (API-server channel, not pod network).
let ok = driver.exec(&handle, &["id", "-u"]).await.unwrap();
assert_eq!(ok.stdout.trim(), "10001");
// Raw outbound connect: dropped by Calico, not merely unconfigured.
let direct = driver
.exec(
&handle,
&["wget", "-T", "3", "-q", "-O", "-", "http://1.1.1.1"],
)
.await
.unwrap();
assert_ne!(direct.exit_code, 0, "egress to 1.1.1.1 must be dropped");
// DNS (UDP egress to cluster DNS) is denied too.
let dns = driver
.exec(&handle, &["nslookup", "anthropic.com"])
.await
.unwrap();
assert_ne!(dns.exit_code, 0, "DNS egress must be dropped");
driver.destroy(&handle).await.unwrap();
}