From ddd31be5a77138707656e865cf751f8857100cb2 Mon Sep 17 00:00:00 2001 From: Felix Moessbauer Date: Sun, 12 Jan 2025 11:09:55 +0100 Subject: [PATCH] kas-container: add limited support for docker rootless Docker rootless mode is similar to podman rootless mode, except that it does not support to share the userid namespace. By that, the bind mounted directories (like /repo, /work, /build) which are owned by the calling user, are mapped with uid==gid==0 inside the container. While we could align this by running as root inside the container, this is not an option as bitbake does not allow this. This comes with the following limitations: - /repo must be mounted ro to not destroy the uid mappings on the host - /work, /build must be an exclusive dir on the host that is only written to by the tooling inside kas-container - a git safe.dirs exception is needed as git operates as builder on repos owned by root - only operations that do not strictly require /repo:rw are supported. - ISAR mode is not supported in rootless mode, fallback to system docker Closes: #124 Signed-off-by: Felix Moessbauer --- container-entrypoint | 41 +++++++++++++++++++- docs/userguide/kas-container-description.inc | 11 ++++++ kas-container | 34 ++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/container-entrypoint b/container-entrypoint index ab81b5ec..33273e56 100755 --- a/container-entrypoint +++ b/container-entrypoint @@ -35,13 +35,35 @@ may also need to update the host distribution (e.g. Debian Jessie -> Stretch). EOF fi +git_disable_safe_dir() +{ + # allow git to operate on non-owned directories + sudo git config --system safe.directory "*" +} + +chown_managed_dirs() +{ + for DIR in /build /work /sstate /downloads /repo-ref; do + if [ -d "$DIR" ]; then + chown -R "$1":"$2" "$DIR" + fi + done +} + +restore_managed_dirs_owner() +{ + if [ "$KAS_DOCKER_ROOTLESS" = "1" ]; then + chown_managed_dirs 0 0 + fi +} + if [ -z "$USER_ID" ]; then # Not a kas-container call GOSU="" # Work around gitlab-runner not aligning checked out repo ownership # with our builder user - sudo git config --system safe.directory "*" + git_disable_safe_dir elif [ "$USER_ID" = 0 ]; then # We shall run everything as root GOSU="" @@ -58,6 +80,15 @@ else GOSU="gosu builder" fi +# kas-container on rootless docker workaround +if [ -n "$USER_ID" ] && [ "$USER_ID" -ne 0 ] && \ + [ "$KAS_DOCKER_ROOTLESS" = "1" ] && [ "$(stat -c %u /repo)" -eq 0 ]; then + # Docker rootless does not support keeping the user namespace + # (podman option --userns=keep-id). By that, the bind mounts + # are owned by root. + git_disable_safe_dir + chown_managed_dirs "$USER_ID" "$GROUP_ID" +fi if [ "$PWD" = / ]; then cd /builder || exit 1 @@ -65,11 +96,17 @@ fi if [ -n "$1" ]; then case "$1" in - build|checkout|clean*|dump|for-all-repos|lock|menu|purge|shell|-*) + build|checkout|clean*|dump|for-all-repos|lock|menu|shell|-*) # SC2086: Double quote to prevent globbing and word splitting. # shellcheck disable=2086 exec $GOSU kas "$@" ;; + purge) + trap restore_managed_dirs_owner EXIT INT TERM + # SC2086: Double quote to prevent globbing and word splitting. + # shellcheck disable=2086 + $GOSU kas "$@" + ;; *) # SC2086: Double quote to prevent globbing and word splitting. # shellcheck disable=2086 diff --git a/docs/userguide/kas-container-description.inc b/docs/userguide/kas-container-description.inc index ac8397a9..b70e9c5d 100644 --- a/docs/userguide/kas-container-description.inc +++ b/docs/userguide/kas-container-description.inc @@ -14,3 +14,14 @@ By default ``kas-container`` uses the official images provided by the kas projec ``KAS_CONTAINER_IMAGE`` environment variable. As container backends, Docker and Podman are supported. To force the use of podman over docker, set ``KAS_CONTAINER_ENGINE=podman``. For details, see :ref:`env-vars-label`. + +Running under docker in `rootless mode `_ +is partially supported and requires a distinct ``KAS_WORK_DIR`` outside of the +calling directory (repo-dir). The ``KAS_WORK_DIR`` is exclusively managed by the +tooling inside the container and must not be written to from the host. To completely +remove all data managed by kas, use ``kas-container purge``. This also restores the +directory owners of the dirs passed to kas, so they can be removed from the host. + +.. note:: + The ISAR build system is not compatible with rootless execution. By that, + we fall back to the system docker or podman instance. diff --git a/kas-container b/kas-container index 93d8f634..a8e9f9cf 100755 --- a/kas-container +++ b/kas-container @@ -130,6 +130,12 @@ enable_isar_mode() KAS_CONTAINER_COMMAND="sudo --preserve-env ${KAS_CONTAINER_COMMAND}" # preserved user PATH may lack sbin needed by privileged podman export PATH="${PATH}:/usr/sbin" + elif [ "${KAS_DOCKER_ROOTLESS}" = "1" ]; then + export DOCKER_HOST="${DOCKER_HOST:-unix:///var/run/docker.sock}" + debug "kas-isar does not support rootless docker. Using system docker" + # force use of well-known system docker socket + KAS_CONTAINER_COMMAND="sudo --preserve-env ${KAS_CONTAINER_COMMAND}" + KAS_DOCKER_ROOTLESS=0 fi } @@ -180,6 +186,26 @@ forward_dir_if_set() fi } +check_docker_rootless() +{ + KAS_DOCKER_ROOTLESS=0 + if [ "$(docker context show)" = "rootless" ]; then + KAS_DOCKER_ROOTLESS=1 + fi +} + +enable_docker_rootless() +{ + warning "Rootless docker used, only limited functionality available." + if [ "${KAS_WORK_DIR}" = "${KAS_REPO_DIR}" ]; then + fatal_error "Docker rootless requires an exclusive KAS_WORK_DIR." + fi + if [ "${KAS_REPO_MOUNT_OPT}" = "rw" ]; then + fatal_error "Docker rootless requires read-only repo." + fi + KAS_RUNTIME_ARGS="${KAS_RUNTIME_ARGS} -e KAS_DOCKER_ROOTLESS=1" +} + KAS_GIT_OVERLAY_FILE="" kas_container_cleanup() { @@ -237,6 +263,7 @@ case "${KAS_CONTAINER_ENGINE}" in docker) KAS_CONTAINER_COMMAND="docker" enable_unpriv_userns_docker + check_docker_rootless ;; podman) KAS_CONTAINER_COMMAND="podman" @@ -480,6 +507,9 @@ fi set_container_image_var +if [ "${KAS_DOCKER_ROOTLESS}" = "1" ]; then + KAS_REPO_MOUNT_OPT_DEFAULT="ro" +fi KAS_REPO_MOUNT_OPT="${KAS_REPO_MOUNT_OPT:-${KAS_REPO_MOUNT_OPT_DEFAULT}}" KAS_FILES="$(echo "${KAS_FILES}" | sed 's|'"${KAS_REPO_DIR}"'/|/repo/|g')" @@ -490,6 +520,10 @@ if [ "$(id -u)" -eq 0 ] && [ "${KAS_ALLOW_ROOT}" != "yes" ] ; then "KAS_ALLOW_ROOT=yes to override." fi +if [ "${KAS_DOCKER_ROOTLESS}" = "1" ]; then + enable_docker_rootless +fi + set -- "$@" -v "${KAS_REPO_DIR}:/repo:${KAS_REPO_MOUNT_OPT}" \ -v "${KAS_WORK_DIR}":/work:rw -e KAS_WORK_DIR=/work \ --workdir=/repo \