Skip to content

Commit 537bee1

Browse files
committed
WIP: Implement rootless execution
1 parent cfc30fa commit 537bee1

File tree

1 file changed

+90
-18
lines changed

1 file changed

+90
-18
lines changed

repro.in

Lines changed: 90 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,44 @@ orig_argv=("$0" "$@")
2121
src_owner=${SUDO_USER:-$USER}
2222
function check_root() {
2323
(( EUID == 0 )) && return
24-
if type -P sudo >/dev/null; then
24+
if ((rootless_userns)); then
25+
exec become-root unshare --mount "${orig_argv[@]}"
26+
elif type -P sudo >/dev/null; then
2527
exec sudo -- "${orig_argv[@]}"
2628
else
2729
exec su root -c "$(printf ' %q' "${orig_argv[@]}")"
2830
fi
2931
}
3032

33+
function require_userns_tools() {
34+
if command -v become-root >/dev/null \
35+
&& command -v nsjail >/dev/null \
36+
&& command -v fuse-overlayfs >/dev/null
37+
then
38+
return 0
39+
fi
40+
warning "nsjail, fuse-overlayfs and become-root are necessary for rootless operation"
41+
warning "https://github.com/giuseppe/become-root"
42+
warning "https://github.com/containers/fuse-overlayfs"
43+
warning "https://github.com/google/nsjail"
44+
return 1
45+
}
46+
47+
function mountoverlay() {
48+
if ((rootless_userns)); then
49+
fuse-overlayfs "$@"
50+
else
51+
mount -t overlayfs overlayfs "$@"
52+
fi
53+
}
54+
function umountoverlay() {
55+
if ((rootless_userns)); then
56+
fusermount -u "$@"
57+
else
58+
umount "$@"
59+
fi
60+
}
61+
3162
# Use a private gpg keyring
3263
function gpg() {
3364
command gpg --homedir="$BUILDDIRECTORY/gnupg" "$@"
@@ -103,6 +134,42 @@ function exec_nspawn(){
103134
-D "$BUILDDIRECTORY/$container" "${@:2}"
104135
}
105136

137+
138+
# Desc: Executes an command inside a given nsjail container
139+
# 1: Container name
140+
# 2: Optional: one --bind=... bindmount option
141+
# 2/3: Command to execute
142+
function exec_nsjail(){
143+
local container=$1
144+
local args=( -Mo --quiet
145+
--disable_clone_newuser
146+
--disable_clone_newnet
147+
--disable_rlimits
148+
--keep_caps
149+
--tmpfsmount /tmp
150+
--bindmount_ro /dev
151+
-E "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin"
152+
--chroot "$BUILDDIRECTORY/$container" --rw
153+
)
154+
## use a no-op forking unshare as pid1
155+
if [[ $2 == --bind=* ]] ; then
156+
nsjail "${args[@]}" --bindmount "${2#--bind=}" -- /usr/bin/unshare -f "${@:3}"
157+
else
158+
nsjail "${args[@]}" -- /usr/bin/unshare -f "${@:2}"
159+
fi
160+
}
161+
162+
function exec_container(){
163+
if ((rootless_userns)); then
164+
exec_nsjail "$@"
165+
else
166+
exec_nspawn "$@"
167+
fi
168+
}
169+
170+
171+
172+
106173
# Desc: Removes the root container
107174
function cleanup_root_volume(){
108175
warning "Removing root container..."
@@ -114,7 +181,7 @@ function cleanup_root_volume(){
114181
function remove_snapshot (){
115182
local build=$1
116183
msg2 "Delete snapshot for $build..."
117-
umount "$BUILDDIRECTORY/$build" || true
184+
umountoverlay "$BUILDDIRECTORY/$build" || true
118185
rm -rf "${BUILDDIRECTORY:?}/${build}"
119186
rm -rf "${BUILDDIRECTORY:?}/${build}_upperdir"
120187
rm -rf "${BUILDDIRECTORY:?}/${build}_workdir"
@@ -131,7 +198,7 @@ function create_snapshot (){
131198
msg2 "Create snapshot for $build..."
132199
mkdir -p "$BUILDDIRECTORY/"{"${build}","${build}_upperdir","${build}_workdir"}
133200
# shellcheck disable=SC2140
134-
mount -t overlay overlay \
201+
mountoverlay \
135202
-o lowerdir="$BUILDDIRECTORY/root",upperdir="$BUILDDIRECTORY/${build}_upperdir",workdir="$BUILDDIRECTORY/${build}_workdir" \
136203
"$BUILDDIRECTORY/${build}"
137204
touch "$BUILDDIRECTORY/$build"
@@ -143,7 +210,7 @@ function create_snapshot (){
143210
function build_package(){
144211
local build=$1
145212
local builddir=${2:-"/startdir"}
146-
exec_nspawn "$build" \
213+
exec_container "$build" \
147214
--bind="$PWD:/srcdest" \
148215
bash <<-__END__
149216
set -e
@@ -180,23 +247,23 @@ function init_chroot(){
180247
printf '%s.UTF-8 UTF-8\n' en_US de_DE > "$BUILDDIRECTORY"/root/etc/locale.gen
181248
printf 'LANG=en_US.UTF-8\n' > "$BUILDDIRECTORY"/root/etc/locale.conf
182249

183-
systemd-machine-id-setup --root="$BUILDDIRECTORY"/root
250+
exec_container root systemd-machine-id-setup
184251
msg2 "Setting up keyring, this might take a while..."
185-
exec_nspawn root pacman-key --init &> /dev/null
186-
exec_nspawn root pacman-key --populate archlinux &> /dev/null
252+
exec_container root pacman-key --init &> /dev/null
253+
exec_container root pacman-key --populate archlinux &> /dev/null
187254

188255
msg2 "Updating and installing base & base-devel"
189-
exec_nspawn root pacman -Syu base-devel --noconfirm
190-
exec_nspawn root pacman -R arch-install-scripts --noconfirm
191-
exec_nspawn root locale-gen
256+
exec_container root pacman -Syu base-devel --noconfirm
257+
exec_container root pacman -R arch-install-scripts --noconfirm
258+
exec_container root locale-gen
192259

193260
printf 'builduser ALL = NOPASSWD: /usr/bin/pacman\n' > "$BUILDDIRECTORY"/root/etc/sudoers.d/builduser-pacman
194-
exec_nspawn root useradd -m -G wheel -s /bin/bash -d /build builduser
261+
exec_container root useradd -m -G wheel -s /bin/bash -d /build builduser
195262
echo "keyserver-options auto-key-retrieve" | install -Dm644 /dev/stdin "$BUILDDIRECTORY/root"/build/.gnupg/gpg.conf
196-
exec_nspawn root chown -R builduser /build/.gnupg
263+
exec_container root chown -R builduser /build/.gnupg
197264
else
198265
printf 'Server = %s\n' "$HOSTMIRROR" > "$BUILDDIRECTORY"/root/etc/pacman.d/mirrorlist
199-
exec_nspawn root pacman -Syu --noconfirm
266+
exec_container root pacman -Syu --noconfirm
200267
fi
201268

202269
trap - ERR INT
@@ -248,7 +315,7 @@ function cmd_check(){
248315
} >> "$BUILDDIRECTORY/$build/etc/makepkg.conf"
249316

250317
# Father I have sinned
251-
exec_nspawn "build" \
318+
exec_container "build" \
252319
bash <<-__END__
253320
shopt -s globstar
254321
install -d -o builduser -g builduser /startdir
@@ -274,16 +341,16 @@ __END__
274341
msg2 "Finished preparing packages"
275342
msg "Installing packages"
276343
# shellcheck disable=SC2086
277-
exec_nspawn build --bind="$(readlink -e ${cachedir}):/cache" bash -c \
344+
exec_container build --bind="$(readlink -e ${cachedir}):/cache" bash -c \
278345
'yes y | pacman -Udd --overwrite "*" -- "$@"' -bash "${packages[@]}"
279346

280347
read -r -a buildinfo_packages <<< "$(buildinfo -f installed "${pkg}")"
281348
uninstall=$(comm -13 \
282349
<(printf '%s\n' "${buildinfo_packages[@]}" | rev | cut -d- -f4- | rev | sort) \
283-
<(exec_nspawn build --bind="$(readlink -e ${cachedir}):/cache" pacman -Qq | sort))
350+
<(exec_container build --bind="$(readlink -e ${cachedir}):/cache" pacman -Qq | sort))
284351

285352
if [ -n "$uninstall" ]; then
286-
exec_nspawn build pacman -Rdd --noconfirm -- $uninstall
353+
exec_container build pacman -Rdd --noconfirm -- $uninstall
287354
fi
288355

289356
build_package "build" "$builddir"
@@ -347,10 +414,15 @@ elif [[ -r "$HOME/.repro.conf" ]]; then
347414
fi
348415

349416

350-
while getopts :hdoC:P:M: arg; do
417+
while getopts :hdorC:P:M: arg; do
351418
case $arg in
352419
h) print_help; exit 0;;
353420
d) run_diffoscope=1;;
421+
r) rootless_userns=1;
422+
require_userns_tools || exit 1
423+
# TODO: better detection for valid writable build directory
424+
[[ $BUILDDIRECTORY == /var/lib/repro ]] && BUILDDIRECTORY="${XDG_CACHE_HOME:-$HOME/.cache}/archlinux-repro"
425+
;;
354426
*) ;;
355427
esac
356428
done

0 commit comments

Comments
 (0)