Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/supervisor/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ export const Env = z
KUBERNETES_RUNNER_SECCOMP_PROFILE_RUNTIMES: z
.enum(["none", "node-24-plus", "all"])
.default("node-24-plus"),
KUBERNETES_RUNNER_SECURITY_CONTEXT: z.enum(["off", "baseline", "restricted"]).default("off"),
KUBERNETES_RUNNER_RUN_AS_USER: z.coerce.number().int().min(1).default(1000),

// Pod DNS config — override the cluster default ndots to `KUBERNETES_POD_DNS_NDOTS`.
// Default k8s ndots is 5: any name with fewer than 5 dots (e.g. `api.example.com`, 2 dots) is first walked
Expand Down
33 changes: 33 additions & 0 deletions apps/supervisor/src/workloadManager/kubernetes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import {
nodetypeNodeSelector,
runPodTolerations,
runnerSecurityContext,
withRunnerSeccompProfile,
withNodeSelector,
} from "./kubernetesPodSpec.js";
Expand Down Expand Up @@ -153,3 +154,35 @@ describe("withRunnerSeccompProfile", () => {
}
});
});

describe("runnerSecurityContext", () => {
it("sets nothing when off", () => {
expect(runnerSecurityContext("off", 1000, "node-24")).toBeUndefined();
});

it("drops all capabilities and blocks escalation at baseline", () => {
expect(runnerSecurityContext("baseline", 1000, "node-24")).toEqual({
allowPrivilegeEscalation: false,
capabilities: { drop: ["ALL"] },
});
});

it("pins the configured uid when restricted", () => {
expect(runnerSecurityContext("restricted", 1000, "node-24")).toEqual({
allowPrivilegeEscalation: false,
capabilities: { drop: ["ALL"] },
runAsNonRoot: true,
runAsUser: 1000,
});
});

it("pins bun's own uid, which differs from node's", () => {
expect(runnerSecurityContext("restricted", 1000, "bun")?.runAsUser).toBe(1001);
});

it("falls back to the configured uid when the runtime is unknown", () => {
for (const runtime of [undefined, null, "", "node", "node-22", "node-26"]) {
expect(runnerSecurityContext("restricted", 1000, runtime)?.runAsUser).toBe(1000);
}
});
});
6 changes: 6 additions & 0 deletions apps/supervisor/src/workloadManager/kubernetes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getRunnerId } from "../util.js";
import {
nodetypeNodeSelector,
runPodTolerations,
runnerSecurityContext,
withRunnerSeccompProfile,
withNodeSelector,
} from "./kubernetesPodSpec.js";
Expand Down Expand Up @@ -175,6 +176,11 @@ export class KubernetesWorkloadManager implements WorkloadManager {
},
],
resources: this.#getResourcesForMachine(opts.machine),
securityContext: runnerSecurityContext(
env.KUBERNETES_RUNNER_SECURITY_CONTEXT,
env.KUBERNETES_RUNNER_RUN_AS_USER,
opts.runtime
),
env: [
{
name: "TRIGGER_DEQUEUED_AT_MS",
Expand Down
30 changes: 30 additions & 0 deletions apps/supervisor/src/workloadManager/kubernetesPodSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,33 @@ export function withRunnerSeccompProfile(
},
};
}

const BUN_RUN_AS_USER = 1001;

/**
* runnerSecurityContext maps a configured level onto the run container's security
* context. "baseline" drops the capability bounding set and blocks setuid
* escalation; "restricted" additionally pins the container to a non-root uid.
*
* The uid is set explicitly rather than read from the image: the kubelet cannot
* verify `runAsNonRoot` against an image that declares a named user, and fails
* the container instead. Bun images carry their user at a different uid to
* node's, so the runtime selects which uid is pinned.
*/
export function runnerSecurityContext(
level: "off" | "baseline" | "restricted",
runAsUser: number,
runtime: string | null | undefined
): k8s.V1SecurityContext | undefined {
if (level === "off") {
return undefined;
}

return {
allowPrivilegeEscalation: false,
capabilities: { drop: ["ALL"] },
...(level === "restricted"
? { runAsNonRoot: true, runAsUser: runtime === "bun" ? BUN_RUN_AS_USER : runAsUser }
: {}),
Comment thread
nicktrn marked this conversation as resolved.
};
Comment thread
nicktrn marked this conversation as resolved.
}