@carderne/sandbox-runtime 0.0.40
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/LICENSE +201 -0
- package/README.md +684 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +163 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/sandbox/generate-seccomp-filter.d.ts +71 -0
- package/dist/sandbox/generate-seccomp-filter.d.ts.map +1 -0
- package/dist/sandbox/generate-seccomp-filter.js +263 -0
- package/dist/sandbox/generate-seccomp-filter.js.map +1 -0
- package/dist/sandbox/http-proxy.d.ts +13 -0
- package/dist/sandbox/http-proxy.d.ts.map +1 -0
- package/dist/sandbox/http-proxy.js +217 -0
- package/dist/sandbox/http-proxy.js.map +1 -0
- package/dist/sandbox/linux-sandbox-utils.d.ts +158 -0
- package/dist/sandbox/linux-sandbox-utils.d.ts.map +1 -0
- package/dist/sandbox/linux-sandbox-utils.js +875 -0
- package/dist/sandbox/linux-sandbox-utils.js.map +1 -0
- package/dist/sandbox/macos-sandbox-utils.d.ts +40 -0
- package/dist/sandbox/macos-sandbox-utils.d.ts.map +1 -0
- package/dist/sandbox/macos-sandbox-utils.js +623 -0
- package/dist/sandbox/macos-sandbox-utils.js.map +1 -0
- package/dist/sandbox/sandbox-config.d.ts +288 -0
- package/dist/sandbox/sandbox-config.d.ts.map +1 -0
- package/dist/sandbox/sandbox-config.js +178 -0
- package/dist/sandbox/sandbox-config.js.map +1 -0
- package/dist/sandbox/sandbox-manager.d.ts +42 -0
- package/dist/sandbox/sandbox-manager.d.ts.map +1 -0
- package/dist/sandbox/sandbox-manager.js +786 -0
- package/dist/sandbox/sandbox-manager.js.map +1 -0
- package/dist/sandbox/sandbox-schemas.d.ts +57 -0
- package/dist/sandbox/sandbox-schemas.d.ts.map +1 -0
- package/dist/sandbox/sandbox-schemas.js +3 -0
- package/dist/sandbox/sandbox-schemas.js.map +1 -0
- package/dist/sandbox/sandbox-utils.d.ts +109 -0
- package/dist/sandbox/sandbox-utils.d.ts.map +1 -0
- package/dist/sandbox/sandbox-utils.js +429 -0
- package/dist/sandbox/sandbox-utils.js.map +1 -0
- package/dist/sandbox/sandbox-violation-store.d.ts +19 -0
- package/dist/sandbox/sandbox-violation-store.d.ts.map +1 -0
- package/dist/sandbox/sandbox-violation-store.js +54 -0
- package/dist/sandbox/sandbox-violation-store.js.map +1 -0
- package/dist/sandbox/socks-proxy.d.ts +13 -0
- package/dist/sandbox/socks-proxy.d.ts.map +1 -0
- package/dist/sandbox/socks-proxy.js +95 -0
- package/dist/sandbox/socks-proxy.js.map +1 -0
- package/dist/utils/config-loader.d.ts +11 -0
- package/dist/utils/config-loader.d.ts.map +1 -0
- package/dist/utils/config-loader.js +60 -0
- package/dist/utils/config-loader.js.map +1 -0
- package/dist/utils/debug.d.ts +7 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +25 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/platform.d.ts +15 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +49 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/utils/ripgrep.d.ts +20 -0
- package/dist/utils/ripgrep.d.ts.map +1 -0
- package/dist/utils/ripgrep.js +42 -0
- package/dist/utils/ripgrep.js.map +1 -0
- package/dist/utils/which.d.ts +9 -0
- package/dist/utils/which.d.ts.map +1 -0
- package/dist/utils/which.js +25 -0
- package/dist/utils/which.js.map +1 -0
- package/dist/vendor/seccomp/arm64/apply-seccomp +0 -0
- package/dist/vendor/seccomp/arm64/unix-block.bpf +0 -0
- package/dist/vendor/seccomp/x64/apply-seccomp +0 -0
- package/dist/vendor/seccomp/x64/unix-block.bpf +0 -0
- package/dist/vendor/seccomp-src/apply-seccomp.c +98 -0
- package/dist/vendor/seccomp-src/seccomp-unix-block.c +97 -0
- package/package.json +88 -0
- package/vendor/seccomp/arm64/apply-seccomp +0 -0
- package/vendor/seccomp/arm64/unix-block.bpf +0 -0
- package/vendor/seccomp/x64/apply-seccomp +0 -0
- package/vendor/seccomp/x64/unix-block.bpf +0 -0
- package/vendor/seccomp-src/apply-seccomp.c +98 -0
- package/vendor/seccomp-src/seccomp-unix-block.c +97 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { Agent, createServer } from 'node:http';
|
|
2
|
+
import { request as httpRequest } from 'node:http';
|
|
3
|
+
import { request as httpsRequest } from 'node:https';
|
|
4
|
+
import { connect } from 'node:net';
|
|
5
|
+
import { URL } from 'node:url';
|
|
6
|
+
import { logForDebugging } from '../utils/debug.js';
|
|
7
|
+
export function createHttpProxyServer(options) {
|
|
8
|
+
const server = createServer();
|
|
9
|
+
// Handle CONNECT requests for HTTPS traffic
|
|
10
|
+
server.on('connect', async (req, socket) => {
|
|
11
|
+
// Attach error handler immediately to prevent unhandled errors
|
|
12
|
+
socket.on('error', err => {
|
|
13
|
+
logForDebugging(`Client socket error: ${err.message}`, { level: 'error' });
|
|
14
|
+
});
|
|
15
|
+
try {
|
|
16
|
+
const [hostname, portStr] = req.url.split(':');
|
|
17
|
+
const port = portStr === undefined ? undefined : parseInt(portStr, 10);
|
|
18
|
+
if (!hostname || !port) {
|
|
19
|
+
logForDebugging(`Invalid CONNECT request: ${req.url}`, {
|
|
20
|
+
level: 'error',
|
|
21
|
+
});
|
|
22
|
+
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const allowed = await options.filter(port, hostname, socket);
|
|
26
|
+
if (!allowed) {
|
|
27
|
+
logForDebugging(`Connection blocked to ${hostname}:${port}`, {
|
|
28
|
+
level: 'error',
|
|
29
|
+
});
|
|
30
|
+
socket.end('HTTP/1.1 403 Forbidden\r\n' +
|
|
31
|
+
'Content-Type: text/plain\r\n' +
|
|
32
|
+
'X-Proxy-Error: blocked-by-allowlist\r\n' +
|
|
33
|
+
'\r\n' +
|
|
34
|
+
'Connection blocked by network allowlist');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Check if this host should be routed through a MITM proxy
|
|
38
|
+
const mitmSocketPath = options.getMitmSocketPath?.(hostname);
|
|
39
|
+
if (mitmSocketPath) {
|
|
40
|
+
// Route through MITM proxy via Unix socket
|
|
41
|
+
logForDebugging(`Routing CONNECT ${hostname}:${port} through MITM proxy at ${mitmSocketPath}`);
|
|
42
|
+
const mitmSocket = connect({ path: mitmSocketPath }, () => {
|
|
43
|
+
// Send CONNECT request to the MITM proxy
|
|
44
|
+
mitmSocket.write(`CONNECT ${hostname}:${port} HTTP/1.1\r\n` +
|
|
45
|
+
`Host: ${hostname}:${port}\r\n` +
|
|
46
|
+
'\r\n');
|
|
47
|
+
});
|
|
48
|
+
// Buffer to accumulate the MITM proxy's response
|
|
49
|
+
let responseBuffer = '';
|
|
50
|
+
const onMitmData = (chunk) => {
|
|
51
|
+
responseBuffer += chunk.toString();
|
|
52
|
+
// Check if we've received the full HTTP response headers
|
|
53
|
+
const headerEndIndex = responseBuffer.indexOf('\r\n\r\n');
|
|
54
|
+
if (headerEndIndex !== -1) {
|
|
55
|
+
// Remove data listener, we're done parsing the response
|
|
56
|
+
mitmSocket.removeListener('data', onMitmData);
|
|
57
|
+
// Check if MITM proxy accepted the connection
|
|
58
|
+
const statusLine = responseBuffer.substring(0, responseBuffer.indexOf('\r\n'));
|
|
59
|
+
if (statusLine.includes(' 200 ')) {
|
|
60
|
+
// Connection established, now pipe data between client and MITM
|
|
61
|
+
socket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
|
|
62
|
+
// If there's any data after the headers, write it to the client
|
|
63
|
+
const remainingData = responseBuffer.substring(headerEndIndex + 4);
|
|
64
|
+
if (remainingData.length > 0) {
|
|
65
|
+
socket.write(remainingData);
|
|
66
|
+
}
|
|
67
|
+
mitmSocket.pipe(socket);
|
|
68
|
+
socket.pipe(mitmSocket);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
logForDebugging(`MITM proxy rejected CONNECT: ${statusLine}`, {
|
|
72
|
+
level: 'error',
|
|
73
|
+
});
|
|
74
|
+
socket.end('HTTP/1.1 502 Bad Gateway\r\n\r\n');
|
|
75
|
+
mitmSocket.destroy();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
mitmSocket.on('data', onMitmData);
|
|
80
|
+
mitmSocket.on('error', err => {
|
|
81
|
+
logForDebugging(`MITM proxy connection failed: ${err.message}`, {
|
|
82
|
+
level: 'error',
|
|
83
|
+
});
|
|
84
|
+
socket.end('HTTP/1.1 502 Bad Gateway\r\n\r\n');
|
|
85
|
+
});
|
|
86
|
+
socket.on('error', err => {
|
|
87
|
+
logForDebugging(`Client socket error: ${err.message}`, {
|
|
88
|
+
level: 'error',
|
|
89
|
+
});
|
|
90
|
+
mitmSocket.destroy();
|
|
91
|
+
});
|
|
92
|
+
socket.on('end', () => mitmSocket.end());
|
|
93
|
+
mitmSocket.on('end', () => socket.end());
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Direct connection (original behavior)
|
|
97
|
+
const serverSocket = connect(port, hostname, () => {
|
|
98
|
+
socket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
|
|
99
|
+
serverSocket.pipe(socket);
|
|
100
|
+
socket.pipe(serverSocket);
|
|
101
|
+
});
|
|
102
|
+
serverSocket.on('error', err => {
|
|
103
|
+
logForDebugging(`CONNECT tunnel failed: ${err.message}`, {
|
|
104
|
+
level: 'error',
|
|
105
|
+
});
|
|
106
|
+
socket.end('HTTP/1.1 502 Bad Gateway\r\n\r\n');
|
|
107
|
+
});
|
|
108
|
+
socket.on('error', err => {
|
|
109
|
+
logForDebugging(`Client socket error: ${err.message}`, {
|
|
110
|
+
level: 'error',
|
|
111
|
+
});
|
|
112
|
+
serverSocket.destroy();
|
|
113
|
+
});
|
|
114
|
+
socket.on('end', () => serverSocket.end());
|
|
115
|
+
serverSocket.on('end', () => socket.end());
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
logForDebugging(`Error handling CONNECT: ${err}`, { level: 'error' });
|
|
120
|
+
socket.end('HTTP/1.1 500 Internal Server Error\r\n\r\n');
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
// Handle regular HTTP requests
|
|
124
|
+
server.on('request', async (req, res) => {
|
|
125
|
+
try {
|
|
126
|
+
const url = new URL(req.url);
|
|
127
|
+
const hostname = url.hostname;
|
|
128
|
+
const port = url.port
|
|
129
|
+
? parseInt(url.port, 10)
|
|
130
|
+
: url.protocol === 'https:'
|
|
131
|
+
? 443
|
|
132
|
+
: 80;
|
|
133
|
+
const allowed = await options.filter(port, hostname, req.socket);
|
|
134
|
+
if (!allowed) {
|
|
135
|
+
logForDebugging(`HTTP request blocked to ${hostname}:${port}`, {
|
|
136
|
+
level: 'error',
|
|
137
|
+
});
|
|
138
|
+
res.writeHead(403, {
|
|
139
|
+
'Content-Type': 'text/plain',
|
|
140
|
+
'X-Proxy-Error': 'blocked-by-allowlist',
|
|
141
|
+
});
|
|
142
|
+
res.end('Connection blocked by network allowlist');
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Check if this host should be routed through a MITM proxy
|
|
146
|
+
const mitmSocketPath = options.getMitmSocketPath?.(hostname);
|
|
147
|
+
if (mitmSocketPath) {
|
|
148
|
+
// Route through MITM proxy via Unix socket
|
|
149
|
+
// Use an agent that connects via the Unix socket
|
|
150
|
+
logForDebugging(`Routing HTTP ${req.method} ${hostname}:${port} through MITM proxy at ${mitmSocketPath}`);
|
|
151
|
+
const mitmAgent = new Agent({
|
|
152
|
+
// @ts-expect-error - socketPath is valid but not in types
|
|
153
|
+
socketPath: mitmSocketPath,
|
|
154
|
+
});
|
|
155
|
+
// Send request to MITM proxy with full URL (proxy-style request)
|
|
156
|
+
const proxyReq = httpRequest({
|
|
157
|
+
agent: mitmAgent,
|
|
158
|
+
// For proxy requests, path should be the full URL
|
|
159
|
+
path: req.url,
|
|
160
|
+
method: req.method,
|
|
161
|
+
headers: {
|
|
162
|
+
...req.headers,
|
|
163
|
+
host: url.host,
|
|
164
|
+
},
|
|
165
|
+
}, proxyRes => {
|
|
166
|
+
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
167
|
+
proxyRes.pipe(res);
|
|
168
|
+
});
|
|
169
|
+
proxyReq.on('error', err => {
|
|
170
|
+
logForDebugging(`MITM proxy request failed: ${err.message}`, {
|
|
171
|
+
level: 'error',
|
|
172
|
+
});
|
|
173
|
+
if (!res.headersSent) {
|
|
174
|
+
res.writeHead(502, { 'Content-Type': 'text/plain' });
|
|
175
|
+
res.end('Bad Gateway');
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
req.pipe(proxyReq);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// Direct request (original behavior)
|
|
182
|
+
// Choose http or https module
|
|
183
|
+
const requestFn = url.protocol === 'https:' ? httpsRequest : httpRequest;
|
|
184
|
+
const proxyReq = requestFn({
|
|
185
|
+
hostname,
|
|
186
|
+
port,
|
|
187
|
+
path: url.pathname + url.search,
|
|
188
|
+
method: req.method,
|
|
189
|
+
headers: {
|
|
190
|
+
...req.headers,
|
|
191
|
+
host: url.host,
|
|
192
|
+
},
|
|
193
|
+
}, proxyRes => {
|
|
194
|
+
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
195
|
+
proxyRes.pipe(res);
|
|
196
|
+
});
|
|
197
|
+
proxyReq.on('error', err => {
|
|
198
|
+
logForDebugging(`Proxy request failed: ${err.message}`, {
|
|
199
|
+
level: 'error',
|
|
200
|
+
});
|
|
201
|
+
if (!res.headersSent) {
|
|
202
|
+
res.writeHead(502, { 'Content-Type': 'text/plain' });
|
|
203
|
+
res.end('Bad Gateway');
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
req.pipe(proxyReq);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
logForDebugging(`Error handling HTTP request: ${err}`, { level: 'error' });
|
|
211
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
212
|
+
res.end('Internal Server Error');
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
return server;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=http-proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-proxy.js","sourceRoot":"","sources":["../../src/sandbox/http-proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { ChildProcess } from 'node:child_process';
|
|
2
|
+
import type { FsReadRestrictionConfig, FsWriteRestrictionConfig } from './sandbox-schemas.js';
|
|
3
|
+
export interface LinuxNetworkBridgeContext {
|
|
4
|
+
httpSocketPath: string;
|
|
5
|
+
socksSocketPath: string;
|
|
6
|
+
httpBridgeProcess: ChildProcess;
|
|
7
|
+
socksBridgeProcess: ChildProcess;
|
|
8
|
+
httpProxyPort: number;
|
|
9
|
+
socksProxyPort: number;
|
|
10
|
+
}
|
|
11
|
+
export interface LinuxSandboxParams {
|
|
12
|
+
command: string;
|
|
13
|
+
needsNetworkRestriction: boolean;
|
|
14
|
+
httpSocketPath?: string;
|
|
15
|
+
socksSocketPath?: string;
|
|
16
|
+
httpProxyPort?: number;
|
|
17
|
+
socksProxyPort?: number;
|
|
18
|
+
readConfig?: FsReadRestrictionConfig;
|
|
19
|
+
writeConfig?: FsWriteRestrictionConfig;
|
|
20
|
+
enableWeakerNestedSandbox?: boolean;
|
|
21
|
+
allowAllUnixSockets?: boolean;
|
|
22
|
+
binShell?: string;
|
|
23
|
+
ripgrepConfig?: {
|
|
24
|
+
command: string;
|
|
25
|
+
args?: string[];
|
|
26
|
+
};
|
|
27
|
+
/** Maximum directory depth to search for dangerous files (default: 3) */
|
|
28
|
+
mandatoryDenySearchDepth?: number;
|
|
29
|
+
/** Allow writes to .git/config files (default: false) */
|
|
30
|
+
allowGitConfig?: boolean;
|
|
31
|
+
/** Custom seccomp binary paths */
|
|
32
|
+
seccompConfig?: {
|
|
33
|
+
bpfPath?: string;
|
|
34
|
+
applyPath?: string;
|
|
35
|
+
};
|
|
36
|
+
/** Abort signal to cancel the ripgrep scan */
|
|
37
|
+
abortSignal?: AbortSignal;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Clean up mount point files created by bwrap for non-existent deny paths.
|
|
41
|
+
*
|
|
42
|
+
* When protecting non-existent deny paths, bwrap creates empty files on the
|
|
43
|
+
* host filesystem as mount points for --ro-bind. These files persist after
|
|
44
|
+
* bwrap exits. This function removes them.
|
|
45
|
+
*
|
|
46
|
+
* This should be called after each sandboxed command completes to prevent
|
|
47
|
+
* ghost dotfiles (e.g. .bashrc, .gitconfig) from appearing in the working
|
|
48
|
+
* directory. It is also called automatically on process exit as a safety net.
|
|
49
|
+
*
|
|
50
|
+
* Safe to call at any time — it only removes files that were tracked during
|
|
51
|
+
* generateFilesystemArgs() and skips any that no longer exist.
|
|
52
|
+
*/
|
|
53
|
+
export declare function cleanupBwrapMountPoints(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Detailed status of Linux sandbox dependencies
|
|
56
|
+
*/
|
|
57
|
+
export type LinuxDependencyStatus = {
|
|
58
|
+
hasBwrap: boolean;
|
|
59
|
+
hasSocat: boolean;
|
|
60
|
+
hasSeccompBpf: boolean;
|
|
61
|
+
hasSeccompApply: boolean;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Result of checking sandbox dependencies
|
|
65
|
+
*/
|
|
66
|
+
export type SandboxDependencyCheck = {
|
|
67
|
+
warnings: string[];
|
|
68
|
+
errors: string[];
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Get detailed status of Linux sandbox dependencies
|
|
72
|
+
*/
|
|
73
|
+
export declare function getLinuxDependencyStatus(seccompConfig?: {
|
|
74
|
+
bpfPath?: string;
|
|
75
|
+
applyPath?: string;
|
|
76
|
+
}): LinuxDependencyStatus;
|
|
77
|
+
/**
|
|
78
|
+
* Check sandbox dependencies and return structured result
|
|
79
|
+
*/
|
|
80
|
+
export declare function checkLinuxDependencies(seccompConfig?: {
|
|
81
|
+
bpfPath?: string;
|
|
82
|
+
applyPath?: string;
|
|
83
|
+
}): SandboxDependencyCheck;
|
|
84
|
+
/**
|
|
85
|
+
* Initialize the Linux network bridge for sandbox networking
|
|
86
|
+
*
|
|
87
|
+
* ARCHITECTURE NOTE:
|
|
88
|
+
* Linux network sandboxing uses bwrap --unshare-net which creates a completely isolated
|
|
89
|
+
* network namespace with NO network access. To enable network access, we:
|
|
90
|
+
*
|
|
91
|
+
* 1. Host side: Run socat bridges that listen on Unix sockets and forward to host proxy servers
|
|
92
|
+
* - HTTP bridge: Unix socket -> host HTTP proxy (for HTTP/HTTPS traffic)
|
|
93
|
+
* - SOCKS bridge: Unix socket -> host SOCKS5 proxy (for SSH/git traffic)
|
|
94
|
+
*
|
|
95
|
+
* 2. Sandbox side: Bind the Unix sockets into the isolated namespace and run socat listeners
|
|
96
|
+
* - HTTP listener on port 3128 -> HTTP Unix socket -> host HTTP proxy
|
|
97
|
+
* - SOCKS listener on port 1080 -> SOCKS Unix socket -> host SOCKS5 proxy
|
|
98
|
+
*
|
|
99
|
+
* 3. Configure environment:
|
|
100
|
+
* - HTTP_PROXY=http://localhost:3128 for HTTP/HTTPS tools
|
|
101
|
+
* - GIT_SSH_COMMAND with socat for SSH through SOCKS5
|
|
102
|
+
*
|
|
103
|
+
* LIMITATION: Unlike macOS sandbox which can enforce domain-based allowlists at the kernel level,
|
|
104
|
+
* Linux's --unshare-net provides only all-or-nothing network isolation. Domain filtering happens
|
|
105
|
+
* at the host proxy level, not the sandbox boundary. This means network restrictions on Linux
|
|
106
|
+
* depend on the proxy's filtering capabilities.
|
|
107
|
+
*
|
|
108
|
+
* DEPENDENCIES: Requires bwrap (bubblewrap) and socat
|
|
109
|
+
*/
|
|
110
|
+
export declare function initializeLinuxNetworkBridge(httpProxyPort: number, socksProxyPort: number): Promise<LinuxNetworkBridgeContext>;
|
|
111
|
+
/**
|
|
112
|
+
* Wrap a command with sandbox restrictions on Linux
|
|
113
|
+
*
|
|
114
|
+
* UNIX SOCKET BLOCKING (APPLY-SECCOMP):
|
|
115
|
+
* This implementation uses a custom apply-seccomp binary to block Unix domain socket
|
|
116
|
+
* creation for user commands while allowing network infrastructure:
|
|
117
|
+
*
|
|
118
|
+
* Stage 1: Outer bwrap - Network and filesystem isolation (NO seccomp)
|
|
119
|
+
* - Bubblewrap starts with isolated network namespace (--unshare-net)
|
|
120
|
+
* - Bubblewrap applies PID namespace isolation (--unshare-pid and --proc)
|
|
121
|
+
* - Filesystem restrictions are applied (read-only mounts, bind mounts, etc.)
|
|
122
|
+
* - Socat processes start and connect to Unix socket bridges (can use socket(AF_UNIX, ...))
|
|
123
|
+
*
|
|
124
|
+
* Stage 2: apply-seccomp - Seccomp filter application (ONLY seccomp)
|
|
125
|
+
* - apply-seccomp binary applies seccomp filter via prctl(PR_SET_SECCOMP)
|
|
126
|
+
* - Sets PR_SET_NO_NEW_PRIVS to allow seccomp without root
|
|
127
|
+
* - Execs user command with seccomp active (cannot create new Unix sockets)
|
|
128
|
+
*
|
|
129
|
+
* This solves the conflict between:
|
|
130
|
+
* - Security: Blocking arbitrary Unix socket creation in user commands
|
|
131
|
+
* - Functionality: Network sandboxing requires socat to call socket(AF_UNIX, ...) for bridge connections
|
|
132
|
+
*
|
|
133
|
+
* The seccomp-bpf filter blocks socket(AF_UNIX, ...) syscalls, preventing:
|
|
134
|
+
* - Creating new Unix domain socket file descriptors
|
|
135
|
+
*
|
|
136
|
+
* Security limitations:
|
|
137
|
+
* - Does NOT block operations (bind, connect, sendto, etc.) on inherited Unix socket FDs
|
|
138
|
+
* - Does NOT prevent passing Unix socket FDs via SCM_RIGHTS
|
|
139
|
+
* - For most sandboxing use cases, blocking socket creation is sufficient
|
|
140
|
+
*
|
|
141
|
+
* The filter allows:
|
|
142
|
+
* - All TCP/UDP sockets (AF_INET, AF_INET6) for normal network operations
|
|
143
|
+
* - All other syscalls
|
|
144
|
+
*
|
|
145
|
+
* PLATFORM NOTE:
|
|
146
|
+
* The allowUnixSockets configuration is not path-based on Linux (unlike macOS)
|
|
147
|
+
* because seccomp-bpf cannot inspect user-space memory to read socket paths.
|
|
148
|
+
*
|
|
149
|
+
* Requirements for seccomp filtering:
|
|
150
|
+
* - Pre-built apply-seccomp binaries are included for x64 and ARM64
|
|
151
|
+
* - Pre-generated BPF filters are included for x64 and ARM64
|
|
152
|
+
* - Other architectures are not currently supported (no apply-seccomp binary available)
|
|
153
|
+
* - To use sandboxing without Unix socket blocking on unsupported architectures,
|
|
154
|
+
* set allowAllUnixSockets: true in your configuration
|
|
155
|
+
* Dependencies are checked by checkLinuxDependencies() before enabling the sandbox.
|
|
156
|
+
*/
|
|
157
|
+
export declare function wrapCommandWithSandboxLinux(params: LinuxSandboxParams): Promise<string>;
|
|
158
|
+
//# sourceMappingURL=linux-sandbox-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linux-sandbox-utils.d.ts","sourceRoot":"","sources":["../../src/sandbox/linux-sandbox-utils.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAYtD,OAAO,KAAK,EACV,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,sBAAsB,CAAA;AAQ7B,MAAM,WAAW,yBAAyB;IACxC,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,YAAY,CAAA;IAC/B,kBAAkB,EAAE,YAAY,CAAA;IAChC,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,uBAAuB,EAAE,OAAO,CAAA;IAChC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,uBAAuB,CAAA;IACpC,WAAW,CAAC,EAAE,wBAAwB,CAAA;IACtC,yBAAyB,CAAC,EAAE,OAAO,CAAA;IACnC,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IACpD,yEAAyE;IACzE,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,yDAAyD;IACzD,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,kCAAkC;IAClC,aAAa,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACxD,8CAA8C;IAC9C,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAiQD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CA2B9C;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,aAAa,EAAE,OAAO,CAAA;IACtB,eAAe,EAAE,OAAO,CAAA;CACzB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,CAAC,EAAE;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,qBAAqB,CAQxB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,CAAC,EAAE;IACrD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,sBAAsB,CAezB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,4BAA4B,CAChD,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,yBAAyB,CAAC,CA2HpC;AAqUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,MAAM,CAAC,CAwPjB"}
|