Message ID | 20250925065433.4180883-2-cedric.hombourger@siemens.com |
---|---|
State | Under Review |
Headers | show |
Series | non-privileged commands in chroot | expand |
Hi Cedric, Am 25.09.25 um 08:54 schrieb 'Cedric Hombourger' via isar-users: > From: "cedric.hombourger@siemens.com" <cedric.hombourger@siemens.com> > > "sudo chroot" is used in several places to run commands inside rootfs > directories constructed by Isar. There are cases where a command could > be used without elevated privileges as long as special folders such as > /isar-apt are mounted (they are often referenced as /isar-apt in > configuration files found in the target rootfs). For such cases, > bubblewrap may be used to create a non-privileged namespace (either > in a bare/native environment or within a docker/podman container) > where the command will be executed as if chroot had been used. The > rootfs may also be the host root file-system: this should however > be used with care to avoid host contamination problems (note: Isar > already relies on a number of host tools). Thank you for the respin! This opens interesting possibilities. I'm currently trying run the build in a container with less than full privileges and it seems that using --cap-add=SYS_ADMIN is more or less is sufficient. Now with your patch applied, I'm unfortunately greeted with | bwrap: pivot_root: Operation not permitted This is caused by the docker (podman on Ubuntu 24.04 doesnt show the issue) default seccomp profile which denies the pivot_root syscall, among others. Of course it could be overcome by adding --security-opt seccomp=unconfined. Or better a more nuanced seccomp profile. However, the ultimate goal of reducing the needed privileges would be to run docker out of the box without the need to extend capabilites or adding exceptions in the default MAC profiles. So, in this regard bwrap, which I had thought to be a key to overcome the bind-mount restriction when switching to rootless containers, adds something to take care of. I dont know if this is something that's even on your roadmap, and for sure it's nothing that should stop this series, I just wanted to share the information. Actually, I just found there's an open issue with even a workaround to avoid pivot_root: https://github.com/containers/bubblewrap/issues/592, so maybe it can be solved easily later. regards, Andreas > > Signed-off-by: Cedric Hombourger <cedric.hombourger@siemens.com> > --- > RECIPE-API-CHANGELOG.md | 8 +++++ > doc/user_manual.md | 1 + > meta/classes/rootfs.bbclass | 67 +++++++++++++++++++++++++++++++++++++ > 3 files changed, 76 insertions(+) > > diff --git a/RECIPE-API-CHANGELOG.md b/RECIPE-API-CHANGELOG.md > index 92e7811c..53e650d4 100644 > --- a/RECIPE-API-CHANGELOG.md > +++ b/RECIPE-API-CHANGELOG.md > @@ -741,3 +741,11 @@ By setting `MS_TPM_20_REF_DIR` in an optee-ftpm recipe, it is now possible to > use the new optee_ftpm code base from the OP-TEE project. That variable has to > point to a subdir in `WORKDIR` which contains the unpacked ms-tpm-20-ref source > code. > + > +### Require bubblewrap to run non-privileged commands with bind-mounts > + > +Isar occasionally needs to run commands within root file-systems that it > +builds and with several bind-mounts (e.g. /isar-apt). bubblewrap may be > +used in Isar classes instead of `sudo chroot` to avoid unecessary privilege > +elevations (when we "just" need to chroot but do not require root). It is > +pre-installed in kas-container version 4.8 (or later). > diff --git a/doc/user_manual.md b/doc/user_manual.md > index 67f91973..be89ce1d 100644 > --- a/doc/user_manual.md > +++ b/doc/user_manual.md > @@ -75,6 +75,7 @@ Install the following packages: > ``` > apt install \ > binfmt-support \ > + bubblewrap \ > bzip2 \ > mmdebstrap \ > arch-test \ > diff --git a/meta/classes/rootfs.bbclass b/meta/classes/rootfs.bbclass > index ebe3bf4a..f740c6e1 100644 > --- a/meta/classes/rootfs.bbclass > +++ b/meta/classes/rootfs.bbclass > @@ -34,6 +34,73 @@ export LANG = "C" > export LANGUAGE = "C" > export LC_ALL = "C" > > +# Execute a command against a rootfs and with isar-apt bind-mounted. > +# Additional mounts may be specified using --bind <source> <target> and a > +# custom directory for the command to be executed with --chdir <dir>. The > +# command is assumed to follow the special "--" argument. This would replace > +# "sudo chroot" calls especially when a native command may be used instead of > +# chroot'ed command and without elevated privileges (the command will likely > +# take the rootfs as argument; e.g. apt-get -o Dir=${ROOTFSDIR}). If the > +# optional rootfs argument is omitted, the host rootfs will be used (e.g. to > +# run native commands): this should be used with care. > +# > +# Usage: rootfs_cmd [options] [rootfs] -- command > +# > +rootfs_cmd() { > + set -- "$@" > + bwrap_args="--bind ${REPO_ISAR_DIR}/${DISTRO} /isar-apt" > + bwrap_binds="" > + bwrap_rootfs="" > + > + while [ "${#}" -gt "0" ] && [ "${1}" != "--" ]; do > + case "${1}" in > + --bind) > + if [ "${#}" -lt "3" ]; then > + bbfatal "--bind requires two arguments" > + fi > + bwrap_binds="${bwrap_binds} --bind ${2} ${3}" > + shift 3 > + ;; > + --chdir) > + if [ "${#}" -lt "2" ]; then > + bbfatal "${1} requires an argument" > + fi > + bwrap_args="${bwrap_args} ${1} ${2}" > + shift 2 > + ;; > + -*) > + bbfatal "${1} is not a supported option!" > + ;; > + *) > + if [ -z "${bwrap_rootfs}" ]; then > + bwrap_rootfs="${1}" > + shift > + else > + bbfatal "unexpected argument '${1}'" > + fi > + ;; > + esac > + done > + > + if [ -n "${bwrap_rootfs}" ]; then > + bwrap_args="${bwrap_args} --bind ${bwrap_rootfs} /" > + fi > + > + if [ "${#}" -le "1" ] || [ "${1}" != "--" ]; then > + bbfatal "no command specified (missing --)" > + fi > + shift # remove "--", command and its arguments follows > + > + for ro_d in bin etc lib lib64 sys usr var; do > + [ -d ${bwrap_rootfs}/${ro_d} ] || continue > + bwrap_args="${bwrap_args} --ro-bind ${bwrap_rootfs}/${ro_d} /${ro_d}" > + done > + > + bwrap --unshare-user --unshare-pid ${bwrap_args} \ > + --dev-bind /dev /dev --proc /proc --tmpfs /tmp \ > + ${bwrap_binds} -- "${@}" > +} > + > rootfs_do_mounts[weight] = "3" > rootfs_do_mounts() { > sudo -s <<'EOSUDO'
diff --git a/RECIPE-API-CHANGELOG.md b/RECIPE-API-CHANGELOG.md index 92e7811c..53e650d4 100644 --- a/RECIPE-API-CHANGELOG.md +++ b/RECIPE-API-CHANGELOG.md @@ -741,3 +741,11 @@ By setting `MS_TPM_20_REF_DIR` in an optee-ftpm recipe, it is now possible to use the new optee_ftpm code base from the OP-TEE project. That variable has to point to a subdir in `WORKDIR` which contains the unpacked ms-tpm-20-ref source code. + +### Require bubblewrap to run non-privileged commands with bind-mounts + +Isar occasionally needs to run commands within root file-systems that it +builds and with several bind-mounts (e.g. /isar-apt). bubblewrap may be +used in Isar classes instead of `sudo chroot` to avoid unecessary privilege +elevations (when we "just" need to chroot but do not require root). It is +pre-installed in kas-container version 4.8 (or later). diff --git a/doc/user_manual.md b/doc/user_manual.md index 67f91973..be89ce1d 100644 --- a/doc/user_manual.md +++ b/doc/user_manual.md @@ -75,6 +75,7 @@ Install the following packages: ``` apt install \ binfmt-support \ + bubblewrap \ bzip2 \ mmdebstrap \ arch-test \ diff --git a/meta/classes/rootfs.bbclass b/meta/classes/rootfs.bbclass index ebe3bf4a..f740c6e1 100644 --- a/meta/classes/rootfs.bbclass +++ b/meta/classes/rootfs.bbclass @@ -34,6 +34,73 @@ export LANG = "C" export LANGUAGE = "C" export LC_ALL = "C" +# Execute a command against a rootfs and with isar-apt bind-mounted. +# Additional mounts may be specified using --bind <source> <target> and a +# custom directory for the command to be executed with --chdir <dir>. The +# command is assumed to follow the special "--" argument. This would replace +# "sudo chroot" calls especially when a native command may be used instead of +# chroot'ed command and without elevated privileges (the command will likely +# take the rootfs as argument; e.g. apt-get -o Dir=${ROOTFSDIR}). If the +# optional rootfs argument is omitted, the host rootfs will be used (e.g. to +# run native commands): this should be used with care. +# +# Usage: rootfs_cmd [options] [rootfs] -- command +# +rootfs_cmd() { + set -- "$@" + bwrap_args="--bind ${REPO_ISAR_DIR}/${DISTRO} /isar-apt" + bwrap_binds="" + bwrap_rootfs="" + + while [ "${#}" -gt "0" ] && [ "${1}" != "--" ]; do + case "${1}" in + --bind) + if [ "${#}" -lt "3" ]; then + bbfatal "--bind requires two arguments" + fi + bwrap_binds="${bwrap_binds} --bind ${2} ${3}" + shift 3 + ;; + --chdir) + if [ "${#}" -lt "2" ]; then + bbfatal "${1} requires an argument" + fi + bwrap_args="${bwrap_args} ${1} ${2}" + shift 2 + ;; + -*) + bbfatal "${1} is not a supported option!" + ;; + *) + if [ -z "${bwrap_rootfs}" ]; then + bwrap_rootfs="${1}" + shift + else + bbfatal "unexpected argument '${1}'" + fi + ;; + esac + done + + if [ -n "${bwrap_rootfs}" ]; then + bwrap_args="${bwrap_args} --bind ${bwrap_rootfs} /" + fi + + if [ "${#}" -le "1" ] || [ "${1}" != "--" ]; then + bbfatal "no command specified (missing --)" + fi + shift # remove "--", command and its arguments follows + + for ro_d in bin etc lib lib64 sys usr var; do + [ -d ${bwrap_rootfs}/${ro_d} ] || continue + bwrap_args="${bwrap_args} --ro-bind ${bwrap_rootfs}/${ro_d} /${ro_d}" + done + + bwrap --unshare-user --unshare-pid ${bwrap_args} \ + --dev-bind /dev /dev --proc /proc --tmpfs /tmp \ + ${bwrap_binds} -- "${@}" +} + rootfs_do_mounts[weight] = "3" rootfs_do_mounts() { sudo -s <<'EOSUDO'