@@ -89,10 +89,15 @@ wrap_all () (
89
89
echo " $file "
90
90
done
91
91
92
+ # Install $venv/bin/shell
92
93
file=" $( realpath " $bin /shell" ) "
93
94
add_bin_shell " $file "
94
95
chmod +x " $file "
95
96
echo " $file "
97
+
98
+ # Install PYTHONPATH=$venv/sandbox with sitecustomize.py
99
+ mkdir -p " $bin /../sandbox"
100
+ extract_segment 3 " $@ " > " $bin /../sandbox/sitecustomize.py"
96
101
)
97
102
98
103
wrap_all " $@ "
@@ -184,8 +189,10 @@ executables="
184
189
/bin/env
185
190
/bin/ls
186
191
/bin/sh
192
+
187
193
/bin/uname
188
- "
194
+ /sbin/ldconfig
195
+ "
189
196
190
197
case $- in *x*) xtrace=-x ;; *) xtrace=+x ;; esac; set +x
191
198
@@ -215,7 +222,12 @@ py_libs="
215
222
/usr/include/python3*
216
223
/usr/lib/python3*
217
224
/usr/lib64/python3*
218
- /usr/local/lib/python3* "
225
+ /usr/local/lib/python3*
226
+
227
+ /usr/lib/python3* /* /seccomp.* .so
228
+ /usr/lib/* /libseccomp.so*
229
+ /usr/lib64/libseccomp.so*
230
+ "
219
231
git_libs="
220
232
/usr/lib* /git-core
221
233
"
@@ -232,7 +244,7 @@ ro_bind_extra="
232
244
/etc/pki
233
245
/etc/ssl
234
246
/usr/share/pki*
235
- "
247
+ "
236
248
237
249
collect="
238
250
$collect
@@ -291,11 +303,13 @@ set -- --bind "$proj_dir" "$proj_dir" "$@"
291
303
home=" ${HOME:- " /home/$USER " } "
292
304
pip_cache=" $home /${XDG_CACHE_HOME:- .cache} /pip"
293
305
mkdir -p " $venv /cache" " $pip_cache "
294
- mkdir -p " $venv /cache/pip" &&
295
- echo " This is an artefact of Bubblewrap bind mounting. Real pip cache is in \$ HOME/.cache/pip" \
296
- > " $venv /cache/pip/note.txt"
297
- set -- --bind " $venv /cache" " $home /.cache" \
298
- --bind " $pip_cache " " $home /.cache/pip" " $@ "
306
+ [ ! " ${SANDBOX_USE_PIP_CACHE-} " ] || {
307
+ mkdir -p " $venv /cache/pip" &&
308
+ echo " This dir is an artefact of Bubblewrap bind mounting. Real pip cache is in \$ HOME/.cache/pip" \
309
+ > " $venv /cache/pip/note.txt"
310
+ set -- --bind " $pip_cache " " $home /.cache/pip" " $@ "
311
+ }
312
+ set -- --bind " $venv /cache" " $home /.cache" " $@ "
299
313
300
314
# Pass our own redacted copy of env
301
315
for var in $( env | grep -E ' ^(USER|LOGNAME|UID|PATH|TERM|LANGUAGE|LANG|LC_.*?|HOSTNAME)=' ) ; do
@@ -328,4 +342,132 @@ exec bwrap \
328
342
--setenv HOME " $home " \
329
343
--setenv USER " user" \
330
344
--setenv VIRTUAL_ENV " $venv " \
345
+ --setenv PYTHONPATH " $venv /sandbox:${PYTHONPATH-} " \
331
346
" $@ "
347
+
348
+
349
+ # CUT HERE ------------------- Appendix 3: sandbox-venv sitecustomize.py script
350
+ #!/usr/bin/python3
351
+ " " "
352
+ Importing this module inits seccomp/pyseccomp, restricting allowed syscalls to those
353
+ listed in SANDBOX_SECCOMP_ALLOW environment variable (or, by default, the list below).
354
+ The module is imported automatically upon startup when on PYTHONPATH.
355
+
356
+ https://docs.python.org/3/library/site.html#module-sitecustomize
357
+ " " "
358
+ import os
359
+ import re
360
+ import sys
361
+
362
+ # XXX: You can debug this list (e.g. update missing items) by
363
+ # looking for EPERM in ` strace -f` output.
364
+ ALLOW_SYSCALLS = [
365
+ # Process & signals
366
+ " clone" , " clone3" , " fork" , " vfork" , " execve" , " exit" , " exit_group" ,
367
+ " kill" , " tgkill" , " tkill" , " wait4" , " getpid" , " getppid" , " gettid" ,
368
+ " prctl" , " arch_prctl" , " getpgrp" ,
369
+ " pidfd_open" , " pidfd_send_signal" ,
370
+ " sigaction" , " sigreturn" ,
371
+ " rt_sigaction" , " rt_sigreturn" ,
372
+ " rt_sigprocmask" , " rt_sigpending" , " rt_sigsuspend" ,
373
+ " sigaltstack" , " rseq" , " process_madvise" ,
374
+
375
+ # File I/O
376
+ " open" , " openat" , " close" , " close_range" , " read" , " write" ,
377
+ " name_to_handle_at" ,
378
+ " pread64" , " pwrite64" , " preadv" , " pwritev" , " preadv2" , " pwritev2" ,
379
+ " lseek" ,
380
+ " stat" , " statx" , " fstat" , " lstat" , " fstatat64" , " newfstatat" ,
381
+ " stat64" , " lstat64" , " fstat64" , " fadvise64" ,
382
+ " faccessat2" , " getdents" , " getdents64" , " access" ,
383
+ " unlink" , " unlinkat" , " mkdir" , " mkdirat" , " rmdir" ,
384
+ " rename" , " renameat" , " renameat2" ,
385
+ " readlink" , " readlinkat" , " symlink" , " symlinkat" , " link" , " linkat" ,
386
+ " truncate" , " ftruncate" , " utime" , " utimes" , " futimesat" , " utimensat" ,
387
+ " chown" , " fchown" , " lchown" , " fchmod" , " fchmodat" , " chmod" , " mknod" , " mknodat" ,
388
+ " getxattr" , " setxattr" , " listxattr" , " removexattr" ,
389
+
390
+ # fd ops
391
+ " dup" , " dup2" , " dup3" , " pipe" , " pipe2" ,
392
+ " fcntl" , " flock" , " fsync" , " fdatasync" ,
393
+ " readahead" ,
394
+ " splice" , " tee" , " vmsplice" ,
395
+
396
+ # mmap / mem / threads
397
+ " brk" , " mmap" , " mmap2" , " munmap" , " mremap" , " mprotect" , " madvise" , " mincore" ,
398
+ " futex" , " futex_time64" , " futex_waitv" , " set_tid_address" , " set_robust_list" , " get_robust_list" ,
399
+ " sched_yield" , " sched_getaffinity" ,
400
+ " mlock" , " munlock" , " mlockall" , " munlockall" ,
401
+ " get_mempolicy" , " set_mempolicy" , " mbind" , " migrate_pages" , " move_pages" ,
402
+ " membarrier" ,
403
+
404
+ # Time / clocks
405
+ " nanosleep" , " clock_gettime" , " clock_getres" , " gettimeofday" , " time" ,
406
+ " clock_nanosleep" , " clock_gettime64" ,
407
+ " timer_create" , " timer_settime" , " timer_gettime" , " timer_delete" ,
408
+ " setitimer" , " getitimer" , " times" ,
409
+ " timerfd_create" , " timerfd_settime" , " timerfd_gettime" ,
410
+
411
+ # Networking
412
+ " socket" , " socketpair" , " socketcall" , " bind" , " listen" ,
413
+ " accept" , " accept4" , " connect" ,
414
+ " getsockname" , " getpeername" ,
415
+ " sendto" , " recvfrom" , " sendmsg" , " recvmsg" , " shutdown" ,
416
+ " setsockopt" , " getsockopt" ,
417
+ " recvmmsg" , " sendmmsg" ,
418
+ " sendfile" , " sendfile64" ,
419
+
420
+ # epoll/poll/select
421
+ " epoll_create" , " epoll_create1" , " epoll_ctl" , " epoll_wait" ,
422
+ " epoll_pwait" , " epoll_pwait2" ,
423
+ " poll" , " ppoll" , " ppoll_time64" , " select" , " pselect6" ,
424
+ " eventfd" , " eventfd2" ,
425
+
426
+ # Descriptors / metadata
427
+ " ioctl" , " readv" , " writev" ,
428
+ " getcwd" , " chdir" , " fchdir" ,
429
+ " statfs" , " fstatfs" , " statfs64" , " fstatfs64" ,
430
+
431
+ # UIDs/GIDs (read + drop privileges)
432
+ " getuid" , " geteuid" , " getgid" , " getegid" , " getgroups" ,
433
+ " getresuid" , " getresgid" , " setresuid" , " setresgid" , " setreuid" , " setregid" ,
434
+ " setuid" , " setgid" ,
435
+
436
+ # Limits / info
437
+ " getrlimit" , " setrlimit" , " prlimit64" , " uname" , " sysinfo" , " getrusage" , " umask" ,
438
+
439
+ # Random
440
+ " getrandom" ,
441
+
442
+ # Misc
443
+ " seccomp" , " capget" ,
444
+ " shmget" , " shmat" , " shmdt" , " shmctl" ,
445
+ ]
446
+
447
+
448
+ try:
449
+ allow_syscalls = re.findall(r'\w+', os.environ['SANDBOX_SECCOMP_ALLOW'])
450
+ except KeyError:
451
+ allow_syscalls = ALLOW_SYSCALLS
452
+
453
+ if allow_syscalls:
454
+ try:
455
+ import seccomp
456
+ except ImportError:
457
+ try:
458
+ import pyseccomp as seccomp
459
+ except ImportError:
460
+ seccomp = None
461
+ if sys.platform.startswith('linux'):
462
+ print(" sandbox-venv/seccomp: Python package ' seccomp' (or ' pyseccomp' ) "
463
+ " not available. If you want seccomp support, apt install python3-seccomp "
464
+ " (requires venv created with --system-site-packages) "
465
+ " or pip install pyseccomp." , file=sys.stderr)
466
+ if seccomp:
467
+ print(f'sandbox-venv/seccomp: allowing {len(allow_syscalls)} syscalls', file=sys.stderr)
468
+ default_action = seccomp.ERRNO(seccomp.errno.EPERM) # EPERM, Operation not permitted
469
+ filter = seccomp.SyscallFilter(default_action)
470
+ for syscall in allow_syscalls:
471
+ # print(syscall, file=sys.stderr)
472
+ filter.add_rule(seccomp.ALLOW, syscall)
473
+ filter.load()
0 commit comments