Merge pull request #29942 from Garvity/fix/image-scp-working-directory

Fix image scp from inaccessible working directories
This commit is contained in:
Matt Heon authored and GitHub committed 2026-11-04 18:25:47 -05:00
commit a3af2edc34
3 files changed
+61 -10

No files matched your search

+27 -9
View File
@@ -958,10 +958,11 @@ func execTransferPodman(execUser *user.User, command []string, needToTag bool) (
_ = cmdLogin.Wait()
}()
cmd := exec.Command(command[0], command[1:]...)
cmd.Env = []string{"PATH=" + os.Getenv("PATH"), "TERM=" + os.Getenv("TERM")}
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
// Use the physical path; $PWD may contain symlinks inaccessible to execUser.
cwd, err := syscall.Getwd()
if err != nil {
return nil, err
}
uid, err := strconv.ParseInt(execUser.Uid, 10, 32)
if err != nil {
return nil, err
@@ -970,7 +971,7 @@ func execTransferPodman(execUser *user.User, command []string, needToTag bool) (
if err != nil {
return nil, err
}
cmd.SysProcAttr = &syscall.SysProcAttr{
sysProcAttr := &syscall.SysProcAttr{
Credential: &syscall.Credential{
Uid: uint32(uid),
Gid: uint32(gid),
@@ -978,11 +979,28 @@ func execTransferPodman(execUser *user.User, command []string, needToTag bool) (
NoSetGroups: false,
},
}
if needToTag {
cmd.Stdout = nil
return cmd.Output()
run := func(dir string) ([]byte, error) {
cmd := exec.Command(command[0], command[1:]...)
// An explicit directory makes exec check access after changing credentials.
cmd.Dir = dir
cmd.Env = []string{"PATH=" + os.Getenv("PATH"), "TERM=" + os.Getenv("TERM")}
cmd.Stderr = os.Stderr
cmd.SysProcAttr = sysProcAttr
if needToTag {
return cmd.Output()
}
cmd.Stdout = os.Stdout
return nil, cmd.Run()
}
return nil, cmd.Run()
out, err := run(cwd)
// Preserve relative paths when possible. Only retry a failure to start;
// a command that has already run must not be executed a second time.
var pathErr *os.PathError
if cwd != "/" && errors.As(err, &pathErr) && pathErr.Op == "fork/exec" && errors.Is(err, fs.ErrPermission) {
return run("/")
}
return out, err
}
func getSigFilename(sigStoreDirPath string) (string, error) {
+30 -1
View File
@@ -113,12 +113,24 @@ verify_iid_and_name() {
if [[ -z "$notme" ]]; then
skip "To run this test, set PODMAN_ROOTLESS_USER to a safe username"
fi
_sudo() { command sudo -n -u "$notme" "$@"; }
_sudo() (
cd /
command sudo -n -u "$notme" "$@"
)
fi
# If we can't sudo, we can't test.
_sudo true || skip "cannot sudo to $notme"
# Verify that save, load, and tag work when the destination user cannot access the caller's working directory.
if ! is_rootless; then
local private_dir=$PODMAN_TMPDIR/scp-cwd
mkdir -m 0700 "$private_dir"
run _sudo test -x "$private_dir"
assert "$status" -eq 1 "destination user cannot access the working directory"
cd "$private_dir"
fi
# Preserve digest of original image; we will compare against it later
run_podman image inspect --format '{{.RepoDigests}}' $IMAGE
src_digests=$output
@@ -178,6 +190,23 @@ verify_iid_and_name() {
run_podman rmi foobar:123
}
@test "podman image scp transfer with relative paths" {
skip_if_remote "only applicable under local podman"
skip_if_rootless "tests local transfers between root-owned stores"
cd "$PODMAN_TMPDIR"
local conf=registries-$(random_string).conf
printf 'unqualified-search-registries = ["example.com"]\n' > "$conf"
# Both forms must resolve paths relative to the caller's directory.
run_podman --registries-conf "./$conf" image scp root@localhost::$IMAGE root@localhost::
run_podman --registries-conf="./$conf" image scp root@localhost::$IMAGE root@localhost::
run_podman --registries-conf "$PODMAN_TMPDIR/$conf" image scp root@localhost::$IMAGE root@localhost::
# A command failure must be returned without retrying from another directory.
run_podman 125 --registries-conf "./$conf" image scp root@localhost::example.com/no-such-image:missing root@localhost::
assert "$output" =~ "image not known" "missing image error is preserved"
}
@test "podman load - by image ID" {
# FIXME: how to build a simple archive instead?
+4
View File
@@ -23,12 +23,16 @@ adjust+:
summary: local rootful test
test: bash ./system.sh
duration: 60m
require+:
- systemd-container
/local-rootless:
tag: [ local, rootless ]
summary: rootless test
test: bash ./system.sh rootless
duration: 60m
require+:
- systemd-container
/remote-root:
tag: [ remote, root ]