@landstrip/landstrip 0.16.23 → 0.17.0
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 +66 -22
- package/lib/index.d.ts +151 -0
- package/package.json +12 -6
package/README.md
CHANGED
|
@@ -42,8 +42,9 @@ statically, e.g. for many filesystem mutator syscalls, or when denials must be
|
|
|
42
42
|
reported back to the launcher. The broker intercepts `openat`/`openat2` via
|
|
43
43
|
seccomp user-notifications, resolves the real path, and validates it
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
Landlock and seccomp cover mostly disjoint filesystem operations: Landlock
|
|
46
|
+
handles kernel-enforced path access, while seccomp mediates unsupported mutators
|
|
47
|
+
and reports broker decisions.
|
|
47
48
|
|
|
48
49
|
### Windows
|
|
49
50
|
|
|
@@ -135,18 +136,20 @@ For a filesystem-only sandbox with unrestricted direct network access, set:
|
|
|
135
136
|
policy enforcement in place. On Windows this grants the AppContainer its network
|
|
136
137
|
capabilities; without it the container denies all network access.
|
|
137
138
|
|
|
138
|
-
##
|
|
139
|
+
## Traps
|
|
139
140
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
Every landstrip event — a sandbox denial, and every failure that keeps the tool
|
|
142
|
+
from running — is reported as a JSON object, one per line, with a fixed `kind`
|
|
143
|
+
discriminant and a stable `code`. Consumers route on `kind` for the shape of the
|
|
144
|
+
record and on `code` for what happened. Failure traps and completed denial traps
|
|
145
|
+
go to standard error by default. On Linux, pending query traps go only to
|
|
146
|
+
`--trap-fd FD`, which writes them to an already-open descriptor.
|
|
144
147
|
|
|
145
148
|
```sh
|
|
146
149
|
landstrip --trap-fd 3 -p policy.json cargo test 3>landstrip-traps.txt
|
|
147
150
|
```
|
|
148
151
|
|
|
149
|
-
The
|
|
152
|
+
The trap kinds are:
|
|
150
153
|
|
|
151
154
|
- `filesystem` (`code` `FILESYSTEM_DENIED`): `operation` is `read` or `write`,
|
|
152
155
|
`path` is the resolved path, `requested_path` is the tool's original path when
|
|
@@ -154,21 +157,43 @@ The two trap kinds are:
|
|
|
154
157
|
`process` carry routing context.
|
|
155
158
|
- `network` (`code` `NETWORK_DENIED`): `operation` is `connect` or `bind` and
|
|
156
159
|
`target` is `address:port`, with `syscall`, `errno`, and `process` context.
|
|
157
|
-
|
|
158
|
-
`
|
|
159
|
-
|
|
160
|
+
- `launch` (`code` `LAUNCH_FAILED`): the sandbox was installed but the tool did
|
|
161
|
+
not start. `program` is the tool, `errno` its symbolic errno where the platform
|
|
162
|
+
has one, and `message` the system's text.
|
|
163
|
+
- `usage` (`code` `USAGE_ERROR`): the command line was rejected. Exits with
|
|
164
|
+
status 2, and reaches standard error only — the trap descriptor is part of the
|
|
165
|
+
arguments that failed to parse.
|
|
166
|
+
- `internal`: everything that fails before the tool runs. `code` names the stage:
|
|
167
|
+
`POLICY_PARSE_FAILED`, `POLICY_IO_FAILED`, `SANDBOX_SETUP_FAILED`,
|
|
168
|
+
`SUPERVISE_FAILED`, `PLATFORM_UNSUPPORTED`, `INTEGER_TOO_LARGE`, a
|
|
169
|
+
`POLICY_*` validation rejection (`POLICY_UNRESTRICTED_READ`,
|
|
170
|
+
`POLICY_TCP_BIND_UNSUPPORTED`, `POLICY_UNIX_SOCKET_UNSUPPORTED`,
|
|
171
|
+
`POLICY_UNIX_SOCKET_PATH`, `POLICY_DENY_WRITE_SYMLINK_ANCESTOR`,
|
|
172
|
+
`POLICY_INVALID_PORT`, `POLICY_EMPTY_PATH`, `POLICY_HOME_UNAVAILABLE`,
|
|
173
|
+
`POLICY_TRAVERSAL_DEPTH`), or `INTERNAL_ERROR` for a failure the code space
|
|
174
|
+
does not name.
|
|
175
|
+
|
|
176
|
+
A code names the stage that failed, not the operating system that reported it:
|
|
177
|
+
the same `LAUNCH_FAILED` or `SANDBOX_SETUP_FAILED` is raised by every backend
|
|
178
|
+
that has that stage. The platform detail rides along in the record instead.
|
|
179
|
+
|
|
180
|
+
`mechanism` records the kernel layer an event is attributed to: `landlock`,
|
|
181
|
+
`seccomp`, `seatbelt`, or `appcontainer`. Per-denial traps are always `seccomp`,
|
|
182
|
+
the only layer with a per-denial callback; Landlock enforces in-kernel without
|
|
183
|
+
one. `SANDBOX_SETUP_FAILED` carries the mechanism that could not be installed.
|
|
184
|
+
|
|
185
|
+
`reason` is a platform-independent classification of a filesystem decision,
|
|
186
|
+
derived from the policy and the requested path:
|
|
160
187
|
|
|
161
188
|
- `allow_miss`: the path matched no allow root and was denied by default.
|
|
162
189
|
- `deny_match`: the path matched an explicit deny root that overrides an allow.
|
|
163
190
|
|
|
164
|
-
`mechanism` records the kernel layer that detected the denial. Per-denial traps
|
|
165
|
-
are always `seccomp`, the only layer with a per-denial callback; Landlock
|
|
166
|
-
enforces in-kernel without one.
|
|
167
|
-
|
|
168
191
|
```json
|
|
169
192
|
{
|
|
170
193
|
"kind": "filesystem",
|
|
171
194
|
"code": "FILESYSTEM_DENIED",
|
|
195
|
+
"state": "info",
|
|
196
|
+
"query_id": "0",
|
|
172
197
|
"operation": "write",
|
|
173
198
|
"path": "/repo/out",
|
|
174
199
|
"requested_path": "out",
|
|
@@ -183,6 +208,8 @@ enforces in-kernel without one.
|
|
|
183
208
|
{
|
|
184
209
|
"kind": "network",
|
|
185
210
|
"code": "NETWORK_DENIED",
|
|
211
|
+
"state": "info",
|
|
212
|
+
"query_id": "0",
|
|
186
213
|
"operation": "connect",
|
|
187
214
|
"target": "127.0.0.1:9999",
|
|
188
215
|
"syscall": "connect",
|
|
@@ -190,15 +217,32 @@ enforces in-kernel without one.
|
|
|
190
217
|
"mechanism": "seccomp",
|
|
191
218
|
"process": { "pid": 1234, "exe": "/usr/bin/nc", "cwd": "/repo" }
|
|
192
219
|
}
|
|
220
|
+
{
|
|
221
|
+
"kind": "launch",
|
|
222
|
+
"code": "LAUNCH_FAILED",
|
|
223
|
+
"program": "/usr/bin/cargo",
|
|
224
|
+
"errno": "ENOENT",
|
|
225
|
+
"message": "No such file or directory (os error 2)"
|
|
226
|
+
}
|
|
227
|
+
{
|
|
228
|
+
"kind": "internal",
|
|
229
|
+
"code": "SANDBOX_SETUP_FAILED",
|
|
230
|
+
"mechanism": "landlock",
|
|
231
|
+
"message": "not enforced by the kernel (Linux 5.13+ with CONFIG_SECURITY_LANDLOCK required, and not disabled via the lsm= boot parameter)"
|
|
232
|
+
}
|
|
193
233
|
```
|
|
194
234
|
|
|
195
|
-
|
|
196
|
-
otherwise quiet on success — standard error belongs to landstrip, standard
|
|
197
|
-
output to the sandboxed tool.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
235
|
+
Denial traps are informational; the configured policy always applies. landstrip
|
|
236
|
+
is otherwise quiet on success — standard error belongs to landstrip, standard
|
|
237
|
+
output to the sandboxed tool. Failure traps are accompanied by a human-readable
|
|
238
|
+
log line; the JSON is what machines should read. Usage errors exit with status 2,
|
|
239
|
+
every other landstrip failure with 1; the tool's own status is passed through
|
|
240
|
+
otherwise.
|
|
241
|
+
|
|
242
|
+
Writing to `--trap-fd` is best-effort: it needs an already-open descriptor (3 or
|
|
243
|
+
greater; 0-2 are reserved), and if the write fails the trap is dropped while the
|
|
244
|
+
policy stays in effect. On Linux, a broker launch failure also reaches
|
|
245
|
+
`--trap-fd` while the descriptor remains open.
|
|
202
246
|
|
|
203
247
|
## Development
|
|
204
248
|
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
/** The kernel layer an event is attributed to. */
|
|
4
|
+
export type LandstripMechanism =
|
|
5
|
+
| 'landlock'
|
|
6
|
+
| 'seccomp'
|
|
7
|
+
| 'seatbelt'
|
|
8
|
+
| 'appcontainer';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* `query` holds the syscall until the launcher answers with a
|
|
12
|
+
* {@link LandstripControlResponse}; `info` is terminal. Queries need a socket on
|
|
13
|
+
* `--trap-fd`, and only the Linux broker raises them.
|
|
14
|
+
*/
|
|
15
|
+
export type LandstripTrapState = 'query' | 'info';
|
|
16
|
+
|
|
17
|
+
/** Why a filesystem access was mediated. */
|
|
18
|
+
export type LandstripTrapReason = 'allow_miss' | 'deny_match';
|
|
19
|
+
|
|
20
|
+
export interface LandstripProcess {
|
|
21
|
+
pid: number;
|
|
22
|
+
exe: string | null;
|
|
23
|
+
cwd: string | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A filesystem access the policy denies. */
|
|
27
|
+
export interface LandstripFilesystemTrap {
|
|
28
|
+
kind: 'filesystem';
|
|
29
|
+
code: 'FILESYSTEM_DENIED';
|
|
30
|
+
state: LandstripTrapState;
|
|
31
|
+
/** Decimal `u64`; `"0"` marks a terminal `info` event. */
|
|
32
|
+
query_id: string;
|
|
33
|
+
operation: 'read' | 'write';
|
|
34
|
+
/** The resolved path. */
|
|
35
|
+
path: string;
|
|
36
|
+
/** The path the tool asked for, before resolution. */
|
|
37
|
+
requested_path: string;
|
|
38
|
+
syscall: string;
|
|
39
|
+
errno: string;
|
|
40
|
+
flags: string[];
|
|
41
|
+
reason: LandstripTrapReason;
|
|
42
|
+
/** The policy edit that would permit this access. */
|
|
43
|
+
suggested_grant: { allowRead?: string; allowWrite?: string };
|
|
44
|
+
process: LandstripProcess;
|
|
45
|
+
mechanism: 'seccomp';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A network access the policy denies. */
|
|
49
|
+
export interface LandstripNetworkTrap {
|
|
50
|
+
kind: 'network';
|
|
51
|
+
code: 'NETWORK_DENIED';
|
|
52
|
+
state: LandstripTrapState;
|
|
53
|
+
/** Decimal `u64`; `"0"` marks a terminal `info` event. */
|
|
54
|
+
query_id: string;
|
|
55
|
+
operation: 'connect' | 'bind';
|
|
56
|
+
/** `address:port`. */
|
|
57
|
+
target: string;
|
|
58
|
+
syscall: string;
|
|
59
|
+
errno: string;
|
|
60
|
+
mechanism: 'seccomp';
|
|
61
|
+
process: LandstripProcess;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The sandbox was installed but the tool did not start. */
|
|
65
|
+
export interface LandstripLaunchTrap {
|
|
66
|
+
kind: 'launch';
|
|
67
|
+
code: 'LAUNCH_FAILED';
|
|
68
|
+
program: string;
|
|
69
|
+
/** Absent where the platform has no POSIX errno for the failure. */
|
|
70
|
+
errno?: string;
|
|
71
|
+
message: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** The command line was rejected. Reaches stderr only; landstrip exits 2. */
|
|
75
|
+
export interface LandstripUsageTrap {
|
|
76
|
+
kind: 'usage';
|
|
77
|
+
code: 'USAGE_ERROR';
|
|
78
|
+
message: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A policy the platform sandbox cannot enforce, rejected before launch. */
|
|
82
|
+
export type LandstripPolicyErrorCode =
|
|
83
|
+
| 'POLICY_PARSE_FAILED'
|
|
84
|
+
| 'POLICY_IO_FAILED'
|
|
85
|
+
| 'POLICY_UNRESTRICTED_READ'
|
|
86
|
+
| 'POLICY_TCP_BIND_UNSUPPORTED'
|
|
87
|
+
| 'POLICY_UNIX_SOCKET_UNSUPPORTED'
|
|
88
|
+
| 'POLICY_UNIX_SOCKET_PATH'
|
|
89
|
+
| 'POLICY_DENY_WRITE_SYMLINK_ANCESTOR'
|
|
90
|
+
| 'POLICY_INVALID_PORT'
|
|
91
|
+
| 'POLICY_EMPTY_PATH'
|
|
92
|
+
| 'POLICY_HOME_UNAVAILABLE'
|
|
93
|
+
| 'POLICY_TRAVERSAL_DEPTH';
|
|
94
|
+
|
|
95
|
+
/** The stage that failed before the tool ran. */
|
|
96
|
+
export type LandstripInternalCode =
|
|
97
|
+
| LandstripPolicyErrorCode
|
|
98
|
+
| 'SANDBOX_SETUP_FAILED'
|
|
99
|
+
| 'SUPERVISE_FAILED'
|
|
100
|
+
| 'PLATFORM_UNSUPPORTED'
|
|
101
|
+
| 'INTEGER_TOO_LARGE'
|
|
102
|
+
| 'INTERNAL_ERROR';
|
|
103
|
+
|
|
104
|
+
/** Everything that fails before the tool runs. */
|
|
105
|
+
export type LandstripInternalTrap =
|
|
106
|
+
| {
|
|
107
|
+
kind: 'internal';
|
|
108
|
+
code: 'SANDBOX_SETUP_FAILED';
|
|
109
|
+
mechanism: LandstripMechanism;
|
|
110
|
+
message: string;
|
|
111
|
+
}
|
|
112
|
+
| {
|
|
113
|
+
kind: 'internal';
|
|
114
|
+
code: Exclude<LandstripInternalCode, 'SANDBOX_SETUP_FAILED'>;
|
|
115
|
+
mechanism?: never;
|
|
116
|
+
message: string;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* One landstrip event. Failure and completed-denial events are written to
|
|
121
|
+
* stderr; pending Linux query events are written only to `--trap-fd`.
|
|
122
|
+
*/
|
|
123
|
+
export type LandstripTrap =
|
|
124
|
+
| LandstripFilesystemTrap
|
|
125
|
+
| LandstripNetworkTrap
|
|
126
|
+
| LandstripLaunchTrap
|
|
127
|
+
| LandstripUsageTrap
|
|
128
|
+
| LandstripInternalTrap;
|
|
129
|
+
|
|
130
|
+
/** Every code landstrip reports, across all trap kinds. */
|
|
131
|
+
export type LandstripCode = LandstripTrap['code'];
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The answer to a `state: "query"` trap, written back to the trap socket as one
|
|
135
|
+
* JSON line. `query_id` is an opaque decimal `u64` copied verbatim from the
|
|
136
|
+
* trap. An unanswered query holds the sandboxed syscall.
|
|
137
|
+
*/
|
|
138
|
+
export interface LandstripControlResponse {
|
|
139
|
+
query_id: string;
|
|
140
|
+
action: 'allow' | 'deny';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Path to the native landstrip binary for the running platform.
|
|
145
|
+
*
|
|
146
|
+
* @throws if the platform is unsupported, or the binary package is not installed.
|
|
147
|
+
*/
|
|
148
|
+
export function binaryPath(platform?: string, arch?: string): string;
|
|
149
|
+
|
|
150
|
+
/** Name of the binary package for the given platform. */
|
|
151
|
+
export function packageName(platform?: string, arch?: string): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@landstrip/landstrip",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Sandbox runner using Landlock, Seatbelt, and AppContainer",
|
|
5
5
|
"license": "Apache-2.0 AND LGPL-2.1-or-later",
|
|
6
6
|
"homepage": "https://github.com/landstrip/landstrip#readme",
|
|
@@ -12,7 +12,13 @@
|
|
|
12
12
|
"url": "https://github.com/landstrip/landstrip/issues"
|
|
13
13
|
},
|
|
14
14
|
"main": "lib/index.js",
|
|
15
|
-
"
|
|
15
|
+
"types": "lib/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
16
22
|
"bin": {
|
|
17
23
|
"landstrip": "bin/landstrip.js"
|
|
18
24
|
},
|
|
@@ -24,10 +30,10 @@
|
|
|
24
30
|
"LICENSE-LGPL-2.1"
|
|
25
31
|
],
|
|
26
32
|
"optionalDependencies": {
|
|
27
|
-
"@landstrip/landstrip-darwin-arm64": "0.
|
|
28
|
-
"@landstrip/landstrip-darwin-x64": "0.
|
|
29
|
-
"@landstrip/landstrip-linux-x64": "0.
|
|
30
|
-
"@landstrip/landstrip-win32-x64": "0.
|
|
33
|
+
"@landstrip/landstrip-darwin-arm64": "0.17.0",
|
|
34
|
+
"@landstrip/landstrip-darwin-x64": "0.17.0",
|
|
35
|
+
"@landstrip/landstrip-linux-x64": "0.17.0",
|
|
36
|
+
"@landstrip/landstrip-win32-x64": "0.17.0"
|
|
31
37
|
},
|
|
32
38
|
"publishConfig": {
|
|
33
39
|
"access": "public"
|