forked from josch/mmdebstrap
allow unshare as root user
- this is useful when you are already root and want the benefits of unsharing the mount namespace to prevent messing up your system - if the unshare mode is used as root, the user namespace is not unshared anymore and newuidmap, setuid and friends are not called anymore - if the unshare mode is used as non-root test if the user namespace can be unshared, otherwise test if the mount namespace can be unshared
This commit is contained in:
parent
0f6741d01a
commit
4693034138
2 changed files with 48 additions and 26 deletions
23
coverage.sh
23
coverage.sh
|
@ -498,29 +498,24 @@ else
|
||||||
runtests=$((runtests+1))
|
runtests=$((runtests+1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_header "mode=unshare,variant=apt: fail with unshare as root user"
|
print_header "mode=unshare,variant=apt: unshare as root user"
|
||||||
cat << END > shared/test.sh
|
cat << END > shared/test.sh
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -eu
|
set -eu
|
||||||
export LC_ALL=C.UTF-8
|
export LC_ALL=C.UTF-8
|
||||||
if [ ! -e /mmdebstrap-testenv ]; then
|
[ "\$(whoami)" = "root" ]
|
||||||
echo "this test modifies the system and should only be run inside a container" >&2
|
$CMD --mode=unshare --variant=apt \
|
||||||
exit 1
|
--customize-hook='chroot "\$1" sh -c "test -e /proc/self/fd"' \
|
||||||
fi
|
$DEFAULT_DIST /tmp/debian-chroot.tar $mirror
|
||||||
sysctl -w kernel.unprivileged_userns_clone=1
|
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
|
||||||
ret=0
|
rm /tmp/debian-chroot.tar
|
||||||
$CMD --mode=unshare --variant=apt $DEFAULT_DIST /tmp/debian-chroot $mirror || ret=\$?
|
|
||||||
if [ "\$ret" = 0 ]; then
|
|
||||||
echo expected failure but got exit \$ret >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
END
|
END
|
||||||
if [ "$HAVE_QEMU" = "yes" ]; then
|
if [ "$HAVE_QEMU" = "yes" ]; then
|
||||||
./run_qemu.sh
|
./run_qemu.sh
|
||||||
runtests=$((runtests+1))
|
runtests=$((runtests+1))
|
||||||
else
|
else
|
||||||
echo "HAVE_QEMU != yes -- Skipping test..." >&2
|
./run_null.sh SUDO
|
||||||
skipped=$((skipped+1))
|
runtests=$((runtests+1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for variant in essential apt minbase buildd important standard; do
|
for variant in essential apt minbase buildd important standard; do
|
||||||
|
|
45
mmdebstrap
45
mmdebstrap
|
@ -223,10 +223,10 @@ sub get_tar_compressor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub test_unshare {
|
sub test_unshare_userns {
|
||||||
my $verbose = shift;
|
my $verbose = shift;
|
||||||
if ($EFFECTIVE_USER_ID == 0) {
|
if ($EFFECTIVE_USER_ID == 0) {
|
||||||
my $msg = "cannot use unshare mode when executing as root";
|
my $msg = "cannot unshare user namespace when executing as root";
|
||||||
if ($verbose) {
|
if ($verbose) {
|
||||||
warning $msg;
|
warning $msg;
|
||||||
} else {
|
} else {
|
||||||
|
@ -401,9 +401,14 @@ sub get_unshare_cmd {
|
||||||
my $cmd = shift;
|
my $cmd = shift;
|
||||||
my $idmap = shift;
|
my $idmap = shift;
|
||||||
|
|
||||||
|
# unsharing the mount namespace (NEWNS) requires CAP_SYS_ADMIN
|
||||||
my $unshare_flags
|
my $unshare_flags
|
||||||
= $CLONE_NEWUSER | $CLONE_NEWNS | $CLONE_NEWPID | $CLONE_NEWUTS
|
= $CLONE_NEWNS | $CLONE_NEWPID | $CLONE_NEWUTS | $CLONE_NEWIPC;
|
||||||
| $CLONE_NEWIPC;
|
|
||||||
|
# we only need to add CLONE_NEWUSER if we are not yet root
|
||||||
|
if ($EFFECTIVE_USER_ID != 0) {
|
||||||
|
$unshare_flags |= $CLONE_NEWUSER;
|
||||||
|
}
|
||||||
|
|
||||||
if (0) {
|
if (0) {
|
||||||
$unshare_flags |= $CLONE_NEWNET;
|
$unshare_flags |= $CLONE_NEWNET;
|
||||||
|
@ -447,6 +452,11 @@ sub get_unshare_cmd {
|
||||||
# waiting for an EOF.
|
# waiting for an EOF.
|
||||||
0 == sysread $rfh, my $c, 1 or error "read() did not receive EOF";
|
0 == sysread $rfh, my $c, 1 or error "read() did not receive EOF";
|
||||||
|
|
||||||
|
# the process is already root, so no need for newuidmap/newgidmap
|
||||||
|
if ($EFFECTIVE_USER_ID == 0) {
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
# The program's new[ug]idmap have to be used because they are
|
# The program's new[ug]idmap have to be used because they are
|
||||||
# setuid root. These privileges are needed to map the ids from
|
# setuid root. These privileges are needed to map the ids from
|
||||||
# /etc/sub[ug]id to the user namespace set up by the parent.
|
# /etc/sub[ug]id to the user namespace set up by the parent.
|
||||||
|
@ -515,9 +525,11 @@ sub get_unshare_cmd {
|
||||||
# want here, like checking /proc/sys/kernel/ngroups_max (which might
|
# want here, like checking /proc/sys/kernel/ngroups_max (which might
|
||||||
# not exist). It would also also call setgroups() in a way that makes
|
# not exist). It would also also call setgroups() in a way that makes
|
||||||
# the root user be part of the group unknown.
|
# the root user be part of the group unknown.
|
||||||
|
if ($EFFECTIVE_USER_ID != 0) {
|
||||||
0 == syscall &SYS_setgid, 0 or error "setgid failed: $!";
|
0 == syscall &SYS_setgid, 0 or error "setgid failed: $!";
|
||||||
0 == syscall &SYS_setuid, 0 or error "setuid failed: $!";
|
0 == syscall &SYS_setuid, 0 or error "setuid failed: $!";
|
||||||
0 == syscall &SYS_setgroups, 0, 0 or error "setgroups failed: $!";
|
0 == syscall &SYS_setgroups, 0, 0 or error "setgroups failed: $!";
|
||||||
|
}
|
||||||
|
|
||||||
if (1) {
|
if (1) {
|
||||||
# When the pid namespace is also unshared, then processes expect a
|
# When the pid namespace is also unshared, then processes expect a
|
||||||
|
@ -4134,7 +4146,7 @@ sub main() {
|
||||||
# lxc-usernsexec -- lxc-unshare -s 'MOUNT|PID|UTSNAME|IPC' ...
|
# lxc-usernsexec -- lxc-unshare -s 'MOUNT|PID|UTSNAME|IPC' ...
|
||||||
# but without needing lxc
|
# but without needing lxc
|
||||||
if ($ARGV[0] eq "--unshare-helper") {
|
if ($ARGV[0] eq "--unshare-helper") {
|
||||||
if (!test_unshare(1)) {
|
if (!test_unshare_userns(1)) {
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
my @idmap = read_subuid_subgid;
|
my @idmap = read_subuid_subgid;
|
||||||
|
@ -4408,9 +4420,9 @@ sub main() {
|
||||||
# if mmdebstrap is executed as root, we assume the user wants root
|
# if mmdebstrap is executed as root, we assume the user wants root
|
||||||
# mode
|
# mode
|
||||||
$options->{mode} = 'root';
|
$options->{mode} = 'root';
|
||||||
} elsif (test_unshare(0)) {
|
} elsif (test_unshare_userns(0)) {
|
||||||
# otherwise, unshare mode is our best option if test_unshare()
|
# if we are not root, unshare mode is our best option if
|
||||||
# succeeds
|
# test_unshare_userns() succeeds
|
||||||
$options->{mode} = 'unshare';
|
$options->{mode} = 'unshare';
|
||||||
} elsif (system('fakechroot --version>/dev/null') == 0) {
|
} elsif (system('fakechroot --version>/dev/null') == 0) {
|
||||||
# the next fallback is fakechroot
|
# the next fallback is fakechroot
|
||||||
|
@ -4449,7 +4461,16 @@ sub main() {
|
||||||
exec 'fakechroot', 'fakeroot', @prefix, $PROGRAM_NAME, @ARGVORIG;
|
exec 'fakechroot', 'fakeroot', @prefix, $PROGRAM_NAME, @ARGVORIG;
|
||||||
}
|
}
|
||||||
} elsif ($options->{mode} eq 'unshare') {
|
} elsif ($options->{mode} eq 'unshare') {
|
||||||
if (!test_unshare(1)) {
|
# For unshare mode to work we either need to already be the root user
|
||||||
|
# and then we do not have to unshare the user namespace anymore but we
|
||||||
|
# need to be able to unshare the mount namespace...
|
||||||
|
if ($EFFECTIVE_USER_ID == 0
|
||||||
|
&& 0 != system 'unshare --mount true 2>/dev/null') {
|
||||||
|
error "unable to unshare the mount namespace";
|
||||||
|
}
|
||||||
|
# ...or we are not root and then we need to be able to unshare the user
|
||||||
|
# namespace.
|
||||||
|
if ($EFFECTIVE_USER_ID != 0 && !test_unshare_userns(1)) {
|
||||||
my $procfile = '/proc/sys/kernel/unprivileged_userns_clone';
|
my $procfile = '/proc/sys/kernel/unprivileged_userns_clone';
|
||||||
open(my $fh, '<', $procfile)
|
open(my $fh, '<', $procfile)
|
||||||
or error "failed to open $procfile: $!";
|
or error "failed to open $procfile: $!";
|
||||||
|
@ -6132,6 +6153,12 @@ available and you know your subuid/subgid offset (100000 in this example):
|
||||||
$ sudo systemd-nspawn --private-users=100000 \
|
$ sudo systemd-nspawn --private-users=100000 \
|
||||||
> --directory=./debian-rootfs /bin/bash
|
> --directory=./debian-rootfs /bin/bash
|
||||||
|
|
||||||
|
If this mode is used as the root user, the user namespace is not unshared (but
|
||||||
|
the mount namespace and other still are) and created directories will have
|
||||||
|
correct ownership information. This is also useful in cases where the root user
|
||||||
|
wants the benefits of an unshared mount namespace to prevent accidentally
|
||||||
|
messing up the system.
|
||||||
|
|
||||||
=item B<fakeroot>, B<fakechroot>
|
=item B<fakeroot>, B<fakechroot>
|
||||||
|
|
||||||
This mode will exec B<mmdebstrap> again under C<fakechroot fakeroot>. A
|
This mode will exec B<mmdebstrap> again under C<fakechroot fakeroot>. A
|
||||||
|
|
Loading…
Reference in a new issue