try unsharing before automatically choosing unshare mode
This commit is contained in:
parent
2614924925
commit
055e1719b9
3 changed files with 83 additions and 59 deletions
128
mmdebstrap
128
mmdebstrap
|
@ -305,14 +305,23 @@ sub shellescape {
|
||||||
|
|
||||||
sub test_unshare_userns {
|
sub test_unshare_userns {
|
||||||
my $verbose = shift;
|
my $verbose = shift;
|
||||||
my $unshare_fail = shift;
|
my $fail = shift;
|
||||||
if ($EFFECTIVE_USER_ID == 0) {
|
|
||||||
my $msg = "cannot unshare user namespace when executing as root";
|
local *maybe_warn = sub {
|
||||||
|
my $msg = shift;
|
||||||
if ($verbose) {
|
if ($verbose) {
|
||||||
|
if ($fail) {
|
||||||
|
error $msg;
|
||||||
|
} else {
|
||||||
warning $msg;
|
warning $msg;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
debug $msg;
|
debug $msg;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($EFFECTIVE_USER_ID == 0) {
|
||||||
|
maybe_warn("cannot unshare user namespace when executing as root");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
# arguments to syscalls have to be stored in their own variable or
|
# arguments to syscalls have to be stored in their own variable or
|
||||||
|
@ -326,12 +335,7 @@ sub test_unshare_userns {
|
||||||
if ($ret == 0) {
|
if ($ret == 0) {
|
||||||
exit 0;
|
exit 0;
|
||||||
} else {
|
} else {
|
||||||
my $msg = "unshare syscall failed: $!";
|
maybe_warn("unshare syscall failed: $!");
|
||||||
if ($verbose) {
|
|
||||||
warning $msg;
|
|
||||||
} else {
|
|
||||||
debug $msg;
|
|
||||||
}
|
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -344,120 +348,140 @@ sub test_unshare_userns {
|
||||||
system "newuidmap 2>/dev/null";
|
system "newuidmap 2>/dev/null";
|
||||||
if (($? >> 8) != 1) {
|
if (($? >> 8) != 1) {
|
||||||
if (($? >> 8) == 127) {
|
if (($? >> 8) == 127) {
|
||||||
my $msg = "cannot find newuidmap";
|
maybe_warn("cannot find newuidmap");
|
||||||
if ($verbose) {
|
|
||||||
if ($unshare_fail) {
|
|
||||||
error $msg;
|
|
||||||
} else {
|
} else {
|
||||||
warning $msg;
|
maybe_warn("newuidmap returned unknown exit status: $?");
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debug $msg;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
my $msg = "newuidmap returned unknown exit status: $?";
|
|
||||||
if ($verbose) {
|
|
||||||
warning $msg;
|
|
||||||
} else {
|
|
||||||
debug $msg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
system "newgidmap 2>/dev/null";
|
system "newgidmap 2>/dev/null";
|
||||||
if (($? >> 8) != 1) {
|
if (($? >> 8) != 1) {
|
||||||
if (($? >> 8) == 127) {
|
if (($? >> 8) == 127) {
|
||||||
my $msg = "cannot find newgidmap";
|
maybe_warn("cannot find newgidmap");
|
||||||
if ($verbose) {
|
|
||||||
warning $msg;
|
|
||||||
} else {
|
} else {
|
||||||
debug $msg;
|
maybe_warn("newgidmap returned unknown exit status: $?");
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
my @idmap = read_subuid_subgid($verbose);
|
||||||
|
if (scalar @idmap == 0) {
|
||||||
|
maybe_warn("failed to parse /etc/subuid and /etc/subgid");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
# too much can go wrong when doing the dance required to unsharing the user
|
||||||
|
# namespace, so instead of adding more complexity to support maybe_warn()
|
||||||
|
# to a function that is already too complex, we use eval()
|
||||||
|
eval {
|
||||||
|
$pid = get_unshare_cmd(
|
||||||
|
sub {
|
||||||
|
if ($EFFECTIVE_USER_ID == 0) {
|
||||||
|
exit 0;
|
||||||
} else {
|
} else {
|
||||||
my $msg = "newgidmap returned unknown exit status: $?";
|
exit 1;
|
||||||
if ($verbose) {
|
|
||||||
warning $msg;
|
|
||||||
} else {
|
|
||||||
debug $msg;
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
\@idmap
|
||||||
|
);
|
||||||
|
waitpid $pid, 0;
|
||||||
|
if ($? != 0) {
|
||||||
|
maybe_warn("failed to unshare the user namespace");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
maybe_warn($@);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub read_subuid_subgid() {
|
sub read_subuid_subgid {
|
||||||
|
my $verbose = shift;
|
||||||
|
my @result = ();
|
||||||
my $username = getpwuid $REAL_USER_ID;
|
my $username = getpwuid $REAL_USER_ID;
|
||||||
my ($subid, $num_subid, $fh, $n);
|
my ($subid, $num_subid, $fh, $n);
|
||||||
my @result = ();
|
|
||||||
|
|
||||||
|
local *maybe_warn = sub {
|
||||||
|
my $msg = shift;
|
||||||
|
if ($verbose) {
|
||||||
|
warning $msg;
|
||||||
|
} else {
|
||||||
|
debug $msg;
|
||||||
|
}
|
||||||
|
};
|
||||||
if (!-e "/etc/subuid") {
|
if (!-e "/etc/subuid") {
|
||||||
warning "/etc/subuid doesn't exist";
|
maybe_warn("/etc/subuid doesn't exist");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!-r "/etc/subuid") {
|
if (!-r "/etc/subuid") {
|
||||||
warning "/etc/subuid is not readable";
|
maybe_warn("/etc/subuid is not readable");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
open $fh, "<", "/etc/subuid"
|
open $fh, "<", "/etc/subuid"
|
||||||
or error "cannot open /etc/subuid for reading: $!";
|
or maybe_warn("cannot open /etc/subuid for reading: $!");
|
||||||
|
if (!$fh) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
while (my $line = <$fh>) {
|
while (my $line = <$fh>) {
|
||||||
($n, $subid, $num_subid) = split(/:/, $line, 3);
|
($n, $subid, $num_subid) = split(/:/, $line, 3);
|
||||||
last if ($n eq $username);
|
last if ($n eq $username);
|
||||||
}
|
}
|
||||||
close $fh;
|
close $fh;
|
||||||
if (!length $subid) {
|
if (!length $subid) {
|
||||||
warning "/etc/subuid is empty";
|
maybe_warn("/etc/subuid is empty");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($n ne $username) {
|
if ($n ne $username) {
|
||||||
warning "no entry in /etc/subuid for $username";
|
maybe_warn("no entry in /etc/subuid for $username");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
push @result, ["u", 0, $subid, $num_subid];
|
push @result, ["u", 0, $subid, $num_subid];
|
||||||
|
|
||||||
if (scalar(@result) < 1) {
|
if (scalar(@result) < 1) {
|
||||||
warning "/etc/subuid does not contain an entry for $username";
|
maybe_warn("/etc/subuid does not contain an entry for $username");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (scalar(@result) > 1) {
|
if (scalar(@result) > 1) {
|
||||||
warning "/etc/subuid contains multiple entries for $username";
|
maybe_warn("/etc/subuid contains multiple entries for $username");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!-e "/etc/subgid") {
|
if (!-e "/etc/subgid") {
|
||||||
warning "/etc/subgid doesn't exist";
|
maybe_warn("/etc/subgid doesn't exist");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!-r "/etc/subgid") {
|
if (!-r "/etc/subgid") {
|
||||||
warning "/etc/subgid is not readable";
|
maybe_warn("/etc/subgid is not readable");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
open $fh, "<", "/etc/subgid"
|
open $fh, "<", "/etc/subgid"
|
||||||
or error "cannot open /etc/subgid for reading: $!";
|
or maybe_warn("cannot open /etc/subgid for reading: $!");
|
||||||
|
if (!$fh) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
while (my $line = <$fh>) {
|
while (my $line = <$fh>) {
|
||||||
($n, $subid, $num_subid) = split(/:/, $line, 3);
|
($n, $subid, $num_subid) = split(/:/, $line, 3);
|
||||||
last if ($n eq $username);
|
last if ($n eq $username);
|
||||||
}
|
}
|
||||||
close $fh;
|
close $fh;
|
||||||
if (!length $subid) {
|
if (!length $subid) {
|
||||||
warning "/etc/subgid is empty";
|
maybe_warn("/etc/subgid is empty");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($n ne $username) {
|
if ($n ne $username) {
|
||||||
warning "no entry in /etc/subgid for $username";
|
maybe_warn("no entry in /etc/subgid for $username");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
push @result, ["g", 0, $subid, $num_subid];
|
push @result, ["g", 0, $subid, $num_subid];
|
||||||
|
|
||||||
if (scalar(@result) < 2) {
|
if (scalar(@result) < 2) {
|
||||||
warning "/etc/subgid does not contain an entry for $username";
|
maybe_warn("/etc/subgid does not contain an entry for $username");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (scalar(@result) > 2) {
|
if (scalar(@result) > 2) {
|
||||||
warning "/etc/subgid contains multiple entries for $username";
|
maybe_warn("/etc/subgid contains multiple entries for $username");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4353,7 +4377,7 @@ sub main() {
|
||||||
}
|
}
|
||||||
my @idmap = ();
|
my @idmap = ();
|
||||||
if ($EFFECTIVE_USER_ID != 0) {
|
if ($EFFECTIVE_USER_ID != 0) {
|
||||||
@idmap = read_subuid_subgid;
|
@idmap = read_subuid_subgid 1;
|
||||||
}
|
}
|
||||||
my $pid = get_unshare_cmd(
|
my $pid = get_unshare_cmd(
|
||||||
sub {
|
sub {
|
||||||
|
@ -5665,7 +5689,7 @@ sub main() {
|
||||||
# for unshare mode the rootfs directory has to have appropriate
|
# for unshare mode the rootfs directory has to have appropriate
|
||||||
# permissions
|
# permissions
|
||||||
if ($EFFECTIVE_USER_ID != 0 and $options->{mode} eq 'unshare') {
|
if ($EFFECTIVE_USER_ID != 0 and $options->{mode} eq 'unshare') {
|
||||||
@idmap = read_subuid_subgid;
|
@idmap = read_subuid_subgid 1;
|
||||||
# sanity check
|
# sanity check
|
||||||
if ( scalar(@idmap) != 2
|
if ( scalar(@idmap) != 2
|
||||||
|| $idmap[0][0] ne 'u'
|
|| $idmap[0][0] ne 'u'
|
||||||
|
|
|
@ -13,4 +13,4 @@ if [ "$ret" = 0 ]; then
|
||||||
echo expected failure but got exit $ret >&2
|
echo expected failure but got exit $ret >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
rm -r /tmp/debian-chroot
|
[ ! -e /tmp/debian-chroot ]
|
||||||
|
|
|
@ -14,4 +14,4 @@ if [ "$ret" = 0 ]; then
|
||||||
echo expected failure but got exit $ret >&2
|
echo expected failure but got exit $ret >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
rm -r /tmp/debian-chroot
|
[ ! -e /tmp/debian-chroot ]
|
||||||
|
|
Loading…
Reference in a new issue