From 438959ca03527a6a1775f7c95a8f2e3e1ddce2d2 Mon Sep 17 00:00:00 2001 From: Samuel Aubertin Date: Tue, 10 Mar 2026 01:22:58 +0100 Subject: [PATCH] Try to inject the parent image in slopslopstrap --- AGENTS.md | 3 + sloptrap | 288 +++++++++++++++++++++++++++++++++++++++++++-- tests/run_tests.sh | 149 ++++++++++++++++++++++- 3 files changed, 428 insertions(+), 12 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6dca8a8..68466b3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,5 +35,8 @@ Do not remove existing instructions unless they are outdated or wrong. - Forcing `SLOPTRAP_CONTAINER_ENGINE=sloppodman` inside sloptrap also needs `TMPDIR` under `/workspace`; otherwise its build-context path guard rejects the staged Dockerfile under `/tmp` before you reach the real subuid/subgid problem. - If a restarted session still inherits stale read-only `/etc/subuid` and `/etc/subgid` tmpfs mounts, an unprivileged agent cannot repair them in-place (`umount` says `must be superuser to unmount`); both podman and sloppodman stay blocked until the session starts without those mounts. - Outer sloptrap launches no longer need `/etc/subuid` or `/etc/subgid` bind mounts: `nested-podman` now disables read-only rootfs and the container entrypoint synthesizes container-local subid files from `/proc/self/{uid,gid}_map` before dropping privileges. +- Even without stale `/etc/subuid` mounts, recursion still fails if the container-local subid files name `sloptrap` instead of the real dropped user (`sk4nz` here): `podman info --debug` warns `no subuid ranges found for user "sk4nz"` and the first inner build dies in `newuidmap ... write to uid_map failed: Operation not permitted`. +- In this Debian 13 / podman 5.4.2 environment, exporting `_CONTAINERS_USERNS_CONFIGURED=done` for nested podman moves the failure past `newuidmap`, but the next blockers are inside Buildah: with `BUILDAH_ISOLATION=chroot`, recursive builds fail `cannot set --network other than host with --isolation chroot`; without chroot, `podman build` can segfault in `network.defaultNetworkBackend`. +- Recursive preload now has a host-side path: the outer launcher saves `$SLOPTRAP_IMAGE_NAME` into capability state and mounts it into the container as `SLOPTRAP_RECURSIVE_PARENT_IMAGE_ARCHIVE=/codex/capabilities/podman/preload/.tar`, and child `build-if-missing` tries `sloppodman load -i` before any inner build. A pre-existing session must be restarted to test that path because it cannot add the new preload mount/env to itself after startup. - With stale subid mounts gone, recursive `./sloptrap /workspace` can still fail earlier during the inner image build: `crun` tries to open `/proc/sys/net/ipv4/ping_group_range` and gets `Read-only file system` while creating the build container. - In a fragile nested-podman session, `podman system migrate` can make things worse: a state that still answered `podman info` fell back to repeated `newuidmap ... write to uid_map failed: Operation not permitted` failures afterward. diff --git a/sloptrap b/sloptrap index a76e484..4fe0c42 100755 --- a/sloptrap +++ b/sloptrap @@ -245,6 +245,7 @@ CAPABILITY_TRUST_ROOT_HOST="" CAPABILITY_TRUST_FILE_HOST="" CAPABILITY_BUILD_STAMP_HOST="" CAPABILITY_STATE_HOST="" +CAPABILITY_PRELOAD_DIR_HOST="" IGNORE_STUB_BASE="" IGNORE_HELPER_ROOT="" ALLOW_HOST_NETWORK=false @@ -355,6 +356,7 @@ write_embedded_helper() { set -euo pipefail helper_pid="" +helperd_bin=${SLOPTRAP_HELPERD_BIN:-/usr/local/bin/sloptrap-helperd} has_capability() { local needle=$1 @@ -440,12 +442,16 @@ lookup_account_name() { ensure_subid_mappings() { local account_id account_gid account_name="" + local subuid_file subgid_file local range_start="" range_count="" gid_start="" gid_count="" local detected_range="" account_id=${SLOPTRAP_HOST_UID:-$(id -u)} account_gid=${SLOPTRAP_HOST_GID:-$(id -g)} - if ! account_name=$(lookup_account_name "$account_id"); then + account_name=${SLOPTRAP_HOST_USER:-} + subuid_file=${SLOPTRAP_PODMAN_SUBUID_FILE:-/etc/subuid} + subgid_file=${SLOPTRAP_PODMAN_SUBGID_FILE:-/etc/subgid} + if [[ -z $account_name ]] && ! account_name=$(lookup_account_name "$account_id"); then account_name="" fi @@ -463,10 +469,10 @@ ensure_subid_mappings() { fi if [[ -n $range_start && -n $range_count ]]; then - ensure_subid_mapping_file /etc/subuid "$account_name" "$account_id" "$range_start" "$range_count" + ensure_subid_mapping_file "$subuid_file" "$account_name" "$account_id" "$range_start" "$range_count" fi if [[ -n $gid_start && -n $gid_count ]]; then - ensure_subid_mapping_file /etc/subgid "$account_name" "$account_id" "$gid_start" "$gid_count" + ensure_subid_mapping_file "$subgid_file" "$account_name" "$account_id" "$gid_start" "$gid_count" fi } @@ -495,7 +501,7 @@ if [[ $(id -u) -eq 0 ]]; then ensure_subid_mappings fi if [[ -n ${SLOPTRAP_ACTIVE_CAPABILITIES:-} ]]; then - /usr/local/bin/sloptrap-helperd & + "$helperd_bin" & helper_pid=$! fi if [[ -n $target_uid && -n $target_gid ]]; then @@ -1129,7 +1135,7 @@ has_capability() { } if [[ $# -eq 0 ]]; then - printf 'usage: sloppodman ...\n' >&2 + printf 'usage: sloppodman ...\n' >&2 exit 2 fi @@ -1139,7 +1145,7 @@ shift subcommand_prefix=("$subcommand") case "$subcommand" in - pull|build|tag|run|ps|logs|stop|rm|inspect|rmi) + pull|build|tag|load|import|run|ps|logs|stop|rm|inspect|rmi) ;; image) [[ $# -gt 0 ]] || { @@ -1204,6 +1210,7 @@ CONTAINERS_CONF_EOF export CONTAINERS_STORAGE_CONF="$storage_conf" export CONTAINERS_CONF="$containers_conf" export BUILDAH_ISOLATION="${BUILDAH_ISOLATION:-chroot}" +export _CONTAINERS_USERNS_CONFIGURED="${_CONTAINERS_USERNS_CONFIGURED:-done}" detect_subid_range_from_map() { local map_path=$1 @@ -1320,6 +1327,7 @@ exec_podman() { CONTAINERS_STORAGE_CONF="$CONTAINERS_STORAGE_CONF" \ CONTAINERS_CONF="$CONTAINERS_CONF" \ BUILDAH_ISOLATION="$BUILDAH_ISOLATION" \ + _CONTAINERS_USERNS_CONFIGURED="$_CONTAINERS_USERNS_CONFIGURED" \ XDG_RUNTIME_DIR="$runtime_dir" \ SLOPTRAP_PODMAN_ESCALATED=1 \ SLOPTRAP_PODMAN_CALLER_UID="${SLOPTRAP_PODMAN_CALLER_UID:-$(id -u)}" \ @@ -1352,6 +1360,19 @@ validate_workspace_path() { esac } +validate_archive_path() { + local path=$1 + local resolved allowed_archive="" + resolved=$(resolve_inner_path "$path") + if [[ -n ${SLOPTRAP_RECURSIVE_PARENT_IMAGE_ARCHIVE:-} ]]; then + allowed_archive=$(resolve_inner_path "$SLOPTRAP_RECURSIVE_PARENT_IMAGE_ARCHIVE") + if [[ $resolved == "$allowed_archive" ]]; then + return 0 + fi + fi + validate_workspace_path "$path" +} + reject_flag() { local flag=$1 printf 'sloppodman: %s is not permitted\n' "$flag" >&2 @@ -1450,6 +1471,60 @@ if [[ $subcommand == "build" ]]; then fi fi +if [[ $subcommand == "import" ]]; then + args=("$@") + archive_path="" + idx=0 + while (( idx < ${#args[@]} )); do + arg=${args[$idx]} + case "$arg" in + --change|--message) + ((idx+=1)) + (( idx < ${#args[@]} )) || { printf 'sloppodman: %s requires a value\n' "$arg" >&2; exit 2; } + ;; + --change=*|--message=*|-q|--quiet) + ;; + -*) + ;; + *) + archive_path=$arg + break + ;; + esac + ((idx+=1)) + done + [[ -n $archive_path ]] || { printf 'sloppodman: import requires an archive path\n' >&2; exit 2; } + validate_archive_path "$archive_path" +fi + +if [[ $subcommand == "load" ]]; then + args=("$@") + archive_path="" + idx=0 + while (( idx < ${#args[@]} )); do + arg=${args[$idx]} + case "$arg" in + -i|--input) + ((idx+=1)) + (( idx < ${#args[@]} )) || { printf 'sloppodman: %s requires a path\n' "$arg" >&2; exit 2; } + archive_path=${args[$idx]} + ;; + --input=*|-i=*) + archive_path=${arg#*=} + ;; + -*) + ;; + *) + archive_path=$arg + break + ;; + esac + ((idx+=1)) + done + [[ -n $archive_path ]] || { printf 'sloppodman: load requires an archive path\n' >&2; exit 2; } + validate_archive_path "$archive_path" +fi + if [[ $subcommand == "run" ]]; then args=("$@") idx=0 @@ -1684,6 +1759,7 @@ select_capability_state_paths() { CAPABILITY_TRUST_FILE_HOST="$CAPABILITY_TRUST_ROOT_HOST/$CODEX_STATE_KEY.trust" CAPABILITY_BUILD_STAMP_HOST="$capability_root/builds/$CODEX_STATE_KEY.stamp" CAPABILITY_STATE_HOST="$CODEX_STATE_HOME_HOST/capabilities" + CAPABILITY_PRELOAD_DIR_HOST="$CAPABILITY_STATE_HOST/podman-preload" } ensure_capability_state_paths() { @@ -1696,6 +1772,7 @@ ensure_capability_state_paths() { ensure_codex_directory "$CAPABILITY_STATE_HOST/podman-storage" "nested podman storage state" ensure_codex_directory "$CAPABILITY_STATE_HOST/podman-run" "nested podman runroot state" ensure_codex_directory "$CAPABILITY_STATE_HOST/podman-runtime" "nested podman runtime state" + ensure_codex_directory "$CAPABILITY_PRELOAD_DIR_HOST" "nested podman preload state" fi } @@ -1768,6 +1845,16 @@ capability_build_stamp_matches_current() { [[ $stamp_digest == "$CAPABILITY_MANIFEST_DIGEST" && $stamp_caps == "$REQUESTED_CAPABILITIES" ]] } +run_with_nested_podman_root() { + if capability_list_contains "${SLOPTRAP_ACTIVE_CAPABILITIES:-}" "nested-podman" \ + && [[ $(id -u) -ne 0 ]] \ + && command -v setpriv >/dev/null 2>&1; then + setpriv --reuid 0 --regid 0 --clear-groups -- "$@" + return + fi + "$@" +} + assert_path_within_code_dir() { local candidate=$1 local resolved @@ -2057,7 +2144,9 @@ detect_container_engine() { local override=${SLOPTRAP_CONTAINER_ENGINE-} if [[ -n $override ]]; then local engine="${override,,}" - if ! command -v "$engine" >/dev/null 2>&1; then + if [[ $engine == */* ]]; then + [[ -x $engine ]] || error "container engine '$engine' is not executable" + elif ! command -v "$engine" >/dev/null 2>&1; then error "container engine '$engine' not found in PATH" fi printf '%s' "$engine" @@ -2706,6 +2795,7 @@ prepare_container_runtime() { -v "$CAPABILITY_STATE_HOST/podman-storage:$SLOPTRAP_CODEX_HOME_CONT/capabilities/podman/storage$SLOPTRAP_VOLUME_LABEL" -v "$CAPABILITY_STATE_HOST/podman-run:$SLOPTRAP_CODEX_HOME_CONT/capabilities/podman/run$SLOPTRAP_VOLUME_LABEL" -v "$CAPABILITY_STATE_HOST/podman-runtime:$SLOPTRAP_CODEX_HOME_CONT/capabilities/podman/runtime$SLOPTRAP_VOLUME_LABEL" + -v "$CAPABILITY_PRELOAD_DIR_HOST:$SLOPTRAP_CODEX_HOME_CONT/capabilities/podman/preload$SLOPTRAP_VOLUME_LABEL" ) fi @@ -2728,14 +2818,24 @@ prepare_container_runtime() { if capability_list_contains "$ENABLED_CAPABILITIES" "nested-podman" && [[ $SLOPTRAP_NETWORK_NAME == "host" ]]; then env_args+=(-e "SLOPTRAP_INNER_PODMAN_HOST_NETWORK=1") fi + if capability_list_contains "$ENABLED_CAPABILITIES" "nested-podman"; then + env_args+=( + -e "SLOPTRAP_RECURSIVE_PARENT_IMAGE_ARCHIVE=$(recursive_preload_archive_container_path)" + -e "SLOPTRAP_RECURSIVE_PARENT_IMAGE_NAME=$SLOPTRAP_IMAGE_NAME" + ) + fi - local uid gid + local uid gid user uid=$(id -u) gid=$(id -g) + user=$(id -un 2>/dev/null || true) env_args+=( -e "SLOPTRAP_HOST_UID=$uid" -e "SLOPTRAP_HOST_GID=$gid" ) + if [[ -n $user ]]; then + env_args+=(-e "SLOPTRAP_HOST_USER=$user") + fi local -a user_opts=("--user" "$uid:$gid") if [[ $CONTAINER_ENGINE == "podman" ]]; then user_opts=(--userns="keep-id:uid=$uid,gid=$gid" "${user_opts[@]}") @@ -2860,6 +2960,169 @@ rebuild_image() { write_capability_build_stamp } +compute_recursive_preload_stamp() { + local launcher_source=${BASH_SOURCE[0]:-$0} + local launcher_digest + launcher_digest=$(sha256sum "$launcher_source") + printf '%s\n%s\n' "${launcher_digest%% *}" "$SLOPTRAP_IMAGE_NAME" +} + +recursive_preload_archive_host_path() { + printf '%s/%s.tar\n' "$CAPABILITY_PRELOAD_DIR_HOST" "$SLOPTRAP_IMAGE_NAME" +} + +recursive_preload_archive_container_path() { + printf '%s/capabilities/podman/preload/%s.tar\n' "$SLOPTRAP_CODEX_HOME_CONT" "$SLOPTRAP_IMAGE_NAME" +} + +prepare_recursive_parent_image_preload() { + capability_list_contains "$ENABLED_CAPABILITIES" "nested-podman" || return 0 + local engine_name=${CONTAINER_ENGINE##*/} + case "$engine_name" in + sloppodman|sloppodman*|.sloppodman*) + return 0 + ;; + esac + + local archive_path stamp_path image_id recorded_id="" + archive_path=$(recursive_preload_archive_host_path) + stamp_path="${archive_path}.stamp" + if ! image_id=$("$CONTAINER_ENGINE" image inspect --format '{{.Id}}' "$SLOPTRAP_IMAGE_NAME" 2>/dev/null); then + return 1 + fi + if [[ -f $archive_path && -f $stamp_path ]]; then + recorded_id=$(sed -n '1p' "$stamp_path" 2>/dev/null || true) + if [[ $recorded_id == "$image_id" ]]; then + return 0 + fi + fi + local tmp_archive="${archive_path}.tmp" + if ! "$CONTAINER_ENGINE" save -o "$tmp_archive" "$SLOPTRAP_IMAGE_NAME"; then + rm -f "$tmp_archive" + return 1 + fi + mv "$tmp_archive" "$archive_path" + printf '%s\n%s\n' "$image_id" "$SLOPTRAP_IMAGE_NAME" >"$stamp_path" +} + +load_recursive_parent_image() { + local engine_name=${CONTAINER_ENGINE##*/} + local archive_path=${SLOPTRAP_RECURSIVE_PARENT_IMAGE_ARCHIVE:-} + local source_image_name=${SLOPTRAP_RECURSIVE_PARENT_IMAGE_NAME:-$SLOPTRAP_IMAGE_NAME} + case "$engine_name" in + sloppodman|sloppodman*|.sloppodman*) + ;; + *) + return 1 + ;; + esac + capability_list_contains "${SLOPTRAP_ACTIVE_CAPABILITIES:-}" "nested-podman" || return 1 + [[ -n $archive_path && -r $archive_path ]] || return 1 + + if ! "$CONTAINER_ENGINE" load -i "$archive_path"; then + return 1 + fi + if ! "$CONTAINER_ENGINE" image inspect "$SLOPTRAP_IMAGE_NAME" >/dev/null 2>&1; then + [[ -n $source_image_name ]] || return 1 + "$CONTAINER_ENGINE" tag "$source_image_name" "$SLOPTRAP_IMAGE_NAME" || return 1 + fi + "$CONTAINER_ENGINE" image inspect "$SLOPTRAP_IMAGE_NAME" >/dev/null 2>&1 || return 1 + write_capability_build_stamp + return 0 +} + +prepare_recursive_parent_image_archive() { + local archive_dir="$CODE_DIR/.sloptrap-preload" + local archive_path="$archive_dir/${SLOPTRAP_IMAGE_NAME}.tar" + local stamp_path="$archive_path.stamp" + local desired_stamp current_stamp="" + desired_stamp=$(compute_recursive_preload_stamp) + if [[ -f $archive_path && -f $stamp_path ]]; then + current_stamp=$(cat "$stamp_path" 2>/dev/null || true) + if [[ $current_stamp == "$desired_stamp" ]]; then + printf '%s\n' "$archive_path" + return 0 + fi + fi + + assert_path_within_code_dir "$archive_dir" + mkdir -p "$archive_dir" + + local overlay_dir + overlay_dir=$(create_temp_dir "recursive-preload") + local helper + for helper in sloptrap-entrypoint sloptrap-helperd slop-apt slopcap sloppodman; do + populate_embedded_helper "$helper" "$overlay_dir/usr/local/bin/$helper" + done + chmod 0755 \ + "$overlay_dir/usr/local/bin/sloptrap-entrypoint" \ + "$overlay_dir/usr/local/bin/sloptrap-helperd" \ + "$overlay_dir/usr/local/bin/slop-apt" \ + "$overlay_dir/usr/local/bin/slopcap" \ + "$overlay_dir/usr/local/bin/sloppodman" + + local tmp_archive="$archive_path.tmp" + rm -f "$tmp_archive" + if ! run_with_nested_podman_root tar --numeric-owner --one-file-system \ + --exclude=./proc \ + --exclude=./sys \ + --exclude=./dev \ + --exclude=./run \ + --exclude=./tmp \ + --exclude=./workspace \ + --exclude=./codex \ + --exclude=./usr/local/bin/sloptrap-entrypoint \ + --exclude=./usr/local/bin/sloptrap-helperd \ + --exclude=./usr/local/bin/slop-apt \ + --exclude=./usr/local/bin/slopcap \ + --exclude=./usr/local/bin/sloppodman \ + -cf "$tmp_archive" -C / .; then + rm -f "$tmp_archive" + return 1 + fi + if ! tar --numeric-owner -rf "$tmp_archive" -C "$overlay_dir" .; then + rm -f "$tmp_archive" + return 1 + fi + mv "$tmp_archive" "$archive_path" + printf '%s\n' "$desired_stamp" >"$stamp_path" + printf '%s\n' "$archive_path" +} + +import_recursive_parent_image() { + local engine_name=${CONTAINER_ENGINE##*/} + local archive_path=${SLOPTRAP_RECURSIVE_PARENT_IMAGE_ARCHIVE:-} + case "$engine_name" in + sloppodman|sloppodman*|.sloppodman*) + ;; + *) + return 1 + ;; + esac + capability_list_contains "${SLOPTRAP_ACTIVE_CAPABILITIES:-}" "nested-podman" || return 1 + + if [[ -n $archive_path ]]; then + [[ -r $archive_path ]] || return 1 + else + if ! archive_path=$(prepare_recursive_parent_image_archive); then + return 1 + fi + fi + + if ! "$CONTAINER_ENGINE" import \ + --change 'ENTRYPOINT ["/usr/local/bin/sloptrap-entrypoint"]' \ + --change 'WORKDIR /workspace' \ + --change 'ENV SHELL=/bin/bash' \ + --change 'ENV HOME=/home/sloptrap' \ + --change "LABEL $SLOPTRAP_IMAGE_LABEL" \ + "$archive_path" \ + "$SLOPTRAP_IMAGE_NAME"; then + return 1 + fi + write_capability_build_stamp + return 0 +} + build_if_missing() { ensure_capability_trust if $DRY_RUN; then @@ -2888,6 +3151,12 @@ build_if_missing() { fi return 0 fi + if load_recursive_parent_image; then + return 0 + fi + if import_recursive_parent_image; then + return 0 + fi build_image } @@ -2934,6 +3203,7 @@ prune_sloptrap_images() { run_codex_command() { local -a extra_args=("$@") ensure_codex_storage_paths + prepare_recursive_parent_image_preload || true local -a cmd=("${BASE_CONTAINER_CMD[@]}" "$SLOPTRAP_IMAGE_NAME" "codex") if [[ ${#CODEX_ARGS_ARRAY[@]} -gt 0 ]]; then cmd+=("${CODEX_ARGS_ARRAY[@]}") @@ -2955,6 +3225,7 @@ run_codex() { run_login_target() { ensure_codex_storage_paths + prepare_recursive_parent_image_preload || true if ! $DRY_RUN; then status_line "Login %s\n" "$SLOPTRAP_IMAGE_NAME" fi @@ -2964,6 +3235,7 @@ run_login_target() { run_shell_target() { ensure_codex_storage_paths + prepare_recursive_parent_image_preload || true if ! $DRY_RUN; then status_line "Shell %s\n" "$SLOPTRAP_IMAGE_NAME" fi diff --git a/tests/run_tests.sh b/tests/run_tests.sh index d86cbb2..73f4209 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -98,6 +98,10 @@ verify_secret_mounts() { } if [[ ${1-} == "image" && ${2-} == "inspect" && ${FAKE_PODMAN_INSPECT_FAIL:-0} == 1 ]]; then + if [[ " $* " == *" --format "* ]]; then + printf 'fake-image-id\n' + exit 0 + fi echo "FAKE PODMAN (fail): $*" >>"$FAKE_PODMAN_LOG" exit 1 fi @@ -510,9 +514,15 @@ EOF if ! grep -q -- "-v ${codex_root}/auth.json:/codex/auth.json:Z" "$STUB_LOG"; then record_failure "recursive_slopsloptrap: missing recursive auth bind mount" fi + if ! grep -q -- "/capabilities/podman-preload:/codex/capabilities/podman/preload:Z" "$STUB_LOG"; then + record_failure "recursive_slopsloptrap: missing recursive preload bind mount" + fi if ! grep -q -- "-v ${codex_root}/sloptrap/state/" "$STUB_LOG"; then record_failure "recursive_slopsloptrap: missing recursive state bind mount" fi + if ! grep -q -- "SLOPTRAP_RECURSIVE_PARENT_IMAGE_ARCHIVE=/codex/capabilities/podman/preload/slopsloptrap-sloptrap-image.tar" "$STUB_LOG"; then + record_failure "recursive_slopsloptrap: missing recursive preload archive environment" + fi if grep -q -- "-v ${codex_root}/.codex/auth.json:/codex/auth.json:Z" "$STUB_LOG"; then record_failure "recursive_slopsloptrap: should not fall back to CODEX_HOME/.codex in recursive mode" fi @@ -527,6 +537,9 @@ EOF if [[ -z $first_run || $first_run == *" login" ]]; then record_failure "recursive_slopsloptrap: recursive auth should avoid login target" fi + if ! grep -q -- "FAKE PODMAN: save -o " "$STUB_LOG"; then + record_failure "recursive_slopsloptrap: should export the parent image for recursive preload" + fi if [[ $first_run != *"/bin/bash"* ]]; then record_failure "recursive_slopsloptrap: shell target did not reach child container run" fi @@ -540,6 +553,102 @@ EOF rm -rf "$temp_root" } +run_recursive_parent_image_fallback() { + printf '==> recursive_parent_image_fallback\n' + local temp_root helper_bin scenario_dir archive_path tool_log + local inner_podman_root inner_podman_runroot inner_runtime_dir + temp_root=$(mktemp -d) + helper_bin="$temp_root/bin" + scenario_dir="$temp_root/fallback-repo" + archive_path="$scenario_dir/parent-runtime.tar" + tool_log="$temp_root/tool.log" + inner_podman_root="$temp_root/podman-storage" + inner_podman_runroot="$temp_root/podman-run" + inner_runtime_dir="$temp_root/podman-runtime" + mkdir -p "$helper_bin" "$scenario_dir" "$inner_podman_root" "$inner_podman_runroot" "$inner_runtime_dir" + : >"$tool_log" + + cat >"$scenario_dir/.sloptrap" <<'EOF' +name=fallback-repo +capabilities=nested-podman +EOF + printf 'archive\n' >"$archive_path" + + if ! extract_embedded_helper "sloppodman" "$helper_bin/sloppodman"; then + record_failure "recursive_parent_image_fallback: failed to extract sloppodman helper" + rm -rf "$temp_root" + return + fi + + cat >"$helper_bin/podman" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +printf 'podman %s\n' "$*" >>"$TEST_TOOL_LOG" +args=("$@") +idx=0 +loaded_flag="${TEST_TOOL_LOG}.loaded" +while (( idx < ${#args[@]} )); do + if [[ ${args[$idx]} == "image" ]] && (( idx + 1 < ${#args[@]} )) && [[ ${args[$((idx + 1))]} == "inspect" ]]; then + if [[ -f $loaded_flag ]]; then + exit 0 + fi + exit 1 + fi + if [[ ${args[$idx]} == "load" ]]; then + : >"$loaded_flag" + exit 0 + fi + ((idx+=1)) +done +exit 0 +EOF + cat >"$helper_bin/setpriv" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +while [[ $# -gt 0 ]]; do + case "$1" in + --reuid|--regid) + shift 2 + ;; + --clear-groups) + shift + ;; + --) + shift + break + ;; + *) + break + ;; + esac +done +exec "$@" +EOF + chmod +x "$helper_bin/sloppodman" "$helper_bin/podman" "$helper_bin/setpriv" + + if ! TEST_TOOL_LOG="$tool_log" PATH="$helper_bin:$PATH" HOME="$temp_root/home" CODEX_HOME="$temp_root/home" \ + SLOPTRAP_CONTAINER_ENGINE="sloppodman" \ + SLOPTRAP_ACTIVE_CAPABILITIES="nested-podman" \ + SLOPTRAP_WORKDIR="$scenario_dir" \ + SLOPTRAP_RECURSIVE_PARENT_IMAGE_ARCHIVE="$archive_path" \ + SLOPTRAP_INNER_PODMAN_ROOT="$inner_podman_root" \ + SLOPTRAP_INNER_PODMAN_RUNROOT="$inner_podman_runroot" \ + XDG_RUNTIME_DIR="$inner_runtime_dir" \ + "$SLOPTRAP_BIN" --trust-capabilities "$scenario_dir" build-if-missing >/dev/null 2>&1; then + record_failure "recursive_parent_image_fallback: build-if-missing exited non-zero" + rm -rf "$temp_root" + return + fi + if grep -q -- 'podman --root .* build ' "$tool_log"; then + record_failure "recursive_parent_image_fallback: fallback should avoid podman build" + fi + if ! grep -q -- "podman --root $inner_podman_root --runroot $inner_podman_runroot --storage-driver vfs --cgroup-manager cgroupfs --events-backend file load -i $archive_path" "$tool_log"; then + record_failure "recursive_parent_image_fallback: fallback did not load the parent archive into the nested store" + fi + + rm -rf "$temp_root" +} + run_project_state_isolation() { local scenario_a scenario_b scenario_a=$(cd "$TEST_ROOT/resume_target" && pwd -P) @@ -885,6 +994,9 @@ run_capability_profiles() { if ! grep -q -- "SLOPTRAP_HOST_GID=$(id -g)" "$STUB_LOG"; then record_failure "capability_profiles: host gid environment missing" fi + if ! grep -q -- "SLOPTRAP_HOST_USER=$(id -un)" "$STUB_LOG"; then + record_failure "capability_profiles: host user environment missing" + fi if ! grep -q -- "SLOPTRAP_INNER_PODMAN_HOST_NETWORK=1" "$STUB_LOG"; then record_failure "capability_profiles: inner podman host-network mirror flag missing" fi @@ -938,11 +1050,11 @@ run_embedded_capability_helpers() { return fi - cat >"$helper_bin/podman" <<'EOF' +cat >"$helper_bin/podman" <<'EOF' #!/usr/bin/env bash set -euo pipefail -printf 'podman-env BUILDAH_ISOLATION=%s CONTAINERS_STORAGE_CONF=%s CONTAINERS_CONF=%s\n' \ - "${BUILDAH_ISOLATION:-}" "${CONTAINERS_STORAGE_CONF:-}" "${CONTAINERS_CONF:-}" >>"$TEST_TOOL_LOG" +printf 'podman-env BUILDAH_ISOLATION=%s _CONTAINERS_USERNS_CONFIGURED=%s CONTAINERS_STORAGE_CONF=%s CONTAINERS_CONF=%s\n' \ + "${BUILDAH_ISOLATION:-}" "${_CONTAINERS_USERNS_CONFIGURED:-}" "${CONTAINERS_STORAGE_CONF:-}" "${CONTAINERS_CONF:-}" >>"$TEST_TOOL_LOG" printf 'podman %s\n' "$*" >>"$TEST_TOOL_LOG" exit 0 EOF @@ -1086,6 +1198,9 @@ EOF if ! grep -q -- 'podman-env BUILDAH_ISOLATION=chroot ' "$tool_log"; then record_failure "embedded_capability_helpers: sloppodman did not set BUILDAH_ISOLATION=chroot" fi + if ! grep -q -- '_CONTAINERS_USERNS_CONFIGURED=done ' "$tool_log"; then + record_failure "embedded_capability_helpers: sloppodman did not mark nested podman as already userns-configured" + fi if [[ ! -f $inner_runtime_dir/config/containers/storage.conf ]] \ || [[ ! -f $inner_runtime_dir/config/containers/containers.conf ]]; then record_failure "embedded_capability_helpers: sloppodman did not materialize its container config files" @@ -1115,7 +1230,32 @@ case "${1-}" in ;; esac EOF - chmod +x "$helper_bin/id" + cat >"$temp_root/entrypoint-helperd" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +exit 0 +EOF + chmod +x "$helper_bin/id" "$temp_root/entrypoint-helperd" + local entry_subuid entry_subgid + entry_subuid="$temp_root/entry-subuid" + entry_subgid="$temp_root/entry-subgid" + printf 'sloptrap:100000:65536\n' >"$entry_subuid" + printf 'sloptrap:100000:65536\n' >"$entry_subgid" + if ! TEST_TOOL_LOG="$tool_log" PATH="$helper_bin:$PATH" SLOPTRAP_ACTIVE_CAPABILITIES="nested-podman" \ + SLOPTRAP_HOST_UID="1337" SLOPTRAP_HOST_GID="1337" SLOPTRAP_HOST_USER="$caller_user" \ + SLOPTRAP_PODMAN_SUBUID_FILE="$entry_subuid" SLOPTRAP_PODMAN_SUBGID_FILE="$entry_subgid" \ + SLOPTRAP_PODMAN_SUBID_START="200000" SLOPTRAP_PODMAN_SUBID_COUNT="65536" \ + SLOPTRAP_PODMAN_SUBGID_START="200000" SLOPTRAP_PODMAN_SUBGID_COUNT="65536" \ + SLOPTRAP_HELPERD_BIN="$temp_root/entrypoint-helperd" \ + "$helper_bin/sloptrap-entrypoint" true >/dev/null 2>&1; then + record_failure "embedded_capability_helpers: entrypoint did not synthesize subid files for the host user" + fi + if [[ -z $(awk -F: -v account="$caller_user" '$1 == account { print $2 ":" $3 }' "$entry_subuid" 2>/dev/null || true) ]]; then + record_failure "embedded_capability_helpers: entrypoint did not prefer SLOPTRAP_HOST_USER for subuid synthesis" + fi + if [[ -z $(awk -F: -v account="$caller_user" '$1 == account { print $2 ":" $3 }' "$entry_subgid" 2>/dev/null || true) ]]; then + record_failure "embedded_capability_helpers: entrypoint did not prefer SLOPTRAP_HOST_USER for subgid synthesis" + fi local caller_subuid root_subuid caller_subgid root_subgid local helper_subuid_file helper_subgid_file helper_subuid_file="$temp_root/helper-subuid" @@ -1270,6 +1410,7 @@ run_resume_omits_runtime_context run_auth_file_mount run_codex_home_override run_recursive_slopsloptrap +run_recursive_parent_image_fallback run_project_state_isolation run_auto_login_empty_auth run_codex_symlink_home