#! /usr/bin/env perl use warnings; use strict; use File::Temp qw/tempdir/; use Getopt::Long; umask 022; if (not defined $ENV{L4RE_SANDBOX_REEXEC}) { print "$0: Entering sandbox\n"; $ENV{L4RE_SANDBOX_REEXEC} = "y"; system("unshare", "-Urm", $0, @ARGV); exit $? >> 8; } sub usage { print <> 8; } sub sh_ignore_exit { my @cmd = @_; print "RUNNING: @cmd\n" if $ENV{V}; system(@cmd); return 0; } # Decouple all mounts from the outside world # See MS_PRIVATE in `man 2 mount` sh("mount --make-rprivate /"); if ($sys_dir =~ m,^/+$,) { sh("mount --bind $_ $_ && mount -o bind,remount,ro $_") foreach (@dirs_ro); # for doing everything read-only (not only @dirs_ro), we would need to # - make all mounts points read-only (what's a good way of finding them out?) # - mount at least /tmp read-write again $cmd = 'sh' unless defined $cmd; sh("$cmd"); sh("umount $_") foreach (@dirs_ro); } else { my $sandbox_dir = tempdir('l4-sandbox-XXXXXXXX', CLEANUP => 0, TMPDIR => 1); sh("mkdir -p $sandbox_dir/upper/dev $sandbox_dir/work $sandbox_dir/system"); sh("mount -t overlay l4re-build-overlay -o lowerdir=$sys_dir,upperdir=$sandbox_dir/upper,workdir=$sandbox_dir/work $sandbox_dir/system"); sh("mount -t tmpfs tmpfs $sandbox_dir/system/tmp"); sh("mount --rbind /dev $sandbox_dir/system/dev"); # On the overlay we get EPERM with reading dev files sh("mount --rbind -orw /proc $sandbox_dir/system/proc"); foreach my $d (@dirs_ro) { sh("mkdir -p $sandbox_dir/upper/$d"); sh("mount --rbind $d $sandbox_dir/system/$d"); sh("mount -o bind,remount,ro $sandbox_dir/system/$d"); } foreach my $d (@dirs_rw) { sh("mkdir -p $sandbox_dir/upper/$d"); sh("mount --rbind -orw $d $sandbox_dir/system/$d"); } my $chroot; # We need to add potential sbin paths since we only have the original user's # PATH environment variable for my $dir ((split /:/,$ENV{PATH}), "/sbin", "/usr/sbin") { my $bin = "$dir/chroot"; next unless -x $bin; $chroot = $bin; last; } die "Cannot find chroot executable" unless defined $chroot; $cmd = 'sh' unless defined $cmd; my $term = $ENV{TERM} || "xterm"; my $makeflags = $ENV{MAKEFLAGS} || ""; # jobserver fifo not reachable in sandbox $makeflags =~ s/--jobserver-auth(\s+|=)[^ ]+//; # Assumed path variable inside the sysdir # In an ideal world sh would load /etc/profile in the chroot my $path = "/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin"; sh("/usr/bin/env -i 'PATH=$path' L4RE_SANDBOX_REEXEC=y BID_SANDBOX_IN_PROGRESS=1 TERM=$term MAKEFLAGS='$makeflags' $chroot $sandbox_dir/system $cmd"); sh_ignore_exit("umount $sandbox_dir/system/$_") foreach (@dirs_rw, @dirs_ro); sh_ignore_exit("umount $sandbox_dir/system/dev"); sh_ignore_exit("umount $sandbox_dir/system/tmp"); sh_ignore_exit("umount -l $sandbox_dir/system/proc"); sh_ignore_exit("umount $sandbox_dir/system"); }