From cb6db0b8064f50f83796384d39ee8d3a0ea736ef 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 shall be exclusive dirs 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 | 44 ++++++++++++++++++-- docs/userguide/kas-container-description.inc | 12 ++++++ kas-container | 35 ++++++++++++++++ 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/container-entrypoint b/container-entrypoint index ab81b5ec..7fc68d8d 100755 --- a/container-entrypoint +++ b/container-entrypoint @@ -35,13 +35,33 @@ 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 "$1":"$2" "$DIR" + fi + done +} + +restore_managed_dirs_owner() +{ + chown_managed_dirs 0 0 +} + 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 +78,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 @@ -66,9 +95,16 @@ fi if [ -n "$1" ]; then case "$1" in build|checkout|clean*|dump|for-all-repos|lock|menu|purge|shell|-*) - # SC2086: Double quote to prevent globbing and word splitting. - # shellcheck disable=2086 - exec $GOSU kas "$@" + # We must restore the dir owner after every kas invocation. + # This is cheap as only the top-level dirs are changed (non recursive). + if [ "$KAS_DOCKER_ROOTLESS" = "1" ]; then + trap restore_managed_dirs_owner EXIT INT TERM + $GOSU kas "$@" + else + # SC2086: Double quote to prevent globbing and word splitting. + # shellcheck disable=2086 + exec $GOSU kas "$@" + fi ;; *) # SC2086: Double quote to prevent globbing and word splitting. diff --git a/docs/userguide/kas-container-description.inc b/docs/userguide/kas-container-description.inc index ac8397a9..ad8e5030 100644 --- a/docs/userguide/kas-container-description.inc +++ b/docs/userguide/kas-container-description.inc @@ -14,3 +14,15 @@ 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. It is recommended to use a distinct ``KAS_WORK_DIR`` outside of the +calling directory (repo-dir), as kas temporarily changes the ownership of the working +directory during its operation. All files managed by kas (including the repos) 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 2d724bd5..5a732ba1 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 } @@ -214,6 +220,27 @@ forward_dir() 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 + warning "On docker rootless a exclusive KAS_WORK_DIR should be used" \ + "as kas temporarily changes the ownership of this directory." + 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() { @@ -275,6 +302,7 @@ case "${KAS_CONTAINER_ENGINE}" in docker) KAS_CONTAINER_COMMAND="docker" enable_unpriv_userns_docker + check_docker_rootless ;; podman) KAS_CONTAINER_COMMAND="podman" @@ -518,6 +546,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')" @@ -528,6 +559,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 \