Skip to content

Fix docker cp when container source path is /#39357

Merged
thaJeztah merged 4 commits intomoby:masterfrom
tiborvass:cp-slash-fix
Jun 14, 2019
Merged

Fix docker cp when container source path is /#39357
thaJeztah merged 4 commits intomoby:masterfrom
tiborvass:cp-slash-fix

Conversation

@tiborvass
Copy link
Contributor

@tiborvass tiborvass commented Jun 12, 2019

Another attempt at fixing #39348
Fixes #39348
Previous attempt at #39351

Before 7a7357d, archive.TarResourceRebase was being used to copy files
and folders from the container. That function splits the source path
into a dirname + basename pair to support copying a file:
if you wanted to tar dir/file it would tar from dir the file file
(as part of the IncludedFiles option).

However, that path splitting logic was kept for folders as well, which
resulted in weird inputs to archive.TarWithOptions:
if you wanted to tar dir1/dir2 it would tar from dir1 the directory
dir2 (as part of IncludedFiles option).

Although it was weird, it worked fine until we started chrooting into
the container rootfs when doing a docker cp with container source set
to / (cf 3029e76 (#39292)).

The fix is to only do the path splitting logic if the source is a file.

Unfortunately, 7a7357d added support for LCOW by duplicating some of
this subtle logic. Ideally we would need to do more refactoring of the
archive codebase to properly encapsulate these behaviors behind well-
documented APIs.

This fix does not do that. Instead, it fixes the issue inline.

Signed-off-by: Tibor Vass [email protected]

I added a couple of more tests than the actual issue needs, just to make sure there are no other regressions compared to before the cve fix (3029e76).

Huge thanks to @cpuguy83 ❤️who worked tirelessly with me to understand the code and make this PR.

@tiborvass tiborvass force-pushed the cp-slash-fix branch 2 times, most recently from 33c5db2 to cac6afc Compare June 12, 2019 20:44
CID=$(docker create alpine)
docker cp $CID:/ out

Signed-off-by: Brian Goff <[email protected]>
Signed-off-by: Tibor Vass <[email protected]>
@codecov
Copy link

codecov bot commented Jun 12, 2019

Codecov Report

❗ No coverage uploaded for pull request base (master@4dc6b21). Click here to learn what that means.
The diff coverage is 0%.

@@            Coverage Diff            @@
##             master   #39357   +/-   ##
=========================================
  Coverage          ?   36.97%           
=========================================
  Files             ?      612           
  Lines             ?    45645           
  Branches          ?        0           
=========================================
  Hits              ?    16877           
  Misses            ?    26480           
  Partials          ?     2288

@cpuguy83
Copy link
Member

15:29:30 --- FAIL: TestCopyFromContainerRoot (1.83s)
15:29:30     copy_test.go:146: assertion failed: expression is false: found["/foo"]: /foo file not found in archive
15:29:30     copy_test.go:147: assertion failed: expression is false: found["/bar/baz"]: /bar/baz file not found in archive

@tiborvass tiborvass force-pushed the cp-slash-fix branch 3 times, most recently from 087d4c3 to d718ac0 Compare June 13, 2019 06:22
@tiborvass tiborvass marked this pull request as ready for review June 13, 2019 06:28
@tiborvass
Copy link
Contributor Author

Tibor Vass added 2 commits June 13, 2019 06:31
Signed-off-by: Tibor Vass <[email protected]>
Before 7a7357d, archive.TarResourceRebase was being used to copy files
and folders from the container. That function splits the source path
into a dirname + basename pair to support copying a file:
if you wanted to tar `dir/file` it would tar from `dir` the file `file`
(as part of the IncludedFiles option).

However, that path splitting logic was kept for folders as well, which
resulted in weird inputs to archive.TarWithOptions:
if you wanted to tar `dir1/dir2` it would tar from `dir1` the directory
`dir2` (as part of IncludedFiles option).

Although it was weird, it worked fine until we started chrooting into
the container rootfs when doing a `docker cp` with container source set
to `/` (cf 3029e76).

The fix is to only do the path splitting logic if the source is a file.

Unfortunately, 7a7357d added support for LCOW by duplicating some of
this subtle logic. Ideally we would need to do more refactoring of the
archive codebase to properly encapsulate these behaviors behind well-
documented APIs.

This fix does not do that. Instead, it fixes the issue inline.

Signed-off-by: Tibor Vass <[email protected]>
Copy link
Member

@cpuguy83 cpuguy83 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

sourceDir := resolvedPath
sourceBase := "."

if stat.Mode&os.ModeDir == 0 { // not dir
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be

if symlink {
opts = rebase
}

and always archivePath(resolvedPath).

Would be more understandable what is actually happening and wouldn't need the hack for removing the slash in the walker. But functionally they seem equivalent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Can be done in a follow up.

@tiborvass
Copy link
Contributor Author

@thaJeztah @kolyshkin updated with small requested change.

Previously, getWalkRoot("/", "foo") would return "//foo"
Now it returns "/foo"

Signed-off-by: Tibor Vass <[email protected]>
@tiborvass
Copy link
Contributor Author

Failure is TestAPISwarmLeaderElection which is unrelated

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@thaJeztah
Copy link
Member

Looks like s390x passed, but is hanging on the cleanup; https://jenkins.dockerproject.org/job/Docker-PRs-s390x/14591/console

06:06:03 OK: 1421 passed, 81 skipped
06:06:03 PASS
06:06:03 ---> Making bundle: .integration-daemon-stop (in bundles/test-integration)
06:06:16 Clearing AppArmor profiles cache:.
06:06:16 All profile caches have been cleared, but no profiles have been unloaded.
06:06:16 Unloading profiles will leave already running processes permanently
06:06:16 unconfined, which can lead to unexpected situations.
06:06:16 
06:06:16 To set a process to complain mode, use the command line tool
06:06:16 'aa-complain'. To really tear down all profiles, run the init script
06:06:16 with the 'teardown' option."
06:06:16 Removing test suite binaries

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Regression due to CVE mitigation from 39292

6 participants