Skip to content

Commit 5cc6840

Browse files
committed
refactor: use targeted io_uring seccomp profile instead of RuntimeDefault
Replace the RuntimeDefault seccomp profile on node-24+ run pods with a Localhost profile that blocks only io_uring_setup/enter/register and allows every other syscall. This keeps node-24+ pods CRIU-checkpointable (libuv falls back to epoll) without RuntimeDefault's broad syscall allowlist, which could break unrelated workloads (browsers, sandboxes, native threads). Still scoped to node-24+; node/node-22/bun pods remain unconfined.
1 parent 0ea5f74 commit 5cc6840

4 files changed

Lines changed: 53 additions & 15 deletions

File tree

apps/supervisor/src/workloadManager/kubernetes.test.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
11
import { describe, expect, it } from "vitest";
2-
import { withRuntimeDefaultSeccompProfile } from "./kubernetesPodSpec.js";
2+
import {
3+
BLOCK_IO_URING_SECCOMP_PROFILE,
4+
runtimeRequiresSeccompProfile,
5+
withBlockIoUringSeccompProfile,
6+
} from "./kubernetesPodSpec.js";
37

4-
describe("withRuntimeDefaultSeccompProfile", () => {
5-
it("adds RuntimeDefault seccomp while preserving pod security defaults", () => {
6-
const podSpec = withRuntimeDefaultSeccompProfile({
8+
describe("runtimeRequiresSeccompProfile", () => {
9+
it("returns true for node-24 and above", () => {
10+
expect(runtimeRequiresSeccompProfile("node-24")).toBe(true);
11+
expect(runtimeRequiresSeccompProfile("node-26")).toBe(true);
12+
expect(runtimeRequiresSeccompProfile("node-30")).toBe(true);
13+
expect(runtimeRequiresSeccompProfile("experimental-node-24")).toBe(true);
14+
});
15+
16+
it("returns false for runtimes that do not create io_uring fds", () => {
17+
expect(runtimeRequiresSeccompProfile("node")).toBe(false);
18+
expect(runtimeRequiresSeccompProfile("node-22")).toBe(false);
19+
expect(runtimeRequiresSeccompProfile("bun")).toBe(false);
20+
expect(runtimeRequiresSeccompProfile(undefined)).toBe(false);
21+
expect(runtimeRequiresSeccompProfile(null)).toBe(false);
22+
expect(runtimeRequiresSeccompProfile("")).toBe(false);
23+
});
24+
});
25+
26+
describe("withBlockIoUringSeccompProfile", () => {
27+
it("adds the Localhost io_uring profile while preserving pod security defaults", () => {
28+
const podSpec = withBlockIoUringSeccompProfile({
729
restartPolicy: "Never",
830
automountServiceAccountToken: false,
931
securityContext: {
@@ -21,7 +43,8 @@ describe("withRuntimeDefaultSeccompProfile", () => {
2143
runAsUser: 1000,
2244
fsGroup: 1000,
2345
seccompProfile: {
24-
type: "RuntimeDefault",
46+
type: "Localhost",
47+
localhostProfile: BLOCK_IO_URING_SECCOMP_PROFILE,
2548
},
2649
},
2750
});

apps/supervisor/src/workloadManager/kubernetes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { type K8sApi, createK8sApi, type k8s } from "../clients/kubernetes.js";
1616
import { getRunnerId } from "../util.js";
1717
import {
1818
runtimeRequiresSeccompProfile,
19-
withRuntimeDefaultSeccompProfile,
19+
withBlockIoUringSeccompProfile,
2020
} from "./kubernetesPodSpec.js";
2121

2222
type ResourceQuantities = {
@@ -111,7 +111,7 @@ export class KubernetesWorkloadManager implements WorkloadManager {
111111
try {
112112
const basePodSpec = this.addPlacementTags(this.#defaultPodSpec, opts.placementTags);
113113
const podSpec = runtimeRequiresSeccompProfile(opts.runtime)
114-
? withRuntimeDefaultSeccompProfile(basePodSpec)
114+
? withBlockIoUringSeccompProfile(basePodSpec)
115115
: basePodSpec;
116116

117117
await this.k8s.core.createNamespacedPod({

apps/supervisor/src/workloadManager/kubernetesPodSpec.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
import type { k8s } from "../clients/kubernetes.js";
22

3+
/**
4+
* Path (relative to the kubelet seccomp root, /var/lib/kubelet/seccomp) of the
5+
* targeted seccomp profile that blocks only io_uring_setup/enter/register and
6+
* allows every other syscall. Must match the profile distributed to worker nodes
7+
* by the infra kubeadm config.
8+
*/
9+
export const BLOCK_IO_URING_SECCOMP_PROFILE = "profiles/block-io-uring.json";
10+
311
/**
412
* Node >= 24 (libuv >= ~1.52) unconditionally creates io_uring file descriptors,
5-
* which cannot be checkpointed. Launching those pods under the RuntimeDefault
6-
* seccomp profile makes io_uring_setup fail so libuv falls back to epoll, keeping
7-
* the pod checkpointable. "node" (21.x), "node-22" (UV_USE_IO_URING=0 still works)
8-
* and "bun" do not create these descriptors, so the profile is scoped to node-24+
9-
* to avoid changing the syscall surface of existing runtimes.
13+
* which cannot be checkpointed. Launching those pods under a seccomp profile that
14+
* fails io_uring_setup makes libuv fall back to epoll, keeping the pod
15+
* checkpointable. "node" (21.x), "node-22" (UV_USE_IO_URING=0 still works) and
16+
* "bun" do not create these descriptors, so the profile is scoped to node-24+ to
17+
* avoid changing the syscall surface of existing runtimes.
1018
*
1119
* Tolerant of an "experimental-" prefix in case a non-normalized value reaches here.
1220
*/
@@ -16,15 +24,22 @@ export function runtimeRequiresSeccompProfile(runtime: string | null | undefined
1624
return match ? Number(match[1]) >= 24 : false;
1725
}
1826

19-
export function withRuntimeDefaultSeccompProfile(
27+
/**
28+
* Applies the targeted Localhost profile that blocks only io_uring, preserving any
29+
* existing security-context fields. Unlike RuntimeDefault this restricts no other
30+
* syscalls, so it cannot break unrelated workloads (browsers, sandboxes, native
31+
* threads). Only call this for runtimes where runtimeRequiresSeccompProfile is true.
32+
*/
33+
export function withBlockIoUringSeccompProfile(
2034
podSpec: Omit<k8s.V1PodSpec, "containers">
2135
): Omit<k8s.V1PodSpec, "containers"> {
2236
return {
2337
...podSpec,
2438
securityContext: {
2539
...podSpec.securityContext,
2640
seccompProfile: {
27-
type: "RuntimeDefault",
41+
type: "Localhost",
42+
localhostProfile: BLOCK_IO_URING_SECCOMP_PROFILE,
2843
},
2944
},
3045
};

apps/supervisor/src/workloadManager/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export interface WorkloadManagerCreateOptions {
4141
deploymentFriendlyId: string;
4242
deploymentVersion: string;
4343
// Canonical runtime identifier (e.g. "node", "node-22", "node-24"). Used to
44-
// scope the RuntimeDefault seccomp profile to runtimes that require it.
44+
// scope the io_uring-blocking seccomp profile to runtimes that require it.
4545
runtime?: string;
4646
runId: string;
4747
runFriendlyId: string;

0 commit comments

Comments
 (0)