From 4dad59fecfd1bcf40ef3999e48f093b960db9df6 Mon Sep 17 00:00:00 2001 From: Xiao Deshi Date: Sat, 6 Jun 2026 10:17:23 +0800 Subject: [PATCH 1/2] fix(sandbox): configure gVisor host network mode for Cilium eBPF compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gVisor's default netstack mode processes TCP/IP in userspace (Sentry), bypassing Cilium's eBPF programs attached to veth pairs. This causes DNS and all egress traffic to fail — Cilium cannot see or enforce network policies on traffic from gVisor pods. Set gvisor.dev/network=host annotation on gVisor pods so Sentry forwards network syscalls to the pod's kernel network namespace. Traffic then flows through the standard veth + eBPF path, allowing Cilium to intercept and enforce policies normally. The pod still has its own network namespace — this is NOT pod-level hostNetwork. gVisor's syscall filtering and sandboxing remain intact. --- pkg/sandboxmatrix/controller.go | 1 + pkg/sandboxmatrix/grpc/orchestrator.go | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pkg/sandboxmatrix/controller.go b/pkg/sandboxmatrix/controller.go index d7c67a034..510694d08 100644 --- a/pkg/sandboxmatrix/controller.go +++ b/pkg/sandboxmatrix/controller.go @@ -175,6 +175,7 @@ func newWarmPod(cfg config.SandboxConfig, runtimeClass string) *corev1.Pod { GenerateName: "sandbox-warm-", Namespace: cfg.Namespace, Labels: map[string]string{sandboxgrpc.LabelState: sandboxgrpc.StateWarm}, + Annotations: sandboxgrpc.GvisorAnnotations(runtimeClass), }, Spec: warmPodSpec(runtimeClass, cfg), } diff --git a/pkg/sandboxmatrix/grpc/orchestrator.go b/pkg/sandboxmatrix/grpc/orchestrator.go index 9d2b0640e..a9a40fc2f 100644 --- a/pkg/sandboxmatrix/grpc/orchestrator.go +++ b/pkg/sandboxmatrix/grpc/orchestrator.go @@ -616,9 +616,10 @@ func (o *Orchestrator) claimOrCreatePod(ctx context.Context, sessionID, runtimeC } pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("sandbox-%s", sessionID), - Namespace: sandboxNS, - Labels: map[string]string{labelState: stateActive, labelSessionID: sessionID}, + Name: fmt.Sprintf("sandbox-%s", sessionID), + Namespace: sandboxNS, + Labels: map[string]string{labelState: stateActive, labelSessionID: sessionID}, + Annotations: GvisorAnnotations(runtimeClass), }, Spec: sandboxPodSpec(runtimeClass, pvcName, cpu, memory), } @@ -671,6 +672,17 @@ func SandboxPodSpec(runtimeClass, pvcName, cpu, memory, image string) corev1.Pod func boolPtr(b bool) *bool { return &b } +// GvisorAnnotations returns pod annotations required for gVisor to work with +// Cilium eBPF. The default netstack mode processes network in userspace, +// bypassing Cilium's eBPF programs attached to veth pairs. Host network mode +// forwards syscalls to the pod's kernel network namespace instead. +func GvisorAnnotations(runtimeClass string) map[string]string { + if runtimeClass == "gvisor" { + return map[string]string{"gvisor.dev/network": "host"} + } + return nil +} + // ensureWorkspacePVC creates a PVC for the session workspace if it doesn't exist. func (o *Orchestrator) ensureWorkspacePVC(ctx context.Context, sessionID string) (string, error) { pvcName := "workspace-" + sessionID From 282893fd5fa42526ed1f647e4e4ba3112d87a65d Mon Sep 17 00:00:00 2001 From: Xiao Deshi Date: Sat, 6 Jun 2026 10:32:32 +0800 Subject: [PATCH 2/2] fix(sandbox): add CNP ingress restriction and --allowed-hosts to run command - Add ingress rule to per-session CNP: only host node can reach sandboxd on port 2024. All other cluster-internal ingress is denied. - Add --allowed-hosts flag to run command for custom network allowlist when auto-creating a session. --- pkg/sandboxcli/commands.go | 7 ++++++- pkg/sandboxmatrix/grpc/orchestrator.go | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/pkg/sandboxcli/commands.go b/pkg/sandboxcli/commands.go index a94e43ec9..650f5af06 100644 --- a/pkg/sandboxcli/commands.go +++ b/pkg/sandboxcli/commands.go @@ -73,8 +73,12 @@ func ensureSession(client *client.Client, ctx *cli.Context) (string, bool, error return sid, false, nil } + var hosts []string + if raw := ctx.String("allowed-hosts"); raw != "" { + hosts = strings.Split(raw, ",") + } resp, err := client.SandboxServiceClient.CreateSession(context.Background(), &pb.CreateSessionRequest{ - TenantId: ctx.String("tenant"), RuntimeClass: "gvisor", + TenantId: ctx.String("tenant"), RuntimeClass: "gvisor", AllowedHosts: hosts, }) if err != nil { return "", false, fmt.Errorf("create session: %w", err) @@ -159,6 +163,7 @@ func RunCommand() cli.Command { cli.StringFlag{Name: "git-repo", Usage: "Git repo to clone (only when auto-creating session)"}, cli.StringFlag{Name: "git-ref", Value: "main", Usage: "Git ref for --git-repo"}, cli.StringFlag{Name: "git-path", Value: "repo", Usage: "Destination path for --git-repo"}, + cli.StringFlag{Name: "allowed-hosts", Usage: "Comma-separated FQDN egress allowlist (only when auto-creating session)"}, }, Action: runAction, } diff --git a/pkg/sandboxmatrix/grpc/orchestrator.go b/pkg/sandboxmatrix/grpc/orchestrator.go index a9a40fc2f..22d7d637f 100644 --- a/pkg/sandboxmatrix/grpc/orchestrator.go +++ b/pkg/sandboxmatrix/grpc/orchestrator.go @@ -733,6 +733,16 @@ func (o *Orchestrator) applyCNP(ctx context.Context, session *sandboxv1.SandboxS "endpointSelector": map[string]interface{}{ "matchLabels": map[string]interface{}{labelSessionID: session.Name}, }, + "ingress": []interface{}{ + map[string]interface{}{ + "fromEntities": []interface{}{"host"}, + "toPorts": []interface{}{ + map[string]interface{}{ + "ports": []interface{}{map[string]interface{}{"port": "2024", "protocol": "TCP"}}, + }, + }, + }, + }, "egress": []interface{}{ map[string]interface{}{ "toEndpoints": []interface{}{