You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
110 lines
2.7 KiB
Ruby
110 lines
2.7 KiB
Ruby
#! /usr/bin/env ruby
|
|
# frozen_string_literal: true
|
|
|
|
require 'set'
|
|
|
|
require_relative 'linux'
|
|
include Linux
|
|
|
|
UID = 1000
|
|
GID = 1000
|
|
|
|
def eerror str; $stderr.puts " \e[31;1m*\e[0m #{str}"; end
|
|
|
|
if Process.uid != 0 && Process.uid != UID
|
|
eerror "Invalid uid #{Process.uid} - expected 0 or #{UID}"
|
|
exit 1
|
|
end
|
|
if Process.gid != 0 && Process.gid != GID
|
|
eerror "Invalid gid #{Process.gid} - expected 0 or #{GID}"
|
|
exit 1
|
|
end
|
|
|
|
path = __dir__
|
|
while path.size > 1 && !Dir.exist?(File.join(path, 'home'))
|
|
path = File.dirname path
|
|
end
|
|
if path.size <= 1
|
|
eerror "Can't find chroot root"
|
|
exit 1
|
|
end
|
|
Dir.chdir path
|
|
|
|
# not yet: CLONE_NEWNET
|
|
pra,pwa = IO.pipe
|
|
prb,pwb = IO.pipe
|
|
pid = clone CLONE_NEWNS | CLONE_NEWCGROUP | CLONE_NEWUTS | CLONE_NEWIPC |
|
|
CLONE_NEWUSER | CLONE_NEWPID do
|
|
pra.close
|
|
pwb.close
|
|
|
|
# map to build user/. We do this in the parent process, so it works if we
|
|
# execute this script as root.
|
|
# uid_gid_map UID, GID, pid
|
|
pwa.close
|
|
prb.read 1
|
|
prb.close
|
|
Process::Sys.setuid UID
|
|
Process::Sys.setgid GID
|
|
|
|
Linux.hostname = 'build'
|
|
Linux.domainname = 'no.such.domain'
|
|
mount nil, '/', nil, MS_PRIVATE | MS_REC
|
|
|
|
# fs jail
|
|
mount '.', 'mnt', nil, MS_BIND | MS_REC
|
|
mount 'home/build', 'mnt/home/build', nil, MS_BIND
|
|
mount '/etc/resolv.conf', 'mnt/etc/resolv.conf', nil, MS_BIND
|
|
mount 'none', 'mnt/proc', 'proc', MS_NOSUID | MS_NODEV
|
|
|
|
# linux is being retarded as usual https://lwn.net/Articles/647757/
|
|
# this is how would someone mount the full sysfs, but it contains shittons of
|
|
# sensitive info, and only wine needs it and only two files (which should be
|
|
# const, unless you do cpu hotplug), so just copy that two files into a tmpfs
|
|
|
|
# begin
|
|
# mount 'none', 'mnt/sys', 'sysfs', MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RDONLY
|
|
# rescue Errno::EPERM
|
|
# mount '/sys', 'mnt/sys', nil, MS_BIND | MS_REC
|
|
# end
|
|
|
|
mount 'none', 'mnt/sys', 'tmpfs'
|
|
FileUtils.mkdir_p 'mnt/sys/devices/system/cpu'
|
|
FileUtils.cp '/sys/devices/system/cpu/online', 'mnt/sys/devices/system/cpu/online'
|
|
FileUtils.cp '/sys/devices/system/cpu/present', 'mnt/sys/devices/system/cpu/present'
|
|
|
|
mount 'none', 'mnt/dev/pts', 'devpts'
|
|
mount 'none', 'mnt/tmp', 'tmpfs', MS_NOSUID | MS_NODEV
|
|
pivot_root 'mnt', 'mnt/mnt'
|
|
umount '/mnt', MNT_DETACH | UMOUNT_NOFOLLOW
|
|
|
|
make_ro Set['/dev/pts', '/home/build', '/proc', '/tmp']
|
|
|
|
Dir.chdir '/home/build'
|
|
env = {
|
|
'HOME' => '/home/build',
|
|
'USER' => 'build',
|
|
'TERM' => ENV['TERM'],
|
|
}
|
|
exec env, ['/bin/bash', '-'], *ARGV, unsetenv_others: true
|
|
fail 'Should not get here'
|
|
rescue Exception
|
|
puts $!.full_message
|
|
exit! 127
|
|
end
|
|
|
|
begin
|
|
pwa.close
|
|
prb.close
|
|
pra.read 1
|
|
pra.close
|
|
|
|
uid_gid_map UID, GID, pid
|
|
pwb.close
|
|
|
|
Process.wait pid
|
|
rescue Exception
|
|
Process.kill 'KILL', pid
|
|
raise
|
|
end
|