Skip to content

Commit 158f695

Browse files
authored
feat(supervisor): make the runner seccomp profile configurable
Replaces the hardcoded runner seccomp profile path with KUBERNETES_RUNNER_SECCOMP_PROFILE_PATH, and the node-24-only condition with KUBERNETES_RUNNER_SECCOMP_PROFILE_RUNTIMES (none | node-24-plus | all). Both defaults reproduce current behaviour, so this is inert on merge. Widening the scope or turning attachment off becomes a config change rather than a deploy.
1 parent 74db5a3 commit 158f695

4 files changed

Lines changed: 87 additions & 36 deletions

File tree

apps/supervisor/src/env.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,14 @@ export const Env = z
210210

211211
KUBERNETES_MEMORY_OVERHEAD_GB: z.coerce.number().min(0).optional(), // Optional memory overhead to add to the limit in GB
212212
KUBERNETES_SCHEDULER_NAME: z.string().optional(), // Custom scheduler name for pods
213+
KUBERNETES_RUNNER_SECCOMP_PROFILE_PATH: z
214+
.string()
215+
.trim()
216+
.min(1)
217+
.default("profiles/block-io-uring.json"),
218+
KUBERNETES_RUNNER_SECCOMP_PROFILE_RUNTIMES: z
219+
.enum(["none", "node-24-plus", "all"])
220+
.default("node-24-plus"),
213221

214222
// Pod DNS config — override the cluster default ndots to `KUBERNETES_POD_DNS_NDOTS`.
215223
// Default k8s ndots is 5: any name with fewer than 5 dots (e.g. `api.example.com`, 2 dots) is first walked

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

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { describe, expect, it } from "vitest";
22
import {
3-
BLOCK_IO_URING_SECCOMP_PROFILE,
43
nodetypeNodeSelector,
54
runPodTolerations,
6-
withBlockIoUringSeccompProfile,
5+
withRunnerSeccompProfile,
76
withNodeSelector,
87
} from "./kubernetesPodSpec.js";
98

@@ -100,27 +99,57 @@ describe("withNodeSelector", () => {
10099
});
101100
});
102101

