From cdb2fc633d9eee07dd42903e8ecce2bc411d2fc6 Mon Sep 17 00:00:00 2001 From: librelad Date: Sun, 24 May 2026 22:53:11 +0100 Subject: [PATCH] fix(install): establish container layer in root phase (real fix for scan noise) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts the 2>/dev/null band-aids and fixes the root cause. The manager-run install boot scans app configs under /docker/containers AS the container user (runFileOp). But init.sh's initFolders creates that dir manager-owned, and the handover to the container user happened later (start_preinstall), AFTER the boot scans — so the scans ran as the container user against a dir it didn't own yet: "find: '/docker/containers/': Permission denied" (cosmetic; the dir is empty that early, but it's the wrong ownership at the wrong time). Add initContainerLayer() to init.sh's root phase (after initGIT + initUpdateConfigs, before the manager-run handoff): rootless-only, it creates the docker-install user if missing and chowns /docker/containers to it (751). The later rootless setup is now idempotent — it finds the user existing and just (re)asserts its password + daemon config (moved updateDockerInstallPassword out of the create-only branch). Rooted is unaffected (containers stay manager-owned, which the manager reads). Result: by the time the boot scans run, /docker/containers is owned by the user doing the scanning — no permission error, nothing suppressed. Co-Authored-By: Claude Opus 4.7 Signed-off-by: librelad --- init.sh | 40 +++++++++++++++++++ .../application_missing_variables.sh | 5 +-- .../docker/install/rootless/rootless_user.sh | 5 ++- scripts/source/loading/scan_files.sh | 7 +--- 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/init.sh b/init.sh index bb32576..794b9a5 100755 --- a/init.sh +++ b/init.sh @@ -830,6 +830,45 @@ initFolders() isSuccessful "All folders have been created." } +# Establish the rootless container layer — the docker-install user and ownership +# of /docker/containers — during the ROOT phase, BEFORE the manager-run install +# boots. That boot scans app configs under /docker/containers AS the container +# user (runFileOp), so if the dir is still manager-owned (as initFolders leaves +# it) the scan errors with "Permission denied". Handing the dir to the container +# user here means the scan reads a dir it owns. Rootless-only — rooted keeps +# containers manager/root-owned, which the manager reads fine. Idempotent: the +# later rootless setup finds the user existing and just (re)asserts its password +# + daemon config. Runs after initGIT (config present) + initFolders (dir present). +initContainerLayer() +{ + local cfg="$configs_dir/general/general_docker_install" + local dtype duser + dtype=$(grep -h '^CFG_DOCKER_INSTALL_TYPE=' "$cfg" 2>/dev/null | head -1 | cut -d= -f2 | awk '{print $1}') + [[ "$dtype" != "rootless" ]] && return 0 + duser=$(grep -h '^CFG_DOCKER_INSTALL_USER=' "$cfg" 2>/dev/null | head -1 | cut -d= -f2 | awk '{print $1}') + duser="${duser:-dockerinstall}" + + isHeader "Container Layer Setup" + if id "$duser" &>/dev/null; then + isSuccessful "Container user '$duser' already exists." + else + # -m + the system login.defs SUB_UID/GID defaults assign its subordinate + # uid/gid ranges (needed for rootless). The later rootless setup sees it + # existing and configures the daemon/linger/password. + sudo useradd -m -s /bin/bash -d "/home/$duser" "$duser" 2>/dev/null + isSuccessful "Created container user '$duser'." + fi + + # Hand containers/ to the container user (it owns per-app data in rootless) so + # the manager-run startup config scans can read it. 751: owner full; the + # manager (other) can traverse in to known paths (it lists/writes via runFileOp). + if [[ -d "$containers_dir" ]]; then + sudo chown "$duser:$duser" "$containers_dir" + sudo chmod 751 "$containers_dir" + isSuccessful "containers/ handed to '$duser'." + fi +} + setupConfigsFromRepo() { isNotice "Setting up configuration files from repository..." @@ -1468,6 +1507,7 @@ else initRootHelpers initLibrePortalCommand initUpdateConfigs + initContainerLayer completeInitMessage elif [[ "$param1" == "uninstall" ]]; then runFullUninstall diff --git a/scripts/config/application/application_missing_variables.sh b/scripts/config/application/application_missing_variables.sh index e3d3ba1..e5598ac 100755 --- a/scripts/config/application/application_missing_variables.sh +++ b/scripts/config/application/application_missing_variables.sh @@ -9,10 +9,7 @@ checkApplicationsConfigFilesMissingVariables() app=$(basename "$live" .config) remote="$install_containers_dir$app/$app.config" reconcileConfigFile "$live" "$remote" - # 2>/dev/null: before the docker-type config loads (early install) runFileOp - # falls back to the manager, which can't list the container-owned containers/ - # dir — harmless "Permission denied" on a best-effort reconcile (no apps yet). - done < <(runFileOp find "$containers_dir" -maxdepth 2 -type f -name '*.config' ! -name '*.bak' 2>/dev/null) + done < <(runFileOp find "$containers_dir" -maxdepth 2 -type f -name '*.config' ! -name '*.bak') isSuccessful "Application config reconciliation completed." } diff --git a/scripts/docker/install/rootless/rootless_user.sh b/scripts/docker/install/rootless/rootless_user.sh index 7f46a32..61f0f1d 100755 --- a/scripts/docker/install/rootless/rootless_user.sh +++ b/scripts/docker/install/rootless/rootless_user.sh @@ -14,7 +14,10 @@ installDockerRootlessUser() # rootless). Run unmasked so checkSuccess sees real failures. runSystem useradd -m -s /bin/bash -d "/home/$CFG_DOCKER_INSTALL_USER" "$CFG_DOCKER_INSTALL_USER" checkSuccess "Creating $CFG_DOCKER_INSTALL_USER User." - updateDockerInstallPassword; fi + # (Re)assert the password regardless — the user may have been pre-created + # in init.sh's root phase (so /docker/containers ownership is ready before + # the manager-run boot scans), where the password isn't set. + updateDockerInstallPassword; fi } diff --git a/scripts/source/loading/scan_files.sh b/scripts/source/loading/scan_files.sh index cc77c9a..9643c24 100755 --- a/scripts/source/loading/scan_files.sh +++ b/scripts/source/loading/scan_files.sh @@ -61,12 +61,7 @@ sourceScanFiles() source "$file" # echo "$load_type FILE $file" fi - # 2>/dev/null: early in an install the docker-type config isn't loaded - # yet, so runFileOp falls back to the manager, which can't list the - # container-owned containers/ dir — a harmless "Permission denied" on a - # best-effort scan (no app configs exist yet). Suppress that noise; the - # -print0 output still flows. - done < <($scan_op find "$folder_dir" -maxdepth 3 -type d \( -name 'resources' \) -prune -o -type f -name "$file_pattern" -print0 2>/dev/null) + done < <($scan_op find "$folder_dir" -maxdepth 3 -type d \( -name 'resources' \) -prune -o -type f -name "$file_pattern" -print0) fi # Load the categories from the file into an array