From 1b8461796a41f000f3a062974fce8cfbae594350 Mon Sep 17 00:00:00 2001 From: Valentin David Date: Thu, 16 Jun 2022 13:21:47 +0200 Subject: [PATCH] Use mount namespace instead of chroot This allow to not worry about mounts done within the namespace. We can now bind mount files into the sysroot instead of copying them. --- Makefile | 14 +--- hooks/001-extra-packages.chroot | 4 -- hooks/999-clean-resolv-conf.chroot | 5 -- mount-ns.sh | 105 +++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 20 deletions(-) delete mode 100755 hooks/999-clean-resolv-conf.chroot create mode 100755 mount-ns.sh diff --git a/Makefile b/Makefile index 3b24564d..97a7d644 100644 --- a/Makefile +++ b/Makefile @@ -14,21 +14,12 @@ install: fi rm -rf $(DESTDIR) cp -aT $(CRAFT_STAGE)/base $(DESTDIR) - # ensure resolving works inside the chroot - cat /etc/resolv.conf > $(DESTDIR)/etc/resolv.conf # copy-in launchpad's build archive if grep -q ftpmaster.internal /etc/apt/sources.list; then \ cp /etc/apt/sources.list $(DESTDIR)/etc/apt/sources.list; \ cp /etc/apt/trusted.gpg $(DESTDIR)/etc/apt/ || true; \ cp -r /etc/apt/trusted.gpg.d $(DESTDIR)/etc/apt/ || true; \ fi - # since recently we're also missing some /dev files that might be - # useful during build - make sure they're there - [ -e $(DESTDIR)/dev/null ] || mknod -m 666 $(DESTDIR)/dev/null c 1 3 - [ -e $(DESTDIR)/dev/zero ] || mknod -m 666 $(DESTDIR)/dev/zero c 1 5 - [ -e $(DESTDIR)/dev/random ] || mknod -m 666 $(DESTDIR)/dev/random c 1 8 - [ -e $(DESTDIR)/dev/urandom ] || \ - mknod -m 666 $(DESTDIR)/dev/urandom c 1 9 # copy static files verbatim /bin/cp -a static/* $(DESTDIR) mkdir -p $(DESTDIR)/install-data @@ -36,8 +27,9 @@ install: # customize set -eux; for f in ./hooks/[0-9]*.chroot; do \ base="$$(basename "$${f}")"; \ - cp -a "$${f}" $(DESTDIR)/install-data/; \ - chroot $(DESTDIR) "/install-data/$${base}"; \ + ./mount-ns.sh spawn $(DESTDIR) \ + --ro-bind $$f "/install-data/$${base}" \ + -- "/install-data/$${base}"; \ rm "$(DESTDIR)/install-data/$${base}"; \ done rm -rf $(DESTDIR)/install-data diff --git a/hooks/001-extra-packages.chroot b/hooks/001-extra-packages.chroot index 0a8ed86a..975b3c68 100755 --- a/hooks/001-extra-packages.chroot +++ b/hooks/001-extra-packages.chroot @@ -12,10 +12,6 @@ export DEBIAN_FRONTEND=noninteractive rm -f /etc/apt/sources.list.d/proposed.list -# ensure we have /proc or systemd will fail -mount -t proc proc /proc -trap 'umount /proc' EXIT - # systemd postinst needs this mkdir -p /var/log/journal diff --git a/hooks/999-clean-resolv-conf.chroot b/hooks/999-clean-resolv-conf.chroot deleted file mode 100755 index b0e0cdc1..00000000 --- a/hooks/999-clean-resolv-conf.chroot +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -set -e - -echo "" > /etc/resolv.conf diff --git a/mount-ns.sh b/mount-ns.sh new file mode 100755 index 00000000..c820f384 --- /dev/null +++ b/mount-ns.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +set -eu + +# FIXME: This should be replace by a mount of devtmpfs when +# it is supported in user namespaces +bind_dev() { + dev="${1}/dev" + + for device in null zero full random urandom tty; do + touch "${dev}/${device}" + mount --bind "/dev/${device}" "${dev}/${device}" + done +} + +# Because systemd debian package post-install script is not very +# robust, we have to create a symlink and bind mount resolv.conf +# there. +bind_resolv() { + sysroot="${1}" + + resolv_real_path="${sysroot}/run/fake-resolv.conf" + create_symlink=yes + if [ -L "${sysroot}/etc/resolv.conf" ]; then + resolv="$(readlink "${sysroot}/etc/resolv.conf")" + if [ "${resolv}" = "../run/systemd/resolve/stub-resolv.conf" ]; then + resolv_real_path="${sysroot}/run/systemd/resolve/stub-resolv.conf" + create_symlink=no + fi + fi + mkdir -p "$(dirname "${resolv_real_path}")" + touch "${resolv_real_path}" + mount --bind -o ro /etc/resolv.conf "${resolv_real_path}" + if [ "${create_symlink}" = yes ]; then + ln -srf "${resolv_real_path}" "${sysroot}/etc/resolv.conf" + fi +} + +if [ $# -lt 3 ]; then + echo "Expected at least 3 arguments" 1>&2 + exit 1 +fi + +command="${1}" +sysroot="${2}" +shift 2 + +case "${command}" in + spawn) + cleanup() { + umount "${sysroot}/dev" || true + umount "${sysroot}/tmp" || true + umount "${sysroot}/run" || true + } + mount -t tmpfs -o mode=0755 tmpfs "${sysroot}/dev" + mount -t tmpfs -o mode=1777 tmpfs "${sysroot}/tmp" + mount -t tmpfs -o mode=0755 tmpfs "${sysroot}/run" + trap cleanup EXIT + unshare --pid --fork --mount -- "${0}" init "${sysroot}" "${@}" + ;; + init) + mount -t proc proc "${sysroot}/proc" + bind_dev "${sysroot}" + bind_resolv "${sysroot}" + while [ $# -gt 1 ]; do + case "${1}" in + --) + shift + break + ;; + --bind|--ro-bind) + if [ -d "$2" ]; then + if ! [ -d "${sysroot}/$3" ]; then + mkdir -p "${sysroot}/$3" + fi + else + if ! [ -e "${sysroot}/$3" ]; then + dir="$(dirname "${sysroot}/$3")" + if ! [ -d "${dir}" ]; then + mkdir -p "${dir}" + fi + touch "${sysroot}/$3" + fi + fi + extra_args=() + case "$1" in + --ro-bind) + extra_args=("-o" "ro") + ;; + esac + mount --bind "${extra_args[@]}" "$2" "${sysroot}/$3" + shift 3 + ;; + *) + break + ;; + esac + done + exec unshare --mount --root="${sysroot}" -- "${@}" + ;; + *) + echo "Unknown command" 1>&2 + exit 1 + ;; +esac