Package: debootstrap Version: 1.0.81 Severity: wishlist Tags: patch Dear Maintainer,
Now that the kernel supports user_namespaces(7), it should be possible to debootstrap in them. Some small changes are needed. Configuration needed: * Kernel 3.8 or later (3.11 recommended) * Set the sysctl kernel.unprivileged_userns_clone to 1 (Debian-specific "temporary" patch from years ago). * Install the `uidmap` package and add yourself to /etc/sub[ug]id * Install the `lxc` package (for one helper binary only) * Make sure the current directory is searchable by other. I have attached the necessary changes as a wrapper script, but there really should be some architectural changes: * The `/usr/sbin/debootstrap` vs `/usr/share/debootstrap/functions` split is quite painful. Move everything into one file and then replace sbin/debootstrap with basically `source functions; main`. * Satisfy `shellcheck`s errors and warnings, and suppress the rest. * Beware that shellcheck currently does not catch `echo $(false)`. * Make it possible to use more than one `--variant` at once somehow. * Debootstrap is currently not idempotent - see the `rm dev...` hack. * If you're in a new mount namespace, no need to `umount` at the end. -- System Information: Debian Release: stretch/sid APT prefers testing-debug APT policy: (600, 'testing-debug'), (600, 'testing'), (500, 'unstable-debug'), (500, 'unstable'), (1, 'experimental') Architecture: amd64 (x86_64) Foreign Architectures: i386, x32 Kernel: Linux 4.6.0-1-amd64 (SMP w/4 CPU cores) Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash Init: systemd (via /run/systemd/system) Versions of packages debootstrap depends on: ii wget 1.18-1 Versions of packages debootstrap recommends: ii debian-archive-keyring 2014.3 ii gnupg 1.4.20-6 debootstrap suggests no packages. -- no debconf information
#!/bin/sh # userns-debootstrap - debootstrap in a unprivileged new UID namespace # # Copyright (c) 2016 Ben Longbons # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # (This is the same license as debootstrap itself). # This currently requires Debootstrap 1.0.76 or later, # to avoid devices.tar.gz (see debian bug 571136). # Note that, if everything works *correctly*, we `exec` rather than `exit`. set -e trap 'echo Failed during setup' EXIT bailout() { local status="$?" echo "Bailing out with status $status" if test -z "$BASH" then echo 'For more debug info, run under bash (e.g. with --bash)' enter_debug_shell return fi echo "Last command: $BASH_COMMAND" test -z "$debug_variables" || ( set -o posix; set; ) backtrace enter_debug_shell } trap 'bailout' EXIT if test -n "$BASH" then # bash doesn't set BASH_LINENO correctly for EXIT, so use ERR set -E # shellcheck disable=SC2039 trap 'bailout' ERR trap 'echo Bug: irregular exit' EXIT fi umask 022 enter_debug_shell() { test -n "$debug_shell" || return echo 'Entering debug shell!' # If run e.g. from vim's :make, it tries to steal our stdio exec "$debug_shell" <> /dev/tty 1>&0 2>&0 } # shellcheck disable=SC2039 backtrace() { local skip_head=0 skip_tail=1 echo Backtrace: for i in $(eval "echo {${skip_head}..$((${#BASH_LINENO[@]}-skip_tail-1))}") do echo "${BASH_SOURCE[i]}:${BASH_LINENO[i]}: error: ... from ${FUNCNAME[i+1]}" done } dispatch() { phase=phase1 suite= target= mirror= script= : $suite $target $mirror $script local arg for arg do case "$arg" in -h | --help) usage ;; --bash) if test -z "$BASH" then echo 'Restarting under bash ...' exec bash "$0" "$@" fi ;; --debug-shell) debug_shell=bash ;; --debug-variables) debug_variables=nz ;; --phase1) phase=phase1 ;; --phase2) phase=phase2 ;; -*) die "Unrecognized argument $arg" ;; '') die "Empty argument!" ;; *) posarg "$arg" suite target mirror script ;; esac done $phase "$@" die "Phase returned unexpectedly!" } debug_var() { local arg val for arg do val="$(indirect_get "$arg")" echo "$arg=$val" done } check_var() { # Verify that something looks like a variable name. # $1 - variable name case "$1" in *[^A-Za-z_0-9]*) die "Invalid variable name: $1" ;; esac } indirect_get() { # Get a variable's value, with indirection. # $1 - variable name check_var "$1" eval echo '$'"$1" } indirect_set() { # Set a variable's value, with indirection. # $1 - variable name # $2 - value check_var "$1" eval "$1"='$''2' } posarg() { local value="$1" var zval shift for var do zval="$(indirect_get $var)" if test -z "$zval" then indirect_set $var "$value" return fi done die 'Too many positional arguments!' } echo() { printf '%s\n' "$*" } echon() { printf '%s' "$*" } echoe() { printf '%b\n' "$*" } echoen() { printf '%b' "$*" } exit_() { ( exit "$1" ) echo 'set -e failed???' exit "$1" } die() { echo "$@" >&2 exit_ 1 } phase1() { # Phase 1 is executed directly in the host environment. echo 'Beginning phase 1' host=$(basename "$target") host=$(echo "$host" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g;s/^-*//;s/-*$//') test -n "$host" || die "Unable to make hostname from $target" # avoid reading /etc/subuid ourselves probe=$(lxc-usernsexec mktemp /tmp/userns-debootstrap.XXXXXX) inner_uid=$(stat -c %u "$probe") inner_gid=$(stat -c %g "$probe") lxc-usernsexec rm "$probe" outer_uid=$(id -u) outer_gid=$(id -g) mkdir -p "$target" lxc-usernsexec -m "u:0:$outer_uid:1" -m "g:0:$outer_gid:1" -m "u:1:$inner_uid:1" -m "g:1:$inner_gid:1" -- chown 1:1 "$target" exec lxc-usernsexec -- lxc-unshare -s 'MOUNT|PID|UTSNAME|IPC' -H "$host" -- env -i TERM="$TERM" "$0" "$@" --phase2 } make_dir_of() { local dir; dir="$(dirname "$1")" mkdir -p "$dir" } mount_bind_file() { make_dir_of "$1" touch "$1" # Just in case someone chroots without binding, # make sure they don't e.g. fill up /dev/null chmod -rwx "$1" mount -o bind /"$1" "$1" } mount_bind_dir() { mkdir -p "$1" # Recursive bind is needed e.g. for /sys/ # Otherwise you get nonsensical error messages. mount -o rbind /"$1" "$1" } just_copy() { make_dir_of "$1" cp /"$1" "$1" } create_fakebin() { mkdir -p fakebin # This is used both inside and outside the chroot PATH="$(pwd)/fakebin:/fakebin:$PATH"; export PATH ln -sf /bin/true fakebin/mknod echo '#!/bin/sh' > fakebin/tar echo 'exec /bin/tar --exclude=sys --exclude=proc "$@"' >> fakebin/tar chmod +x fakebin/tar ln -sf /bin/true fakebin/mount } phase2() { # Phase 2 is executed in the new namespace, but without chroot'ing. echo 'Beginning phase 2' mount -t proc proc /proc # Use apt-cacher-ng to make this not take forever. # (ideally, read this from apt config if present) export http_proxy=http://localhost:3142 # $target is created during phase1 to get the permissions right. cd "$target" mkdir -p "etc/apt/apt.conf.d" echo 'Acquire::http::proxy "'"$http_proxy"'";' > etc/apt/apt.conf.d/02proxy # These are the nodes created by `setup_devices_simple` in debootstrap. mount_bind_file dev/null mount_bind_file dev/zero mount_bind_file dev/full mount_bind_file dev/random mount_bind_file dev/urandom mount_bind_file dev/tty mount_bind_dir sys # must be *before* `create_fakebin` mkdir -p proc mount -t proc proc ./proc just_copy etc/resolv.conf just_copy etc/hostname create_fakebin # A failed debootstrap cannot be rerun unless these are deleted. rm -rf dev/pts dev/shm dev/ptmx dev/fd dev/stdin dev/stdout dev/stderr echo 'Beginning actual debootstrap!' exec debootstrap "$suite" . "$mirror" "$script" --variant=buildd } dispatch "$@"