Compare commits
7 commits
850eeb24d5
...
7d472ca116
Author | SHA1 | Date | |
---|---|---|---|
7d472ca116 | |||
047619967e | |||
5a5f57b404 | |||
7ab770267c | |||
1a18160fe8 | |||
e53d246a3b | |||
91d8be5f9c |
3 changed files with 95 additions and 96 deletions
|
@ -39,6 +39,7 @@ Summary:
|
|||
- foreign architecture chroots with qemu-user
|
||||
- variant installing only Essential:yes packages and dependencies
|
||||
- temporary chroots by redirecting to /dev/null
|
||||
- chroots without apt inside (for chroot from buildinfo file with debootsnap)
|
||||
|
||||
The author believes that a chroot of a Debian stable release should include the
|
||||
latest packages including security fixes by default. This has been a wontfix
|
||||
|
|
65
coverage.sh
65
coverage.sh
|
@ -120,7 +120,7 @@ if [ ! -e shared/hooks/eatmydata/customize.sh ] || [ hooks/eatmydata/customize.s
|
|||
fi
|
||||
fi
|
||||
starttime=
|
||||
total=198
|
||||
total=217
|
||||
skipped=0
|
||||
runtests=0
|
||||
i=1
|
||||
|
@ -655,19 +655,18 @@ else
|
|||
runtests=$((runtests+1))
|
||||
fi
|
||||
|
||||
print_header "mode=root,variant=apt: fail with root without cap_sys_admin"
|
||||
print_header "mode=unshare,variant=apt: root without cap_sys_admin"
|
||||
cat << END > shared/test.sh
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
export LC_ALL=C.UTF-8
|
||||
ret=0
|
||||
[ "\$(whoami)" = "root" ]
|
||||
capsh --drop=cap_sys_admin -- -c 'exec "\$@"' exec \
|
||||
$CMD --mode=root --variant=apt $DEFAULT_DIST /tmp/debian-chroot $mirror || ret=\$?
|
||||
if [ "\$ret" = 0 ]; then
|
||||
echo expected failure but got exit \$ret >&2
|
||||
exit 1
|
||||
fi
|
||||
[ ! -e /tmp/debian-chroot ]
|
||||
$CMD --mode=root --variant=apt \
|
||||
--customize-hook='chroot "\$1" sh -c "test ! -e /proc/self/fd"' \
|
||||
$DEFAULT_DIST /tmp/debian-chroot.tar $mirror
|
||||
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
|
||||
rm /tmp/debian-chroot.tar
|
||||
END
|
||||
if [ "$CONTAINER" = "lxc" ]; then
|
||||
# see https://stackoverflow.com/questions/65748254/
|
||||
|
@ -681,45 +680,19 @@ else
|
|||
runtests=$((runtests+1))
|
||||
fi
|
||||
|
||||
print_header "mode=root,variant=apt: fail without mounted /proc"
|
||||
print_header "mode=root,variant=apt: mount is missing"
|
||||
cat << END > shared/test.sh
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
export LC_ALL=C.UTF-8
|
||||
# success with /proc mounted
|
||||
$CMD --mode=root --variant=apt \
|
||||
--customize-hook='chroot "\$1" bash -c "test \\"\\\$(cat <(echo foobar))\\" = foobar"' \
|
||||
$DEFAULT_DIST /dev/null $mirror
|
||||
# failure without /proc mounted (using --skip=check/canmount)
|
||||
ret=0
|
||||
$CMD --mode=root --variant=apt \
|
||||
--customize-hook='chroot "\$1" bash -c "test \\"\\\$(cat <(echo foobar))\\" = foobar"' \
|
||||
--skip=check/canmount $DEFAULT_DIST /tmp/debian-chroot $mirror || ret=\$?
|
||||
if [ "\$ret" = 0 ]; then
|
||||
echo expected failure but got exit \$ret >&2
|
||||
if [ ! -e /mmdebstrap-testenv ]; then
|
||||
echo "this test modifies the system and should only be run inside a container" >&2
|
||||
exit 1
|
||||
fi
|
||||
rm -r /tmp/debian-chroot
|
||||
END
|
||||
if [ "$HAVE_QEMU" = "yes" ]; then
|
||||
./run_qemu.sh
|
||||
runtests=$((runtests+1))
|
||||
else
|
||||
./run_null.sh SUDO
|
||||
runtests=$((runtests+1))
|
||||
fi
|
||||
|
||||
|
||||
print_header "mode=unshare,variant=apt: root without cap_sys_admin but --skip=check/canmount"
|
||||
cat << END > shared/test.sh
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
export LC_ALL=C.UTF-8
|
||||
[ "\$(whoami)" = "root" ]
|
||||
capsh --drop=cap_sys_admin -- -c 'exec "\$@"' exec \
|
||||
$CMD --mode=root --variant=apt \
|
||||
--customize-hook='chroot "\$1" sh -c "test ! -e /proc/self/fd"' \
|
||||
--skip=check/canmount $DEFAULT_DIST /tmp/debian-chroot.tar $mirror
|
||||
for p in /bin /usr/bin /sbin /usr/sbin; do
|
||||
rm -f "\$p/mount"
|
||||
done
|
||||
$CMD --mode=root --variant=apt $DEFAULT_DIST /tmp/debian-chroot.tar $mirror
|
||||
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
|
||||
rm /tmp/debian-chroot.tar
|
||||
END
|
||||
|
@ -727,8 +700,8 @@ if [ "$HAVE_QEMU" = "yes" ]; then
|
|||
./run_qemu.sh
|
||||
runtests=$((runtests+1))
|
||||
else
|
||||
./run_null.sh SUDO
|
||||
runtests=$((runtests+1))
|
||||
echo "HAVE_QEMU != yes -- Skipping test..." >&2
|
||||
skipped=$((skipped+1))
|
||||
fi
|
||||
|
||||
for variant in essential apt minbase buildd important standard; do
|
||||
|
@ -3097,7 +3070,9 @@ fi
|
|||
|
||||
# test all --dry-run variants
|
||||
|
||||
for variant in extract custom essential apt; do
|
||||
# we are testing all variants here because with 0.7.5 we had a bug:
|
||||
# mmdebstrap sid /dev/null --simulate ==> E: cannot read /var/cache/apt/archives/
|
||||
for variant in extract custom essential apt minbase buildd important standard; do
|
||||
for mode in root unshare fakechroot proot chrootless; do
|
||||
print_header "mode=$mode,variant=$variant: create tarball --dry-run"
|
||||
if [ "$mode" = "unshare" ] && [ "$HAVE_UNSHARE" != "yes" ]; then
|
||||
|
|
125
mmdebstrap
125
mmdebstrap
|
@ -58,9 +58,14 @@ use version;
|
|||
*CLONE_NEWNET = \0x40000000; # net
|
||||
*_LINUX_CAPABILITY_VERSION_3 = \0x20080522;
|
||||
*CAP_SYS_ADMIN = \21;
|
||||
our ($CLONE_NEWNS, $CLONE_NEWUTS, $CLONE_NEWIPC,
|
||||
$CLONE_NEWUSER, $CLONE_NEWPID, $CLONE_NEWNET,
|
||||
$_LINUX_CAPABILITY_VERSION_3, $CAP_SYS_ADMIN);
|
||||
*PR_CAPBSET_READ = \23;
|
||||
our (
|
||||
$CLONE_NEWNS, $CLONE_NEWUTS,
|
||||
$CLONE_NEWIPC, $CLONE_NEWUSER,
|
||||
$CLONE_NEWPID, $CLONE_NEWNET,
|
||||
$_LINUX_CAPABILITY_VERSION_3, $CAP_SYS_ADMIN,
|
||||
$PR_CAPBSET_READ
|
||||
);
|
||||
|
||||
#<<<
|
||||
# type codes:
|
||||
|
@ -1009,7 +1014,8 @@ sub run_chroot {
|
|||
}
|
||||
} elsif ($type == 3 or $type == 4) {
|
||||
# character/block special
|
||||
if ($options->{mode} eq 'root' && !$options->{canmount}) {
|
||||
if ((any { $_ eq $options->{mode} } ('root', 'unshare'))
|
||||
&& !$options->{canmount}) {
|
||||
warning "skipping bind-mounting ./dev/$fname";
|
||||
} elsif (!$options->{havemknod}) {
|
||||
if (!-d "$options->{root}/dev") {
|
||||
|
@ -1051,7 +1057,7 @@ sub run_chroot {
|
|||
or error "mount ./dev/$fname failed: $?";
|
||||
}
|
||||
} elsif ($type == 5
|
||||
&& $options->{mode} eq 'root'
|
||||
&& (any { $_ eq $options->{mode} } ('root', 'unshare'))
|
||||
&& !$options->{canmount}) {
|
||||
warning "skipping bind-mounting ./dev/$fname";
|
||||
} elsif ($type == 5) { # directory
|
||||
|
@ -1135,11 +1141,21 @@ sub run_chroot {
|
|||
# We can only mount /proc and /sys after extracting the essential
|
||||
# set because if we mount it before, then base-files will not be able
|
||||
# to extract those
|
||||
if ($options->{mode} eq 'root' && !$options->{canmount}) {
|
||||
if ((any { $_ eq $options->{mode} } ('root', 'unshare'))
|
||||
&& !$options->{canmount}) {
|
||||
warning "skipping mount sysfs";
|
||||
} elsif ($options->{mode} eq 'root' && !-d "$options->{root}/sys") {
|
||||
} elsif ((any { $_ eq $options->{mode} } ('root', 'unshare'))
|
||||
&& !-d "$options->{root}/sys") {
|
||||
warning("skipping mounting of sysfs because the"
|
||||
. " /sys directory is missing in the target");
|
||||
} elsif ((any { $_ eq $options->{mode} } ('root', 'unshare'))
|
||||
&& !-e "/sys") {
|
||||
warning("skipping bind-mounting /sys because"
|
||||
. " /sys does not exist on the outside");
|
||||
} elsif ((any { $_ eq $options->{mode} } ('root', 'unshare'))
|
||||
&& !-d "/sys") {
|
||||
warning("skipping bind-mounting /sys because"
|
||||
. " /sys on the outside is not a directory");
|
||||
} elsif ($options->{mode} eq 'root') {
|
||||
push @cleanup_tasks, sub {
|
||||
0 == system('umount', "$options->{root}/sys")
|
||||
|
@ -1150,15 +1166,6 @@ sub run_chroot {
|
|||
'-o', 'ro,nosuid,nodev,noexec', 'sys',
|
||||
"$options->{root}/sys"
|
||||
) or error "mount /sys failed: $?";
|
||||
} elsif ($options->{mode} eq 'unshare' && !-d "$options->{root}/sys") {
|
||||
warning("skipping bind-mounting /sys because the"
|
||||
. " /sys directory is missing in the target");
|
||||
} elsif ($options->{mode} eq 'unshare' && !-e "/sys") {
|
||||
warning("skipping bind-mounting /sys because"
|
||||
. " /sys does not exist on the outside");
|
||||
} elsif ($options->{mode} eq 'unshare' && !-d "/sys") {
|
||||
warning("skipping bind-mounting /sys because"
|
||||
. " /sys on the outside is not a directory");
|
||||
} elsif ($options->{mode} eq 'unshare') {
|
||||
# naturally we have to clean up after ourselves in sudo mode where
|
||||
# we do a real mount. But we also need to unmount in unshare mode
|
||||
|
@ -1189,11 +1196,21 @@ sub run_chroot {
|
|||
} else {
|
||||
error "unknown mode: $options->{mode}";
|
||||
}
|
||||
if ($options->{mode} eq 'root' && !$options->{canmount}) {
|
||||
if ((any { $_ eq $options->{mode} } ('root', 'unshare'))
|
||||
&& !$options->{canmount}) {
|
||||
warning "skipping mount proc";
|
||||
} elsif ($options->{mode} eq 'root' && !-d "$options->{root}/proc") {
|
||||
} elsif ((any { $_ eq $options->{mode} } ('root', 'unshare'))
|
||||
&& !-d "$options->{root}/proc") {
|
||||
warning("skipping mounting of proc because the"
|
||||
. " /proc directory is missing in the target");
|
||||
} elsif ((any { $_ eq $options->{mode} } ('root', 'unshare'))
|
||||
&& !-e "/proc") {
|
||||
warning("skipping bind-mounting /proc because"
|
||||
. " /proc does not exist on the outside");
|
||||
} elsif ((any { $_ eq $options->{mode} } ('root', 'unshare'))
|
||||
&& !-d "/proc") {
|
||||
warning("skipping bind-mounting /proc because"
|
||||
. " /proc on the outside is not a directory");
|
||||
} elsif ($options->{mode} eq 'root') {
|
||||
push @cleanup_tasks, sub {
|
||||
# some maintainer scripts mount additional stuff into /proc
|
||||
|
@ -1213,16 +1230,6 @@ sub run_chroot {
|
|||
0 == system('mount', '-t', 'proc', '-o', 'ro', 'proc',
|
||||
"$options->{root}/proc")
|
||||
or error "mount /proc failed: $?";
|
||||
} elsif ($options->{mode} eq 'unshare' && !-d "$options->{root}/proc")
|
||||
{
|
||||
warning("skipping bind-mounting /proc because the"
|
||||
. " /proc directory is missing in the target");
|
||||
} elsif ($options->{mode} eq 'unshare' && !-e "/proc") {
|
||||
warning("skipping bind-mounting /proc because"
|
||||
. " /proc does not exist on the outside");
|
||||
} elsif ($options->{mode} eq 'unshare' && !-d "/proc") {
|
||||
warning("skipping bind-mounting /proc because"
|
||||
. " /proc on the outside is not a directory");
|
||||
} elsif ($options->{mode} eq 'unshare') {
|
||||
# naturally we have to clean up after ourselves in sudo mode where
|
||||
# we do a real mount. But we also need to unmount in unshare mode
|
||||
|
@ -4648,13 +4655,8 @@ sub main() {
|
|||
error "unknown mode: $options->{mode}";
|
||||
}
|
||||
|
||||
# By default, mount is not used. This is so that mounting is skipped if the
|
||||
# user supplies --skip=check/canmount. This only gets enabled if
|
||||
# CAP_SYS_ADMIN and unshare --mount are available in root mode.
|
||||
$options->{canmount} = 0;
|
||||
if (any { $_ eq 'check/canmount' } @{ $options->{skip} }) {
|
||||
info "skipping check/canmount as requested";
|
||||
} elsif ($options->{mode} eq 'root') {
|
||||
$options->{canmount} = 1;
|
||||
if ($options->{mode} eq 'root') {
|
||||
# It's possible to be root but not be able to mount anything.
|
||||
# This is for example the case when running under docker.
|
||||
# Mounting needs CAP_SYS_ADMIN which might not be available.
|
||||
|
@ -4671,11 +4673,15 @@ sub main() {
|
|||
0 == syscall &SYS_capget, $hdrp, $datap
|
||||
or error "capget failed: $!";
|
||||
my ($effective, undef) = unpack "LLLLLL", $datap;
|
||||
if (($effective >> $CAP_SYS_ADMIN) & 1 == 1) {
|
||||
# we have CAP_SYS_ADMIN, and thus can mount
|
||||
$options->{canmount} = 1;
|
||||
} else {
|
||||
error "root mode requires mount which requires CAP_SYS_ADMIN";
|
||||
if (($effective >> $CAP_SYS_ADMIN) & 1 != 1) {
|
||||
warning
|
||||
"cannot mount because CAP_SYS_ADMIN is not in the effective set";
|
||||
$options->{canmount} = 0;
|
||||
}
|
||||
if (0 == syscall &SYS_prctl, $PR_CAPBSET_READ, $CAP_SYS_ADMIN) {
|
||||
warning
|
||||
"cannot mount because CAP_SYS_ADMIN is not in the bounding set";
|
||||
$options->{canmount} = 0;
|
||||
}
|
||||
# To test whether we can use mount without actually trying to mount
|
||||
# something we try unsharing the mount namespace. If this is allowed,
|
||||
|
@ -4685,15 +4691,26 @@ sub main() {
|
|||
# we get 'cannot change root filesystem propagation' when running
|
||||
# mmdebstrap inside a chroot for which the root of the chroot is not
|
||||
# its own mount point.
|
||||
if (0 == system 'unshare --mount --propagation unchanged -- true') {
|
||||
$options->{canmount} = 1;
|
||||
} else {
|
||||
if (0 != system 'unshare --mount --propagation unchanged -- true') {
|
||||
# if we cannot unshare the mount namespace as root, then we also
|
||||
# cannot mount
|
||||
error "root mode requires mount but unshare --mount failed";
|
||||
warning "cannot mount because unshare --mount failed";
|
||||
$options->{canmount} = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (any { $_ eq $options->{mode} } ('root', 'unshare')) {
|
||||
if (system('mount --version>/dev/null') != 0) {
|
||||
warning "cannot execute mount";
|
||||
$options->{canmount} = 0;
|
||||
}
|
||||
}
|
||||
|
||||
# we can only possibly mount in root and unshare mode
|
||||
if (none { $_ eq $options->{mode} } ('root', 'unshare')) {
|
||||
$options->{canmount} = 0;
|
||||
}
|
||||
|
||||
my @architectures = ();
|
||||
foreach my $archs (@{ $options->{architectures} }) {
|
||||
foreach my $arch (split /[,\s]+/, $archs) {
|
||||
|
@ -4986,7 +5003,6 @@ sub main() {
|
|||
'--ignore-time-conflict', '--no-options',
|
||||
'--no-default-keyring', '--homedir',
|
||||
$gpghome, '--no-auto-check-trustdb',
|
||||
'--trust-model', 'always'
|
||||
);
|
||||
my ($ret, $message);
|
||||
{
|
||||
|
@ -5994,10 +6010,11 @@ option happens to be an existing file, then its contents are pasted into the
|
|||
chroot's sources.list. This can be used to supply a deb822 style
|
||||
sources.list. If I<MIRROR> is C<-> then standard input is pasted into the
|
||||
chroot's sources.list. More than one mirror can be specified and are appended
|
||||
to the chroot's sources.list in the given order. If any mirror contains a
|
||||
https URI, then the packages apt-transport-https and ca-certificates will be
|
||||
installed inside the chroot. If any mirror contains a tor+xxx URI, then the
|
||||
apt-transport-tor package will be installed inside the chroot.
|
||||
to the chroot's sources.list in the given order. If you specify a https or tor
|
||||
I<MIRROR> and you want the chroot to be able to update itself, don't forget to
|
||||
also install the ca-certificates package, the apt-transport-https package for
|
||||
apt versions less than 1.5 and/or the apt-transport-tor package using the
|
||||
B<--include> option, as necessary.
|
||||
|
||||
The optional I<TARGET> argument can either be the path to a directory, the path
|
||||
to a tarball filename, the path to a squashfs image, the path to an ext2 image,
|
||||
|
@ -6689,8 +6706,6 @@ Upon startup, several checks are carried out, like:
|
|||
|
||||
=item * which mode to use and whether prerequisites are met
|
||||
|
||||
=item * if you are root, check whether you have the ability to mount. This check requires the C<unshare> program from the C<util-linux> package and can be disabled by using B<--skip=check/canmount>.
|
||||
|
||||
=item * whether the requested architecture can be executed (requires arch-test) using qemu binfmt_misc support. This requires arch-test and can be disabled using B<--skip=check/qemu>
|
||||
|
||||
=item * how the apt sources can be assembled from I<SUITE>, I<MIRROR> and B<--components> and/or from standard input as deb822 or one-line format and whether the required GPG keys exist.
|
||||
|
@ -6965,6 +6980,14 @@ Create a system that can be used with docker:
|
|||
root
|
||||
$ sudo docker rmi debian
|
||||
|
||||
Create a system that can be used with podman:
|
||||
|
||||
$ mmdebstrap unstable | podman import - debian
|
||||
[...]
|
||||
$ podman run --network=none -it --rm debian whoami
|
||||
root
|
||||
$ podman rmi debian
|
||||
|
||||
=head1 ENVIRONMENT VARIABLES
|
||||
|
||||
=over 8
|
||||
|
|
Loading…
Reference in a new issue