bulkhead-runtime 0.1.0 → 2026.4.5-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +344 -262
- package/dist/cli.js +5 -1
- package/dist/cli.js.map +1 -1
- package/dist/config/index.d.ts +28 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +9 -6
- package/dist/config/index.js.map +1 -1
- package/dist/credentials/store.d.ts.map +1 -1
- package/dist/credentials/store.js +39 -15
- package/dist/credentials/store.js.map +1 -1
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -1
- package/dist/index.js.map +1 -1
- package/dist/infra/warning-filter.js +1 -1
- package/dist/infra/warning-filter.js.map +1 -1
- package/dist/logging/subsystem.d.ts +15 -1
- package/dist/logging/subsystem.d.ts.map +1 -1
- package/dist/logging/subsystem.js +310 -45
- package/dist/logging/subsystem.js.map +1 -1
- package/dist/memory/embedding-batch.d.ts +38 -0
- package/dist/memory/embedding-batch.d.ts.map +1 -0
- package/dist/memory/embedding-batch.js +253 -0
- package/dist/memory/embedding-batch.js.map +1 -0
- package/dist/memory/embedding-cache.d.ts +16 -0
- package/dist/memory/embedding-cache.d.ts.map +1 -0
- package/dist/memory/embedding-cache.js +113 -0
- package/dist/memory/embedding-cache.js.map +1 -0
- package/dist/memory/embeddings-debug.js +1 -1
- package/dist/memory/embeddings.d.ts +1 -0
- package/dist/memory/embeddings.d.ts.map +1 -1
- package/dist/memory/embeddings.js +115 -92
- package/dist/memory/embeddings.js.map +1 -1
- package/dist/memory/file-indexer.d.ts +26 -0
- package/dist/memory/file-indexer.d.ts.map +1 -0
- package/dist/memory/file-indexer.js +258 -0
- package/dist/memory/file-indexer.js.map +1 -0
- package/dist/memory/hybrid.d.ts.map +1 -1
- package/dist/memory/hybrid.js +6 -2
- package/dist/memory/hybrid.js.map +1 -1
- package/dist/memory/index.d.ts +5 -0
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +5 -2
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/session-indexer.d.ts +41 -0
- package/dist/memory/session-indexer.d.ts.map +1 -0
- package/dist/memory/session-indexer.js +367 -0
- package/dist/memory/session-indexer.js.map +1 -0
- package/dist/memory/simple-manager.d.ts +6 -0
- package/dist/memory/simple-manager.d.ts.map +1 -1
- package/dist/memory/simple-manager.js +35 -12
- package/dist/memory/simple-manager.js.map +1 -1
- package/dist/memory/ssrf.d.ts +18 -0
- package/dist/memory/ssrf.d.ts.map +1 -0
- package/dist/memory/ssrf.js +305 -0
- package/dist/memory/ssrf.js.map +1 -0
- package/dist/package.json +8 -5
- package/dist/platform/platform.d.ts.map +1 -1
- package/dist/platform/platform.js +30 -7
- package/dist/platform/platform.js.map +1 -1
- package/dist/platform/types.d.ts +2 -0
- package/dist/platform/types.d.ts.map +1 -1
- package/dist/runtime/agent.d.ts +8 -0
- package/dist/runtime/agent.d.ts.map +1 -1
- package/dist/runtime/agent.js +194 -46
- package/dist/runtime/agent.js.map +1 -1
- package/dist/runtime/api-key-rotation.d.ts +26 -0
- package/dist/runtime/api-key-rotation.d.ts.map +1 -0
- package/dist/runtime/api-key-rotation.js +174 -0
- package/dist/runtime/api-key-rotation.js.map +1 -0
- package/dist/runtime/context-guard.d.ts +32 -0
- package/dist/runtime/context-guard.d.ts.map +1 -0
- package/dist/runtime/context-guard.js +61 -0
- package/dist/runtime/context-guard.js.map +1 -0
- package/dist/runtime/failover-error.d.ts +62 -0
- package/dist/runtime/failover-error.d.ts.map +1 -0
- package/dist/runtime/failover-error.js +733 -0
- package/dist/runtime/failover-error.js.map +1 -0
- package/dist/runtime/failover-policy.d.ts +5 -0
- package/dist/runtime/failover-policy.d.ts.map +1 -0
- package/dist/runtime/failover-policy.js +18 -0
- package/dist/runtime/failover-policy.js.map +1 -0
- package/dist/runtime/index.d.ts +11 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +11 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/memory-flush.d.ts +24 -0
- package/dist/runtime/memory-flush.d.ts.map +1 -0
- package/dist/runtime/memory-flush.js +64 -0
- package/dist/runtime/memory-flush.js.map +1 -0
- package/dist/runtime/memory-tools.d.ts +14 -0
- package/dist/runtime/memory-tools.d.ts.map +1 -0
- package/dist/runtime/memory-tools.js +58 -0
- package/dist/runtime/memory-tools.js.map +1 -0
- package/dist/runtime/model-fallback.d.ts +56 -0
- package/dist/runtime/model-fallback.d.ts.map +1 -0
- package/dist/runtime/model-fallback.js +301 -0
- package/dist/runtime/model-fallback.js.map +1 -0
- package/dist/runtime/model-fallback.types.d.ts +14 -0
- package/dist/runtime/model-fallback.types.d.ts.map +1 -0
- package/dist/runtime/model-fallback.types.js +3 -0
- package/dist/runtime/model-fallback.types.js.map +1 -0
- package/dist/runtime/retry.d.ts +24 -0
- package/dist/runtime/retry.d.ts.map +1 -0
- package/dist/runtime/retry.js +102 -0
- package/dist/runtime/retry.js.map +1 -0
- package/dist/runtime/session-pruning.d.ts +22 -0
- package/dist/runtime/session-pruning.d.ts.map +1 -0
- package/dist/runtime/session-pruning.js +118 -0
- package/dist/runtime/session-pruning.js.map +1 -0
- package/dist/runtime/stream-adapters.d.ts +11 -0
- package/dist/runtime/stream-adapters.d.ts.map +1 -0
- package/dist/runtime/stream-adapters.js +46 -0
- package/dist/runtime/stream-adapters.js.map +1 -0
- package/dist/runtime/subagent.d.ts +83 -0
- package/dist/runtime/subagent.d.ts.map +1 -0
- package/dist/runtime/subagent.js +190 -0
- package/dist/runtime/subagent.js.map +1 -0
- package/dist/runtime/tool-result-truncation.d.ts +25 -0
- package/dist/runtime/tool-result-truncation.d.ts.map +1 -0
- package/dist/runtime/tool-result-truncation.js +115 -0
- package/dist/runtime/tool-result-truncation.js.map +1 -0
- package/dist/sandbox/cgroup.d.ts +4 -1
- package/dist/sandbox/cgroup.d.ts.map +1 -1
- package/dist/sandbox/cgroup.js +28 -15
- package/dist/sandbox/cgroup.js.map +1 -1
- package/dist/sandbox/index.d.ts +2 -1
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +2 -1
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/ipc.d.ts +4 -1
- package/dist/sandbox/ipc.d.ts.map +1 -1
- package/dist/sandbox/ipc.js +33 -17
- package/dist/sandbox/ipc.js.map +1 -1
- package/dist/sandbox/manager.d.ts +1 -2
- package/dist/sandbox/manager.d.ts.map +1 -1
- package/dist/sandbox/manager.js +136 -130
- package/dist/sandbox/manager.js.map +1 -1
- package/dist/sandbox/namespace.d.ts +1 -1
- package/dist/sandbox/namespace.d.ts.map +1 -1
- package/dist/sandbox/namespace.js +36 -37
- package/dist/sandbox/namespace.js.map +1 -1
- package/dist/sandbox/rootfs.d.ts +6 -1
- package/dist/sandbox/rootfs.d.ts.map +1 -1
- package/dist/sandbox/rootfs.js +114 -30
- package/dist/sandbox/rootfs.js.map +1 -1
- package/dist/sandbox/seccomp-apply.d.ts +9 -0
- package/dist/sandbox/seccomp-apply.d.ts.map +1 -0
- package/dist/sandbox/seccomp-apply.js +227 -0
- package/dist/sandbox/seccomp-apply.js.map +1 -0
- package/dist/sandbox/seccomp.js +3 -3
- package/dist/sandbox/seccomp.js.map +1 -1
- package/dist/sandbox/types.d.ts +1 -3
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sandbox/worker.d.ts +3 -0
- package/dist/sandbox/worker.d.ts.map +1 -1
- package/dist/sandbox/worker.js +84 -17
- package/dist/sandbox/worker.js.map +1 -1
- package/dist/sessions/index.d.ts +1 -0
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/sessions/index.js +1 -0
- package/dist/sessions/index.js.map +1 -1
- package/dist/sessions/store.d.ts +2 -2
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +49 -27
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/transcript-events.d.ts +11 -0
- package/dist/sessions/transcript-events.d.ts.map +1 -0
- package/dist/sessions/transcript-events.js +40 -0
- package/dist/sessions/transcript-events.js.map +1 -0
- package/dist/shared/agent-session.d.ts +10 -0
- package/dist/shared/agent-session.d.ts.map +1 -0
- package/dist/shared/agent-session.js +33 -0
- package/dist/shared/agent-session.js.map +1 -0
- package/dist/shared/constants.d.ts +6 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/constants.js +11 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/fs.d.ts +7 -0
- package/dist/shared/fs.d.ts.map +1 -0
- package/dist/shared/fs.js +14 -0
- package/dist/shared/fs.js.map +1 -0
- package/dist/shared/index.d.ts +4 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +4 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/skills/enablement.d.ts.map +1 -1
- package/dist/skills/enablement.js +2 -2
- package/dist/skills/enablement.js.map +1 -1
- package/dist/workspace/runner.d.ts.map +1 -1
- package/dist/workspace/runner.js +436 -105
- package/dist/workspace/runner.js.map +1 -1
- package/dist/workspace/types.d.ts +1 -0
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/workspace.d.ts.map +1 -1
- package/dist/workspace/workspace.js +12 -3
- package/dist/workspace/workspace.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,52 +1,51 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
2
3
|
import * as fs from "node:fs";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
16
|
-
}
|
|
4
|
+
import { LINUX_REQUIRED_MESSAGE } from "../shared/index.js";
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
export async function detectCapabilities() {
|
|
7
|
+
if (process.platform !== "linux") {
|
|
8
|
+
throw new Error(LINUX_REQUIRED_MESSAGE);
|
|
9
|
+
}
|
|
10
|
+
const [hasUserNamespace, hasPidNamespace, hasMountNamespace, hasNetNamespace] = await Promise.all([
|
|
11
|
+
checkUserNamespace(),
|
|
12
|
+
checkNamespaceSupport("pid"),
|
|
13
|
+
checkNamespaceSupport("mnt"),
|
|
14
|
+
checkNamespaceSupport("net"),
|
|
15
|
+
]);
|
|
17
16
|
return {
|
|
18
|
-
hasUserNamespace
|
|
19
|
-
hasPidNamespace
|
|
20
|
-
hasMountNamespace
|
|
21
|
-
hasNetNamespace
|
|
17
|
+
hasUserNamespace,
|
|
18
|
+
hasPidNamespace,
|
|
19
|
+
hasMountNamespace,
|
|
20
|
+
hasNetNamespace,
|
|
22
21
|
hasCgroupV2: checkCgroupV2(),
|
|
23
22
|
hasSeccomp: checkSeccomp(),
|
|
24
|
-
hasUnshare: checkUnshare(),
|
|
25
|
-
platform,
|
|
23
|
+
hasUnshare: await checkUnshare(),
|
|
24
|
+
platform: "linux",
|
|
26
25
|
};
|
|
27
26
|
}
|
|
28
|
-
function
|
|
29
|
-
switch (process.platform) {
|
|
30
|
-
case "linux":
|
|
31
|
-
return "linux";
|
|
32
|
-
case "darwin":
|
|
33
|
-
return "darwin";
|
|
34
|
-
default:
|
|
35
|
-
return "other";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
function checkUserNamespace() {
|
|
27
|
+
async function checkUserNamespace() {
|
|
39
28
|
try {
|
|
40
29
|
const max = fs
|
|
41
30
|
.readFileSync("/proc/sys/user/max_user_namespaces", "utf-8")
|
|
42
31
|
.trim();
|
|
43
|
-
|
|
32
|
+
if (parseInt(max, 10) <= 0)
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await execAsync("unshare --user --map-root-user -- true", {
|
|
40
|
+
timeout: 3000,
|
|
41
|
+
});
|
|
42
|
+
return true;
|
|
44
43
|
}
|
|
45
44
|
catch {
|
|
46
45
|
return false;
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
|
-
function checkNamespaceSupport(ns) {
|
|
48
|
+
async function checkNamespaceSupport(ns) {
|
|
50
49
|
if (!fs.existsSync(`/proc/self/ns/${ns}`))
|
|
51
50
|
return false;
|
|
52
51
|
const flagMap = { mnt: "--mount", pid: "--pid", net: "--net" };
|
|
@@ -54,7 +53,7 @@ function checkNamespaceSupport(ns) {
|
|
|
54
53
|
if (!flag)
|
|
55
54
|
return false;
|
|
56
55
|
try {
|
|
57
|
-
|
|
56
|
+
await execAsync(`unshare --user --map-root-user ${flag} ${ns === "pid" ? "--fork" : ""} -- true`, { timeout: 3000 });
|
|
58
57
|
return true;
|
|
59
58
|
}
|
|
60
59
|
catch {
|
|
@@ -79,9 +78,9 @@ function checkSeccomp() {
|
|
|
79
78
|
return false;
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
|
-
function checkUnshare() {
|
|
81
|
+
async function checkUnshare() {
|
|
83
82
|
try {
|
|
84
|
-
|
|
83
|
+
await execAsync("which unshare", { timeout: 3000 });
|
|
85
84
|
return true;
|
|
86
85
|
}
|
|
87
86
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../src/sandbox/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../src/sandbox/namespace.ts"],"names":[],"mappings":"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"}
|
package/dist/sandbox/rootfs.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export interface RootfsOptions {
|
|
|
3
3
|
sandboxId: string;
|
|
4
4
|
workspaceDir: string;
|
|
5
5
|
nodeExecutable?: string;
|
|
6
|
+
/** Directory containing the worker script (src/ or dist/) */
|
|
7
|
+
projectDir?: string;
|
|
6
8
|
additionalBinds?: MountBind[];
|
|
7
9
|
}
|
|
8
10
|
export interface PreparedRootfs {
|
|
@@ -11,5 +13,8 @@ export interface PreparedRootfs {
|
|
|
11
13
|
cleanup(): void;
|
|
12
14
|
}
|
|
13
15
|
export declare function prepareRootfs(options: RootfsOptions): PreparedRootfs;
|
|
14
|
-
export
|
|
16
|
+
export interface MountScriptOptions {
|
|
17
|
+
hasPidNamespace?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function buildMountScript(rootDir: string, mounts: MountBind[], options?: MountScriptOptions): string;
|
|
15
20
|
//# sourceMappingURL=rootfs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rootfs.d.ts","sourceRoot":"","sources":["../../src/sandbox/rootfs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rootfs.d.ts","sourceRoot":"","sources":["../../src/sandbox/rootfs.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,IAAI,IAAI,CAAC;CACjB;AAkBD,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,cAAc,CA2IpE;AAED,MAAM,WAAW,kBAAkB;IACjC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,SAAS,EAAE,EACnB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,MAAM,CAoDR"}
|
package/dist/sandbox/rootfs.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import * as os from "node:os";
|
|
4
|
+
import * as url from "node:url";
|
|
5
|
+
import { escapeShellArg } from "../shared/index.js";
|
|
4
6
|
const SYSTEM_READONLY_PATHS = [
|
|
5
7
|
"/usr",
|
|
6
8
|
"/lib",
|
|
@@ -17,14 +19,18 @@ const SYSTEM_READONLY_PATHS = [
|
|
|
17
19
|
"/etc/group",
|
|
18
20
|
];
|
|
19
21
|
export function prepareRootfs(options) {
|
|
20
|
-
const
|
|
22
|
+
const resolvedWorkspace = path.resolve(options.workspaceDir);
|
|
23
|
+
if (isSensitiveHostPath(resolvedWorkspace)) {
|
|
24
|
+
throw new Error(`workspaceDir "${options.workspaceDir}" references a sensitive host path`);
|
|
25
|
+
}
|
|
26
|
+
const rootDir = path.join(os.tmpdir(), "bulkhead-runtime-rootfs", options.sandboxId);
|
|
21
27
|
fs.mkdirSync(rootDir, { recursive: true });
|
|
22
28
|
const mounts = [];
|
|
23
29
|
for (const sysPath of SYSTEM_READONLY_PATHS) {
|
|
24
|
-
|
|
30
|
+
const stat = safeStat(sysPath);
|
|
31
|
+
if (!stat)
|
|
25
32
|
continue;
|
|
26
33
|
const targetInRoot = path.join(rootDir, sysPath);
|
|
27
|
-
const stat = safeStat(sysPath);
|
|
28
34
|
if (stat?.isDirectory()) {
|
|
29
35
|
fs.mkdirSync(targetInRoot, { recursive: true });
|
|
30
36
|
}
|
|
@@ -36,6 +42,10 @@ export function prepareRootfs(options) {
|
|
|
36
42
|
}
|
|
37
43
|
const nodeExec = options.nodeExecutable ?? process.execPath;
|
|
38
44
|
const nodeDir = path.dirname(nodeExec);
|
|
45
|
+
const resolvedNodeDir = path.resolve(nodeDir);
|
|
46
|
+
if (isSensitiveHostPath(resolvedNodeDir)) {
|
|
47
|
+
throw new Error(`nodeExecutable directory "${nodeDir}" references a sensitive host path`);
|
|
48
|
+
}
|
|
39
49
|
const nodeDirInRoot = path.join(rootDir, nodeDir);
|
|
40
50
|
fs.mkdirSync(nodeDirInRoot, { recursive: true });
|
|
41
51
|
mounts.push({ source: nodeDir, target: nodeDirInRoot, readonly: true });
|
|
@@ -45,6 +55,15 @@ export function prepareRootfs(options) {
|
|
|
45
55
|
fs.mkdirSync(nmInRoot, { recursive: true });
|
|
46
56
|
mounts.push({ source: nodeModulesPath, target: nmInRoot, readonly: true });
|
|
47
57
|
}
|
|
58
|
+
if (options.projectDir) {
|
|
59
|
+
const resolvedProjectDir = path.resolve(options.projectDir);
|
|
60
|
+
if (isSensitiveHostPath(resolvedProjectDir)) {
|
|
61
|
+
throw new Error(`projectDir "${options.projectDir}" references a sensitive host path`);
|
|
62
|
+
}
|
|
63
|
+
const projInRoot = path.join(rootDir, options.projectDir);
|
|
64
|
+
fs.mkdirSync(projInRoot, { recursive: true });
|
|
65
|
+
mounts.push({ source: options.projectDir, target: projInRoot, readonly: true });
|
|
66
|
+
}
|
|
48
67
|
const wsInRoot = path.join(rootDir, options.workspaceDir);
|
|
49
68
|
fs.mkdirSync(wsInRoot, { recursive: true });
|
|
50
69
|
mounts.push({
|
|
@@ -69,17 +88,32 @@ export function prepareRootfs(options) {
|
|
|
69
88
|
const oldRootDir = path.join(rootDir, ".old-root");
|
|
70
89
|
fs.mkdirSync(oldRootDir, { recursive: true });
|
|
71
90
|
if (options.additionalBinds) {
|
|
91
|
+
const resolvedRoot = path.resolve(rootDir);
|
|
72
92
|
for (const bind of options.additionalBinds) {
|
|
93
|
+
const resolvedSource = path.resolve(bind.source);
|
|
94
|
+
if (isSensitiveHostPath(resolvedSource)) {
|
|
95
|
+
throw new Error(`additionalBind source "${bind.source}" references a sensitive host path`);
|
|
96
|
+
}
|
|
73
97
|
const targetInRoot = path.join(rootDir, bind.target);
|
|
74
|
-
const
|
|
75
|
-
if (
|
|
98
|
+
const resolvedTarget = path.resolve(targetInRoot);
|
|
99
|
+
if (!resolvedTarget.startsWith(resolvedRoot + path.sep) && resolvedTarget !== resolvedRoot) {
|
|
100
|
+
throw new Error(`additionalBind target "${bind.target}" resolves outside rootDir`);
|
|
101
|
+
}
|
|
102
|
+
const srcStat2 = safeStat(resolvedSource);
|
|
103
|
+
if (!srcStat2) {
|
|
104
|
+
throw new Error(`additionalBind source "${bind.source}" does not exist`);
|
|
105
|
+
}
|
|
106
|
+
if (srcStat2.isSymbolicLink()) {
|
|
107
|
+
throw new Error(`additionalBind source "${bind.source}" is a symbolic link (not allowed)`);
|
|
108
|
+
}
|
|
109
|
+
if (srcStat2.isDirectory()) {
|
|
76
110
|
fs.mkdirSync(targetInRoot, { recursive: true });
|
|
77
111
|
}
|
|
78
112
|
else {
|
|
79
113
|
fs.mkdirSync(path.dirname(targetInRoot), { recursive: true });
|
|
80
114
|
safeTouch(targetInRoot);
|
|
81
115
|
}
|
|
82
|
-
mounts.push({ source:
|
|
116
|
+
mounts.push({ source: resolvedSource, target: targetInRoot, readonly: bind.readonly });
|
|
83
117
|
}
|
|
84
118
|
}
|
|
85
119
|
return {
|
|
@@ -95,58 +129,108 @@ export function prepareRootfs(options) {
|
|
|
95
129
|
},
|
|
96
130
|
};
|
|
97
131
|
}
|
|
98
|
-
export function buildMountScript(rootDir, mounts,
|
|
132
|
+
export function buildMountScript(rootDir, mounts, options) {
|
|
133
|
+
const esc = escapeShellArg;
|
|
99
134
|
const lines = [
|
|
100
135
|
"#!/bin/sh",
|
|
101
136
|
"set -e",
|
|
102
137
|
"",
|
|
103
|
-
`ROOTDIR
|
|
138
|
+
`ROOTDIR=${esc(rootDir)}`,
|
|
104
139
|
"",
|
|
105
|
-
|
|
140
|
+
"# Make rootDir a mount point for pivot_root",
|
|
106
141
|
'mount --bind "$ROOTDIR" "$ROOTDIR"',
|
|
107
142
|
"",
|
|
108
143
|
];
|
|
109
144
|
for (const mount of mounts) {
|
|
110
145
|
if (mount.readonly) {
|
|
111
|
-
lines.push(`mount --bind
|
|
112
|
-
lines.push(`mount -o remount,ro,bind
|
|
146
|
+
lines.push(`mount --bind ${esc(mount.source)} ${esc(mount.target)}`);
|
|
147
|
+
lines.push(`mount -o remount,ro,bind ${esc(mount.target)}`);
|
|
113
148
|
}
|
|
114
149
|
else {
|
|
115
|
-
lines.push(`mount --bind
|
|
150
|
+
lines.push(`mount --bind ${esc(mount.source)} ${esc(mount.target)}`);
|
|
116
151
|
}
|
|
117
152
|
}
|
|
118
153
|
lines.push("");
|
|
119
|
-
|
|
120
|
-
|
|
154
|
+
if (options?.hasPidNamespace) {
|
|
155
|
+
lines.push("# Mount proc (PID namespace active)");
|
|
156
|
+
lines.push('mount -t proc proc "$ROOTDIR/proc" || { echo "WARNING: proc mount failed" >&2; }');
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
lines.push("# No PID namespace: skip proc mount to prevent host process exposure");
|
|
160
|
+
}
|
|
121
161
|
lines.push("");
|
|
122
162
|
lines.push("# pivot_root: swap root filesystem");
|
|
123
|
-
lines.push(
|
|
124
|
-
lines.push(
|
|
163
|
+
lines.push('cd "$ROOTDIR"');
|
|
164
|
+
lines.push("pivot_root . .old-root");
|
|
125
165
|
lines.push("");
|
|
126
|
-
lines.push("# Unmount old root - fail-closed: abort if
|
|
127
|
-
lines.push("umount
|
|
128
|
-
lines.push(
|
|
129
|
-
lines.push('
|
|
166
|
+
lines.push("# Unmount old root - fail-closed: abort if either unmount fails");
|
|
167
|
+
lines.push("umount /.old-root 2>/dev/null || umount -l /.old-root || { echo 'FATAL: cannot unmount old root' >&2; exit 1; }");
|
|
168
|
+
lines.push("# Verify old root is no longer a mount point");
|
|
169
|
+
lines.push('if mountpoint -q /.old-root 2>/dev/null; then');
|
|
170
|
+
lines.push(' echo "FATAL: old root still mounted after unmount" >&2');
|
|
171
|
+
lines.push(" exit 1");
|
|
172
|
+
lines.push("fi");
|
|
173
|
+
lines.push("# Verify old root directory is empty (fail-closed: ls errors also trigger abort)");
|
|
174
|
+
lines.push('if [ -d "/.old-root" ] && [ -n "$(ls -A /.old-root 2>&1)" ]; then');
|
|
175
|
+
lines.push(' echo "FATAL: old root still accessible after unmount" >&2');
|
|
176
|
+
lines.push(" exit 1");
|
|
130
177
|
lines.push("fi");
|
|
178
|
+
lines.push("rmdir /.old-root 2>/dev/null || true");
|
|
131
179
|
lines.push("");
|
|
132
180
|
return lines.join("\n");
|
|
133
181
|
}
|
|
134
182
|
function findNodeModules() {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
183
|
+
const searchRoots = [
|
|
184
|
+
path.dirname(url.fileURLToPath(import.meta.url)),
|
|
185
|
+
process.cwd(),
|
|
186
|
+
];
|
|
187
|
+
for (const start of searchRoots) {
|
|
188
|
+
let dir = start;
|
|
189
|
+
for (let i = 0; i < 10; i++) {
|
|
190
|
+
const nmPath = path.join(dir, "node_modules");
|
|
191
|
+
const stat = safeStat(nmPath);
|
|
192
|
+
if (stat && stat.isDirectory() && !stat.isSymbolicLink())
|
|
193
|
+
return nmPath;
|
|
194
|
+
const parent = path.dirname(dir);
|
|
195
|
+
if (parent === dir)
|
|
196
|
+
break;
|
|
197
|
+
dir = parent;
|
|
198
|
+
}
|
|
144
199
|
}
|
|
145
200
|
return null;
|
|
146
201
|
}
|
|
202
|
+
const SENSITIVE_HOST_PATHS = new Set([
|
|
203
|
+
"/",
|
|
204
|
+
"/root",
|
|
205
|
+
"/proc",
|
|
206
|
+
"/sys",
|
|
207
|
+
"/dev",
|
|
208
|
+
"/boot",
|
|
209
|
+
"/run",
|
|
210
|
+
"/var/run/docker.sock",
|
|
211
|
+
"/run/docker.sock",
|
|
212
|
+
]);
|
|
213
|
+
const SENSITIVE_HOST_PREFIXES = [
|
|
214
|
+
"/root/",
|
|
215
|
+
"/proc/",
|
|
216
|
+
"/sys/",
|
|
217
|
+
"/home/",
|
|
218
|
+
"/run/",
|
|
219
|
+
"/etc/shadow",
|
|
220
|
+
"/etc/sudoers",
|
|
221
|
+
"/etc/gshadow",
|
|
222
|
+
"/etc/master.passwd",
|
|
223
|
+
"/var/run/docker.sock",
|
|
224
|
+
"/run/docker.sock",
|
|
225
|
+
];
|
|
226
|
+
function isSensitiveHostPath(resolved) {
|
|
227
|
+
if (SENSITIVE_HOST_PATHS.has(resolved))
|
|
228
|
+
return true;
|
|
229
|
+
return SENSITIVE_HOST_PREFIXES.some((prefix) => resolved.startsWith(prefix));
|
|
230
|
+
}
|
|
147
231
|
function safeStat(p) {
|
|
148
232
|
try {
|
|
149
|
-
return fs.
|
|
233
|
+
return fs.lstatSync(p);
|
|
150
234
|
}
|
|
151
235
|
catch {
|
|
152
236
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rootfs.js","sourceRoot":"","sources":["../../src/sandbox/rootfs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"rootfs.js","sourceRoot":"","sources":["../../src/sandbox/rootfs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type SeccompProfile } from "./seccomp.js";
|
|
2
|
+
export declare function ensureSeccompLoader(): string | null;
|
|
3
|
+
export declare function buildSeccompWrapperArgs(profile: SeccompProfile, sandboxId: string, command: string, args: string[]): {
|
|
4
|
+
command: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
profilePath: string;
|
|
7
|
+
} | null;
|
|
8
|
+
export declare function isSeccompAvailable(): boolean;
|
|
9
|
+
//# sourceMappingURL=seccomp-apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seccomp-apply.d.ts","sourceRoot":"","sources":["../../src/sandbox/seccomp-apply.ts"],"names":[],"mappings":"AAKA,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AA6MxE,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAMnD;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,GACb;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAUjE;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
5
|
+
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
6
|
+
import { writeSeccompProfile } from "./seccomp.js";
|
|
7
|
+
const log = createSubsystemLogger("seccomp-apply");
|
|
8
|
+
// Real seccomp-BPF loader: applies PR_SET_NO_NEW_PRIVS + a BPF filter
|
|
9
|
+
// that blocks dangerous syscalls (ptrace, mount, unshare, bpf, etc.)
|
|
10
|
+
// with EPERM. Falls back to no-new-privs only if seccomp() syscall fails.
|
|
11
|
+
const SECCOMP_LOADER_SOURCE = `
|
|
12
|
+
#include <stdio.h>
|
|
13
|
+
#include <stdlib.h>
|
|
14
|
+
#include <string.h>
|
|
15
|
+
#include <unistd.h>
|
|
16
|
+
#include <errno.h>
|
|
17
|
+
#include <stddef.h>
|
|
18
|
+
#include <sys/prctl.h>
|
|
19
|
+
#include <sys/syscall.h>
|
|
20
|
+
#include <linux/seccomp.h>
|
|
21
|
+
#include <linux/filter.h>
|
|
22
|
+
#include <linux/audit.h>
|
|
23
|
+
|
|
24
|
+
#ifndef SECCOMP_SET_MODE_FILTER
|
|
25
|
+
#define SECCOMP_SET_MODE_FILTER 1
|
|
26
|
+
#endif
|
|
27
|
+
|
|
28
|
+
#if defined(__x86_64__)
|
|
29
|
+
#define AUDIT_ARCH_CURRENT AUDIT_ARCH_X86_64
|
|
30
|
+
#elif defined(__aarch64__)
|
|
31
|
+
#define AUDIT_ARCH_CURRENT AUDIT_ARCH_AARCH64
|
|
32
|
+
#else
|
|
33
|
+
#error "Unsupported architecture for seccomp-BPF"
|
|
34
|
+
#endif
|
|
35
|
+
|
|
36
|
+
/* Blocked syscalls matching BLOCKED_SYSCALLS in seccomp.ts */
|
|
37
|
+
static const int BLOCKED[] = {
|
|
38
|
+
#ifdef __NR_ptrace
|
|
39
|
+
__NR_ptrace,
|
|
40
|
+
#endif
|
|
41
|
+
#ifdef __NR_mount
|
|
42
|
+
__NR_mount,
|
|
43
|
+
#endif
|
|
44
|
+
#ifdef __NR_umount2
|
|
45
|
+
__NR_umount2,
|
|
46
|
+
#endif
|
|
47
|
+
#ifdef __NR_pivot_root
|
|
48
|
+
__NR_pivot_root,
|
|
49
|
+
#endif
|
|
50
|
+
#ifdef __NR_chroot
|
|
51
|
+
__NR_chroot,
|
|
52
|
+
#endif
|
|
53
|
+
#ifdef __NR_reboot
|
|
54
|
+
__NR_reboot,
|
|
55
|
+
#endif
|
|
56
|
+
#ifdef __NR_kexec_load
|
|
57
|
+
__NR_kexec_load,
|
|
58
|
+
#endif
|
|
59
|
+
#ifdef __NR_init_module
|
|
60
|
+
__NR_init_module,
|
|
61
|
+
#endif
|
|
62
|
+
#ifdef __NR_finit_module
|
|
63
|
+
__NR_finit_module,
|
|
64
|
+
#endif
|
|
65
|
+
#ifdef __NR_delete_module
|
|
66
|
+
__NR_delete_module,
|
|
67
|
+
#endif
|
|
68
|
+
#ifdef __NR_acct
|
|
69
|
+
__NR_acct,
|
|
70
|
+
#endif
|
|
71
|
+
#ifdef __NR_swapon
|
|
72
|
+
__NR_swapon,
|
|
73
|
+
#endif
|
|
74
|
+
#ifdef __NR_swapoff
|
|
75
|
+
__NR_swapoff,
|
|
76
|
+
#endif
|
|
77
|
+
#ifdef __NR_bpf
|
|
78
|
+
__NR_bpf,
|
|
79
|
+
#endif
|
|
80
|
+
#ifdef __NR_userfaultfd
|
|
81
|
+
__NR_userfaultfd,
|
|
82
|
+
#endif
|
|
83
|
+
#ifdef __NR_perf_event_open
|
|
84
|
+
__NR_perf_event_open,
|
|
85
|
+
#endif
|
|
86
|
+
#ifdef __NR_unshare
|
|
87
|
+
__NR_unshare,
|
|
88
|
+
#endif
|
|
89
|
+
#ifdef __NR_setns
|
|
90
|
+
__NR_setns,
|
|
91
|
+
#endif
|
|
92
|
+
#ifdef __NR_keyctl
|
|
93
|
+
__NR_keyctl,
|
|
94
|
+
#endif
|
|
95
|
+
#ifdef __NR_request_key
|
|
96
|
+
__NR_request_key,
|
|
97
|
+
#endif
|
|
98
|
+
#ifdef __NR_add_key
|
|
99
|
+
__NR_add_key,
|
|
100
|
+
#endif
|
|
101
|
+
#ifdef __NR_process_vm_readv
|
|
102
|
+
__NR_process_vm_readv,
|
|
103
|
+
#endif
|
|
104
|
+
#ifdef __NR_process_vm_writev
|
|
105
|
+
__NR_process_vm_writev,
|
|
106
|
+
#endif
|
|
107
|
+
#ifdef __NR_personality
|
|
108
|
+
__NR_personality,
|
|
109
|
+
#endif
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
#define N (sizeof(BLOCKED)/sizeof(BLOCKED[0]))
|
|
113
|
+
|
|
114
|
+
static int apply_filter(void) {
|
|
115
|
+
/* BPF program: check arch, load nr, for each blocked: jeq->errno, default allow */
|
|
116
|
+
unsigned int len = 4 + N + 2;
|
|
117
|
+
struct sock_filter *f = calloc(len, sizeof(struct sock_filter));
|
|
118
|
+
if (!f) return -1;
|
|
119
|
+
unsigned int i = 0;
|
|
120
|
+
/* [0] load arch */
|
|
121
|
+
f[i++] = (struct sock_filter)BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, arch));
|
|
122
|
+
/* [1] check arch */
|
|
123
|
+
f[i++] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_CURRENT, 1, 0);
|
|
124
|
+
/* [2] kill on wrong arch */
|
|
125
|
+
f[i++] = (struct sock_filter)BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL_PROCESS);
|
|
126
|
+
/* [3] load syscall nr */
|
|
127
|
+
f[i++] = (struct sock_filter)BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, nr));
|
|
128
|
+
/* [4..4+N-1] check each blocked: jt jumps to errno return at [4+N+1] */
|
|
129
|
+
for (unsigned int j = 0; j < N; j++) {
|
|
130
|
+
unsigned int jt = (unsigned int)(N - j); /* distance to errno instr */
|
|
131
|
+
f[i++] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, (unsigned int)BLOCKED[j], jt, 0);
|
|
132
|
+
}
|
|
133
|
+
/* [4+N] allow */
|
|
134
|
+
f[i++] = (struct sock_filter)BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW);
|
|
135
|
+
/* [4+N+1] errno EPERM */
|
|
136
|
+
f[i++] = (struct sock_filter)BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO|(EPERM & SECCOMP_RET_DATA));
|
|
137
|
+
|
|
138
|
+
struct sock_fprog prog = { .len = (unsigned short)i, .filter = f };
|
|
139
|
+
int ret = (int)syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog);
|
|
140
|
+
free(f);
|
|
141
|
+
return ret;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
int main(int argc, char *argv[]) {
|
|
145
|
+
if (argc < 3) {
|
|
146
|
+
fprintf(stderr, "Usage: seccomp-loader <profile.json> <command> [args...]\\n");
|
|
147
|
+
return 1;
|
|
148
|
+
}
|
|
149
|
+
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
|
|
150
|
+
perror("prctl(PR_SET_NO_NEW_PRIVS)");
|
|
151
|
+
return 1;
|
|
152
|
+
}
|
|
153
|
+
if (apply_filter() < 0) {
|
|
154
|
+
fprintf(stderr, "seccomp-loader: BPF filter failed (errno=%d); continuing with no-new-privs only\\n", errno);
|
|
155
|
+
}
|
|
156
|
+
execvp(argv[2], &argv[2]);
|
|
157
|
+
perror("execvp");
|
|
158
|
+
return 1;
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
let loaderBinaryPath = null;
|
|
162
|
+
function getLoaderDir() {
|
|
163
|
+
return path.join(os.tmpdir(), "bulkhead-runtime-seccomp");
|
|
164
|
+
}
|
|
165
|
+
function compileLoader() {
|
|
166
|
+
const dir = getLoaderDir();
|
|
167
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
168
|
+
const binaryPath = path.join(dir, "seccomp-loader");
|
|
169
|
+
const sourcePath = path.join(dir, "seccomp-loader.c");
|
|
170
|
+
if (fs.existsSync(binaryPath)) {
|
|
171
|
+
try {
|
|
172
|
+
const stat = fs.statSync(binaryPath);
|
|
173
|
+
if (stat.isFile() && (stat.mode & 0o100)) {
|
|
174
|
+
return binaryPath;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// recompile
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
fs.writeFileSync(sourcePath, SECCOMP_LOADER_SOURCE, { mode: 0o600 });
|
|
182
|
+
try {
|
|
183
|
+
execSync(`cc -o ${binaryPath} ${sourcePath} -static 2>/dev/null || cc -o ${binaryPath} ${sourcePath}`, {
|
|
184
|
+
timeout: 30_000,
|
|
185
|
+
stdio: "pipe",
|
|
186
|
+
});
|
|
187
|
+
fs.chmodSync(binaryPath, 0o700);
|
|
188
|
+
log.info("seccomp-loader compiled successfully (BPF filter enabled)");
|
|
189
|
+
return binaryPath;
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
log.warn("failed to compile seccomp-loader (cc not available?)", {
|
|
193
|
+
error: String(err),
|
|
194
|
+
});
|
|
195
|
+
try {
|
|
196
|
+
fs.unlinkSync(sourcePath);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
// best effort
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
export function ensureSeccompLoader() {
|
|
205
|
+
if (loaderBinaryPath && fs.existsSync(loaderBinaryPath)) {
|
|
206
|
+
return loaderBinaryPath;
|
|
207
|
+
}
|
|
208
|
+
loaderBinaryPath = compileLoader();
|
|
209
|
+
return loaderBinaryPath;
|
|
210
|
+
}
|
|
211
|
+
export function buildSeccompWrapperArgs(profile, sandboxId, command, args) {
|
|
212
|
+
const loader = ensureSeccompLoader();
|
|
213
|
+
if (!loader)
|
|
214
|
+
return null;
|
|
215
|
+
const profilePath = writeSeccompProfile(profile, sandboxId);
|
|
216
|
+
return {
|
|
217
|
+
command: loader,
|
|
218
|
+
args: [profilePath, command, ...args],
|
|
219
|
+
profilePath,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
export function isSeccompAvailable() {
|
|
223
|
+
if (process.platform !== "linux")
|
|
224
|
+
return false;
|
|
225
|
+
return ensureSeccompLoader() !== null;
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=seccomp-apply.js.map
|