103-
describe("withBlockIoUringSeccompProfile", () => {
104-
it("adds the Localhost io_uring profile for node-24 and above, preserving pod security defaults", () => {
102+
describe("withRunnerSeccompProfile", () => {
103+
const base = {
104+
profilePath: "profiles/example.json",
105+
runtimes: "node-24-plus" as const,
106+
runtime: "node-24",
107+
checkpointsEnabled: true,
108+
};
109+
110+
const withProfile = {
111+
...basePodSpec,
112+
securityContext: {
113+
...basePodSpec.securityContext,
114+
seccompProfile: { type: "Localhost", localhostProfile: "profiles/example.json" },
115+
},
116+
};
117+
118+
it("applies the profile to node-24 and above under the default scope", () => {
105119
for (const runtime of ["node-24", "node-26", "node-30", "experimental-node-24"]) {
106-
const podSpec = withBlockIoUringSeccompProfile(basePodSpec, runtime);
107-
108-
expect(podSpec).toMatchObject({
109-
...basePodSpec,
110-
securityContext: {
111-
...basePodSpec.securityContext,
112-
seccompProfile: {
113-
type: "Localhost",
114-
localhostProfile: BLOCK_IO_URING_SECCOMP_PROFILE,
115-
},
116-
},
117-
});
120+
expect(withRunnerSeccompProfile(basePodSpec, { ...base, runtime })).toMatchObject(
121+
withProfile
122+
);
118123
}
119124
});
120125

121-
it("leaves the pod spec unchanged for runtimes that do not create io_uring fds", () => {
126+
it("skips older runtimes under the default scope", () => {
122127
for (const runtime of ["node", "node-22", "bun", undefined, null, ""]) {
123-
expect(withBlockIoUringSeccompProfile(basePodSpec, runtime)).toEqual(basePodSpec);
128+
expect(withRunnerSeccompProfile(basePodSpec, { ...base, runtime })).toBe(basePodSpec);
129+
}
130+
});
131+
132+
it("applies the profile to every runtime under the all scope", () => {
133+
for (const runtime of ["node", "node-22", "bun", "node-24", undefined]) {
134+
expect(
135+
withRunnerSeccompProfile(basePodSpec, { ...base, runtimes: "all", runtime })
136+
).toMatchObject(withProfile);
137+
}
138+
});
139+
140+
it("applies nothing under the none scope, whatever the runtime", () => {
141+
for (const runtime of ["node-24", "bun", "node-22"]) {
142+
expect(withRunnerSeccompProfile(basePodSpec, { ...base, runtimes: "none", runtime })).toBe(
143+
basePodSpec
144+
);
145+
}
146+
});
147+
148+
it("applies nothing when checkpoints are disabled", () => {
149+
for (const runtimes of ["none", "node-24-plus", "all"] as const) {
150+
expect(
151+
withRunnerSeccompProfile(basePodSpec, { ...base, runtimes, checkpointsEnabled: false })
152+
).toBe(basePodSpec);
124153
}
125154
});
126155
});

apps/supervisor/src/workloadManager/kubernetes.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { getRunnerId } from "../util.js";
1717
import {
1818
nodetypeNodeSelector,
1919
runPodTolerations,
20-
withBlockIoUringSeccompProfile,
20+
withRunnerSeccompProfile,
2121
withNodeSelector,
2222
} from "./kubernetesPodSpec.js";
2323

@@ -135,9 +135,12 @@ export class KubernetesWorkloadManager implements WorkloadManager {
135135
);
136136
}
137137
}
138-
const podSpec = this.opts.checkpointsEnabled
139-
? withBlockIoUringSeccompProfile(basePodSpec, opts.runtime)
140-
: basePodSpec;
138+
const podSpec = withRunnerSeccompProfile(basePodSpec, {
139+
profilePath: env.KUBERNETES_RUNNER_SECCOMP_PROFILE_PATH,
140+
runtimes: env.KUBERNETES_RUNNER_SECCOMP_PROFILE_RUNTIMES,
141+
runtime: opts.runtime,
142+
checkpointsEnabled: this.opts.checkpointsEnabled,
143+
});
141144

142145
await this.k8s.core.createNamespacedPod({
143146
namespace: this.namespace,

apps/supervisor/src/workloadManager/kubernetesPodSpec.ts

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

3-
/**
4-
* Relative path (kubelet seccomp root) of the profile blocking only io_uring
5-
* syscalls. Must match the profile deployed to worker nodes.
6-
*/
7-
export const BLOCK_IO_URING_SECCOMP_PROFILE = "profiles/block-io-uring.json";
8-
93
/**
104
* An empty label is the documented off-switch, leaving the pod unpinned. The Helm
115
* chart ships an empty value, so don't collapse this into a fallback default -
@@ -60,27 +54,44 @@ export function withNodeSelector(
6054
};
6155
}
6256

57+
export type RunnerSeccompProfileOptions = {
58+
profilePath: string;
59+
runtimes: "none" | "node-24-plus" | "all";
60+
runtime: string | null | undefined;
61+
checkpointsEnabled: boolean | undefined;
62+
};
63+
6364
/**
64-
* Node >= 24 always creates io_uring fds, which can't be checkpointed. Blocking
65-
* io_uring_setup makes libuv fall back to epoll. Other runtimes don't need this,
66-
* so the profile is only applied for node-24+. Tolerates an "experimental-" prefix.
65+
* Applies the runner seccomp profile, which is a node-local file installed outside
66+
* this repo - pointing a pod at a profile its node doesn't have fails pod creation,
67+
* so every condition for skipping it lives here.
68+
*
69+
* "node-24-plus" matches the original rollout: node >= 24 always creates io_uring
70+
* fds, which can't be checkpointed, and blocking io_uring_setup makes libuv fall
71+
* back to epoll. Tolerates an "experimental-" prefix. "bun" matches only under "all".
6772
*/
68-
export function withBlockIoUringSeccompProfile(
73+
export function withRunnerSeccompProfile(
6974
podSpec: Omit<k8s.V1PodSpec, "containers">,
70-
runtime: string | null | undefined
75+
options: RunnerSeccompProfileOptions
7176
): Omit<k8s.V1PodSpec, "containers"> {
72-
const match = runtime ? /^(?:experimental-)?node-(\d+)$/.exec(runtime) : null;
73-
if (!match || Number(match[1]) < 24) {
77+
if (!options.checkpointsEnabled || options.runtimes === "none") {
7478
return podSpec;
7579
}
7680

81+
if (options.runtimes === "node-24-plus") {
82+
const match = options.runtime ? /^(?:experimental-)?node-(\d+)$/.exec(options.runtime) : null;
83+
if (!match || Number(match[1]) < 24) {
84+
return podSpec;
85+
}
86+
}
87+
7788
return {
7889
...podSpec,
7990
securityContext: {
8091
...podSpec.securityContext,
8192
seccompProfile: {
8293
type: "Localhost",
83-
localhostProfile: BLOCK_IO_URING_SECCOMP_PROFILE,
94+
localhostProfile: options.profilePath,
8495
},
8596
},
8697
};

0 commit comments

Comments
 (0)