-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathtests.nix
More file actions
437 lines (357 loc) · 17.1 KB
/
tests.nix
File metadata and controls
437 lines (357 loc) · 17.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
{ pkgs, wrap }:
pkgs.nixosTest {
name = "mytest";
nodes.machine = { config, pkgs, ... }: {
system.stateVersion = "24.05";
networking.dhcpcd.enable = false; # boots faster
environment.systemPackages = [ wrap ];
users.users.alice = {
isNormalUser = true;
createHome = true;
};
};
testScript = /* python */ ''
machine.wait_for_unit("default.target")
# Run a script as alice
# This function wraps a bash script with su in order to run as alice.
# Single quotes are escaped in the script, and the script is handed to su
# in $'...' ansi quoting in order to allow escaped single quotes inside the
# single quoted string. The script is run in 'bash strict mode'.
def as_alice(script):
script = script.replace("'", r"\'")
script = "set -euo pipefail\n" + script
return ( "su "
"--login alice "
"--shell ${pkgs.bash}/bin/bash "
f"--command $'{script}'"
)
# as_alice = lambda script: (
# f"su --login alice --shell ${pkgs.bash}/bin/bash --command $'{script.replace("'", r"\'")}'"
# )
with subtest("Environment variable $HOME is always exposed"):
machine.succeed(as_alice("""
# ensure $HOME is set
echo $HOME | grep '^/home/alice$' ||
(echo 'Unexpected: $HOME is not set outside sandbox'; false)
# ensure $HOME remains set in sandbox
wrap bash -c 'echo $HOME' | grep '^/home/alice$' ||
(echo 'Unexpected: $HOME is unset in sandbox'; false)
"""))
with subtest("Environment variable $EDITOR is always exposed"):
machine.succeed(as_alice("""
# ensure $EDITOR is set
export EDITOR=vim
echo $EDITOR | grep '^vim$' ||
(echo 'Unexpected: $VIM is not set outside sandbox'; false)
# ensure $EDITOR remains set in sandbox
wrap bash -c 'echo $EDITOR' | grep '^vim$' ||
(echo 'Unexpected: $EDITOR is unset in sandbox: '; false)
"""))
with subtest("Username is hidden in sandbox, whoami does not work"):
machine.succeed(as_alice("""
# ensure `whoami` works outside sandbox
whoami | grep 'alice' ||
(echo 'Unexpected: whoami does not work outside sandbox'; false)
# ensure `whoami` does not work in sandbox
! wrap whoami ||
(echo 'Unexpected: whoami works in sandbox'; false)
! wrap whoami 2>&1 | grep "cannot find name for user ID" ||
(echo 'Unexpected: whoami seems to work in sandbox'; false)
"""))
with subtest("-u exposes username in sandbox, whoami does work"):
machine.succeed(as_alice("""
# ensure `whoami` works outside sandbox
whoami | grep 'alice' ||
(echo 'Unexpected: whoami does not work outside sandbox'; false)
# ensure `whoami` does work in sandbox
wrap -u whoami ||
(echo 'Unexpected: whoami does not work in sandbox'; false)
# ensure `whoami` returns username in sandbox
wrap -u whoami | grep "^alice$" ||
(echo 'Unexpected: whoami does not return username in sandbox'; false)
"""))
with subtest("Environment variable $WAYLAND_DISPLAY is hidden by default"):
machine.succeed(as_alice("""
# ensure $WAYLAND_DISPLAY is set outside sandbox
export WAYLAND_DISPLAY=wl-0
echo $WAYLAND_DISPLAY | grep '^wl-0$' ||
(echo 'Unexpected: WAYLAND_DISPLAY is not set outside sandbox'; false)
# ensure $WAYLAND_DISPLAY is unset in sandbox
! (wrap bash -c 'echo $WAYLAND_DISPLAY' | grep '^wl-0$') ||
(echo 'Unexpected: WAYLAND_DISPLAY is set in sandbox'; false)
"""))
with subtest("-d exposes $WAYLAND_DISPLAY in sandbox"):
machine.succeed(as_alice("""
# ensure $WAYLAND_DISPLAY is set outside sandbox
export WAYLAND_DISPLAY=wl-0
echo $WAYLAND_DISPLAY | grep '^wl-0$' ||
(echo 'Unexpected: WAYLAND_DISPLAY is not set outside sandbox'; false)
# ensure $WAYLAND_DISPLAY is set in sandbox
wrap -d bash -c 'echo $WAYLAND_DISPLAY' | grep '^wl-0$' ||
(echo 'Unexpected: WAYLAND_DISPLAY is not set in sandbox'; false)
"""))
with subtest("-d exposes wayland socket in sandbox"):
machine.succeed(as_alice("""
# ensure $WAYLAND_DISPLAY is set outside sandbox
export WAYLAND_DISPLAY=wl-12345
echo $WAYLAND_DISPLAY | grep '^wl-12345$' ||
(echo 'Unexpected: WAYLAND_DISPLAY is not set outside sandbox'; false)
# create wayland socket mock
export XDG_RUNTIME_DIR=/tmp/wayland
mkdir -p $XDG_RUNTIME_DIR
touch $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY
echo $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY | grep '^/tmp/wayland/wl-12345$' ||
(echo 'Unexpected: WAYLAND socket mock does not exist outside sandbox'; false)
# ensure $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY is set in sandbox
wrap -d bash -c 'echo $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY' | grep '^/tmp/wayland/wl-12345$' ||
(echo 'Unexpected: $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY is not set in sandbox'; false)
# ensure wayland socket mock exists in sandbox
wrap -d bash -c 'ls $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY' | grep '^/tmp/wayland/wl-12345$' ||
(echo 'Unexpected: wayland socket mock does not exist in sandbox'; false)
"""))
with subtest("Environment variable $DISPLAY is hidden by default"):
machine.succeed(as_alice("""
# ensure $DISPLAY is set outside sandbox
export DISPLAY=:0
echo $DISPLAY | grep '^:0$' ||
(echo 'Unexpected: $DISPLAY is not set outside sandbox'; false)
# ensure $DISPLAY is unset in sandbox
! (wrap bash -c 'echo $DISPLAY' | grep '^:0$') ||
(echo 'Unexpected: $DISPLAY is set in sandbox'; false)
"""))
with subtest("-d exposes $DISPLAY in sandbox"):
machine.succeed(as_alice("""
# ensure $DISPLAY is set outside sandbox
export DISPLAY=:0
echo $DISPLAY | grep '^:0$' ||
(echo 'Unexpected: $DISPLAY is not set outside sandbox'; false)
# ensure $DISPLAY is set in sandbox
wrap -d bash -c 'echo $DISPLAY' | grep '^:0$' ||
(echo 'Unexpected: $DISPLAY is not set in sandbox'; false)
"""))
with subtest("-d exposes X11 socket in sandbox"):
machine.succeed(as_alice("""
mkdir -p /tmp/.X11-unix
touch /tmp/.X11-unix/X12345
# ensure $DISPLAY is set outside sandbox
export DISPLAY=:12345
echo $DISPLAY | grep '^:12345$' ||
(echo 'Unexpected: $DISPLAY is not set outside sandbox'; false)
# ensure $DISPLAY socket is visible in sandbox
wrap -d bash -c 'ls /tmp/.X11-unix/X12345' | grep 'X12345' ||
(echo 'Unexpected: $DISPLAY socket is not visible in sandbox'; false)
"""))
with subtest("-d exposes .Xauthority in sandbox"):
machine.succeed(as_alice("""
# create mock home
export HOME=/tmp/home
mkdir -p $HOME
# create mock Xauthority file
touch $HOME/.Xauthority
# ensure Xauthority file exists in sandbox
wrap -d bash -c 'ls $HOME/.Xauthority' | grep '.Xauthority$' ||
(echo 'Unexpected: .Xauthority is not visible in sandbox'; false)
"""))
with subtest("-d exposes custom Xauthority file in sandbox"):
machine.succeed(as_alice("""
# create mock home
export HOME=/tmp/home
mkdir -p $HOME
# create mock custom Xauthority file
touch $HOME/myxauthfile
echo "mysecret" > $HOME/myxauthfile
export XAUTHORITY=myxauthfile
# ensure custom Xauthority file exists in sandbox
wrap -d bash -c 'cat $HOME/.Xauthority' | grep '^mysecret$' ||
(echo 'Unexpected: custom .Xauthority is not visible in sandbox'; false)
"""))
with subtest("-r exposes path readonly"):
machine.succeed(as_alice("""
# create some test file
mkdir -p /tmp/some-dir
echo "file-content" > /tmp/some-dir/test-file
# try to read the test file
wrap -r /tmp/some-dir bash -c 'cat /tmp/some-dir/test-file' | grep '^file-content$' ||
(echo 'Unexpected: Did not get expected output when trying to read from readonly path'; false)
# try to write to the test file
wrap -r /tmp/some-dir bash -c 'echo "Hello World" > /tmp/some-dir/test-file' 2> error || true
cat error | grep '/tmp/some-dir/test-file: Read-only file system$' ||
(echo 'Unexpected: Did not get expected error when trying to write to readonly path'; false)
"""))
with subtest("-w exposes path readwrite"):
machine.succeed(as_alice("""
# create some test file
mkdir -p /tmp/some-dir
echo "file-content" > /tmp/some-dir/test-file
# try to read the test file
wrap -w /tmp/some-dir bash -c 'cat /tmp/some-dir/test-file' | grep '^file-content$' ||
(echo 'Unexpected: Did not get expected output when trying to read from readwrite path'; false)
# try to write to the test file
wrap -w /tmp/some-dir bash -c 'echo "Hello World" > /tmp/some-dir/test-file' ||
(echo 'Unexpected: Cannot write to readwrite path'; false)
cat /tmp/some-dir/test-file | grep '^Hello World$' ||
(echo 'Unexpected: Did not get expected output when reading freshly written file'; false)
"""))
with subtest("cwd is exposed by default"):
machine.succeed(as_alice("""
mkdir -p /tmp/some-dir
cd /tmp/some-dir
echo "file-content" > test-file
# Expect cat to succeed inside sandbox
wrap bash -c 'cat test-file' | grep '^file-content$' ||
(echo 'Unexpected: cwd not exposed by default'; false)
"""))
with subtest("-p does not expose cwd"):
machine.succeed(as_alice("""
mkdir -p /tmp/some-dir
cd /tmp/some-dir
echo "file-content" > test-file
# Expect cat to fail inside sandbox when -p is used
wrap -p bash -c 'cat test-file; echo $?' | grep '^1$' ||
(echo 'Unexpected: cwd exposed when using -p'; false)
"""))
with subtest("-p cds to $HOME"):
machine.succeed(as_alice("""
mkdir -p /tmp/new-home
export HOME=/tmp/new-home
mkdir -p /tmp/new-home/some-dir
cd /tmp/new-home/some-dir
# Expect pwd to return $HOME in sandbox
wrap -p bash -c 'pwd' | grep '^/tmp/new-home$' ||
(echo 'Unexpected: -p did not change cwd to $HOME as expected'; false)
# Expect this home dir to be empty
wrap -p bash -c 'ls -l $HOME' | grep '^total 0$' ||
(echo 'Unexpected: Sandbox $HOME is not empty'; false)
"""))
with subtest("$HOME as cwd is not shared implicitly"):
machine.succeed(as_alice("""
# setup prerequisites
mkdir -p /tmp/new-home
export HOME=/tmp/new-home
touch /tmp/new-home/something-in-home
cd $HOME
# expect cwd to still be $HOME
wrap bash -c 'pwd' | grep '^/tmp/new-home$' ||
(echo 'Unexpected: Cwd in sandbox is not $HOME'; false)
# expect $HOME to be empty
wrap bash -c 'ls -l $HOME' | grep '^total 0$' ||
(echo 'Unexpected: Sandbox $HOME is not empty'; false)
"""))
with subtest("/etc as cwd is not shared implicitly"):
machine.succeed("""
touch /etc/something-in-etc
""")
machine.succeed(as_alice("""
# setup prerequisites
export HOME=/tmp/new-home
mkdir -p /tmp/new-home
cd /etc
# expect cwd to still be $HOME
wrap bash -c 'pwd' | grep '^/tmp/new-home$' ||
(echo 'Unexpected: Cwd in sandbox is not $HOME'; false)
# expect /etc to not contain file from outside sandbox
! (wrap bash -c 'ls /etc/something-in-etc' | grep '^something-in-etc$') ||
(echo 'Unexpected: Sandbox /etc contains file from outside sandbox'; false)
"""))
with subtest("-f forces sharing HOME as cwd"):
machine.succeed(as_alice("""
# setup prerequisites
mkdir -p /tmp/new-home
export HOME=/tmp/new-home
touch /tmp/new-home/something-in-home
cd $HOME
# expect cwd to be $HOME
wrap -f bash -c 'pwd' | grep '^/tmp/new-home$' ||
(echo 'Unexpected: Cwd in sandbox is not $HOME'; false)
# expect file in $HOME
wrap -f bash -c 'ls $HOME' | grep '^something-in-home$' ||
(echo 'Unexpected: Sandbox $HOME is empty'; false)
"""))
with subtest("Network files are available only with -n"):
machine.succeed(as_alice("""
# Without -n, resolv.conf should not be visible
! wrap bash -c 'ls /etc/resolv.conf' ||
(echo 'Unexpected: /etc/resolv.conf visible without -n'; false)
# With -n, resolv.conf and /etc/ssl should be visible
wrap -n bash -c 'ls /etc/resolv.conf' | grep '^/etc/resolv.conf$' ||
(echo 'Unexpected: /etc/resolv.conf not visible with -n'; false)
wrap -n bash -c 'test -d /etc/ssl && echo ok' | grep '^ok$' ||
(echo 'Unexpected: /etc/ssl not visible with -n'; false)
"""))
with subtest("Audio sockets visible only with -a"):
machine.succeed(as_alice("""
export XDG_RUNTIME_DIR=/tmp/xdg
mkdir -p "$XDG_RUNTIME_DIR/pulse"
touch "$XDG_RUNTIME_DIR/pulse/native"
touch "$XDG_RUNTIME_DIR/pipewire-0"
touch "$XDG_RUNTIME_DIR/pipewire-0.lock"
# Without -a, these paths should not be visible in sandbox
! (wrap bash -c 'ls $XDG_RUNTIME_DIR/pulse/native') ||
(echo 'Unexpected: pulse/native visible without -a'; false)
! (wrap bash -c 'ls $XDG_RUNTIME_DIR/pipewire-0') ||
(echo 'Unexpected: pipewire-0 visible without -a'; false)
! (wrap bash -c 'ls $XDG_RUNTIME_DIR/pipewire-0.lock') ||
(echo 'Unexpected: pipewire-0.lock visible without -a'; false)
# With -a, they should be visible
wrap -a bash -c 'ls $XDG_RUNTIME_DIR/pulse/native' | grep 'pulse/native' ||
(echo 'Unexpected: pulse/native not visible with -a'; false)
wrap -a bash -c 'ls $XDG_RUNTIME_DIR/pipewire-0' | grep 'pipewire-0$' ||
(echo 'Unexpected: pipewire-0 not visible with -a'; false)
wrap -a bash -c 'ls $XDG_RUNTIME_DIR/pipewire-0.lock' | grep 'pipewire-0.lock$' ||
(echo 'Unexpected: pipewire-0.lock not visible with -a'; false)
"""))
with subtest("DBus session socket visible only with -b"):
machine.succeed(as_alice("""
# Create a fake DBus session socket and export address
touch /tmp/dbus-sock
export DBUS_SESSION_BUS_ADDRESS="unix:path=/tmp/dbus-sock"
# Without -b, it should not be visible
! (wrap bash -c 'ls /tmp/dbus-sock') ||
(echo 'Unexpected: DBus socket visible without -b'; false)
# With -b, it should be visible
wrap -b bash -c 'ls /tmp/dbus-sock' | grep '/tmp/dbus-sock' ||
(echo 'Unexpected: DBus socket not visible with -b'; false)
"""))
with subtest("Env var passthrough via -e and default stripping"):
machine.succeed(as_alice("""
export SECRET_VAR=topsecret
# By default SECRET_VAR should be stripped
wrap bash -c 'echo ''${SECRET_VAR:-unset}' | grep '^unset$' ||
(echo 'Unexpected: SECRET_VAR visible without -e'; false)
# With -e SECRET_VAR it should be visible
wrap -e SECRET_VAR bash -c 'echo ''${SECRET_VAR:-unset}' | grep '^topsecret$' ||
(echo 'Unexpected: SECRET_VAR not visible with -e'; false)
"""))
with subtest("NIX_PROFILES are ro-bound and not writable"):
machine.succeed("""
mkdir -p /profile
""")
machine.succeed(as_alice("""
export NIX_PROFILES="''${NIX_PROFILES} /profile"
# Directory should be visible in sandbox
wrap bash -c 'ls -l /profile' | grep '^total 0$' ||
(echo 'Unexpected: NIX_PROFILES dir not visible in sandbox'; false)
# Writing inside should fail due to ro-bind
wrap bash -c 'echo hi > /profile/file' 2> error || true
cat error | grep '/profile/file: Read-only file system$' ||
(echo 'Unexpected: /profile is writable in sandbox'; false)
"""))
with subtest("HOME is created and writable inside sandbox"):
machine.succeed(as_alice("""
export HOME=/tmp/new-home-absent
rm -rf "$HOME"
# With -p (no cwd sharing), bwrap --dir should create HOME inside sandbox and be writable
wrap -p bash -c 'test -d "$HOME" -a -w "$HOME" && echo ok' | grep '^ok$' ||
(echo 'Unexpected: $HOME not created or not writable inside sandbox'; false)
"""))
with subtest("r/w shared directory is created if it does not exist"):
machine.succeed(as_alice("""
export SOME_DIR=/tmp/some-dir
rm -rf "$SOME_DIR"
wrap -p -e SOME_DIR -r "$SOME_DIR" bash -c 'test -d "$SOME_DIR" && echo ok' | grep '^ok$' ||
(echo 'Unexpected: -r $SOME_DIR does not create directory'; false)
wrap -p -e SOME_DIR -w "$SOME_DIR" bash -c 'test -d "$SOME_DIR" && echo ok' | grep '^ok$' ||
(echo 'Unexpected: -w $SOME_DIR does not create directory'; false)
"""))
'';
}