Skip to content

Comments

Fix copying of symlinks in containers#14885

Merged
calavera merged 1 commit intomoby:masterfrom
jlhawn:fix_cp_symlink
Jul 30, 2015
Merged

Fix copying of symlinks in containers#14885
calavera merged 1 commit intomoby:masterfrom
jlhawn:fix_cp_symlink

Conversation

@jlhawn
Copy link
Contributor

@jlhawn jlhawn commented Jul 23, 2015

Copying a symlink should do just that: copy the symlink NOT copy the target of the symlink. Also, the resulting file from the copy should have the name of the symlink NOT the name of the target file.

This patch includes a fix to StatPath, ArchivePath, and ExtractToDir methods on containers. These operations should resolve symlinks that are in the path but if the resource itself is a symlink then it should not be resolved. This patch puts this logic into a common function resolvePath which resolves symlinks of the path's dir in scope of the container rootfs but does not resolve the final element of the path. Now archive, extract, and stat operations will return symlinks if the path is indeed a symlink.

Notably, destination paths should always be fully resolved, i.e., copying to a symlink should affect the symlink target, not the symlink itself.

@jlhawn
Copy link
Contributor Author

jlhawn commented Jul 23, 2015

ping @tonistiigi @thaJeztah

@jlhawn
Copy link
Contributor Author

jlhawn commented Jul 23, 2015

see discussion in #13171 (comment)

@icecrime
Copy link
Contributor

Can you please let us know if this is strictly required for 1.8.0, and if we're only fixing usability issues or potential security holes?

@jlhawn
Copy link
Contributor Author

jlhawn commented Jul 23, 2015

@icecrime So far it's just a usability/behavior issue. We haven't yet determined if there's a real security issue with this.

@thaJeztah
Copy link
Member

Because none of us is a security expert, I'd prefer to have the security team to have a look,
both at this change and the issue it should resolve (reported here #13171 (comment) and discussed on IRC, starting here: https://botbot.me/freenode/docker-dev/2015-07-22/?msg=45272608&page=6).
Better safe than sorry, now that we are still able to check before the 1.8 release.

ping @ewindisch @diogomonica @NathanMcCauley

(just my 0.02, apologies if I crossed my boundaries here)

@tonistiigi
Copy link
Member

This seems to only address the Stat-header issue and not rebasing. If I run docker cp id:/foo/ bar where foo is a symlink I don't get local folder bar but a folder that has the name of the symlink target.

This breaks symlink targets without slash. cp -a seems to use same behavior for symlinks-to-dirs as regular directories. I think for local paths this was already different before but for remotes it changed with this PR.

Any comment on the other points in #13171 (comment) ?

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 we should do this clean step always. There's a breakout with path /.. for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah, yeah. I'll remove this condition then.

@icecrime
Copy link
Contributor

Ok! Adding this to the milestone, we don't have to block 1.8.0-rc1 on it though.

@icecrime icecrime added this to the 1.8.0 milestone Jul 23, 2015
@jlhawn
Copy link
Contributor Author

jlhawn commented Jul 23, 2015

@tonistiigi

This seems to only address the Stat-header issue and not rebasing. If I run docker cp id:/foo/ bar where foo is a symlink I don't get local folder bar but a folder that has the name of the symlink target.

I'll add a test case for this and make sure it's fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Rationale for removing this: It is never used, we have logic that uses it below, but I replaced it with the rebaseNames map.

@jlhawn
Copy link
Contributor Author

jlhawn commented Jul 24, 2015

A couple of windows test failures as expected... wish I could run those locally :-(.

@thaJeztah
Copy link
Member

@jlhawn I'm sure @ahmetalpbalkan is able to give you access to an azure machine 😄

@jlhawn
Copy link
Contributor Author

jlhawn commented Jul 24, 2015

@thaJeztah eh, no biggie... I have a windows 8.1 VM locally that I could use, I just haven't bothered setting up the docker dev environment on it. The test failure was also pretty minor: I was comparing a local windows backslash-delimited path with a constant unix-style forward-slash-delimited path.

@jlhawn jlhawn force-pushed the fix_cp_symlink branch 2 times, most recently from 465df6a to afb7388 Compare July 25, 2015 00:01
@jlhawn
Copy link
Contributor Author

jlhawn commented Jul 25, 2015

@tonistiigi This should now fix the behavior you saw on the source being a symlink to a directory.

As for your other question from #13171 (comment):

How can we protect this from running containers updating a filesystem/volumes when the request is taking place? I mean we check for breakouts in the beginning of GET/PUT but if a container is using the filesystem it could just flip a symlink in the right time and then we would have full read/write to host.

I think the only thing we could do is require that the container be stopped or paused? (can that be considered outside the scope of this PR?)

@jlhawn jlhawn force-pushed the fix_cp_symlink branch 4 times, most recently from 5c268ea to ec8e53d Compare July 27, 2015 22:33
@tonistiigi
Copy link
Member

Is docker cp with symlinks supposed to follow the semantics of cp -a? Docs are bit contradictory saying it's cp -a but also that symlink is always/only followed if it's followed by a slash.

#setup (GNU coreutils 8.21):
mkdir foo
touch foo/bar
ln -s foo baz
mkdir abc
touch abc/def

cp -a abc baz # creates foo/abc/def
docker cp t0:abc baz # error: cannot copy directory
docker cp abc t0:baz # error: cannot copy directory

cp -a abc/def baz # creates foo/def
docker cp t0:abc/def baz # creates baz
docker cp abc/def t0:baz # creates baz

cp -a baz/. abc # creates abc/bar
docker cp t0:baz/. abc # creates abc/bar
docker cp baz/. t0:abc # creates abc/(baz -> foo) + WARN

The resolvePath() logic itself seems safe but I'm bit concerned about the readability of the actual place where the breakout protection happens. There is bunch of logic in TarResource for testing symlink/ paths but if I understand it correctly this is only used in client side and would be unsafe otherwise. Maybe we could add a comment in the daemon code that calls TarResource that the path sent there can be symlink or dir/ but should never be symlink/ or symlink/. and it's the calling code's responsibility to check that.

Having two separate code paths that do tar rebasing probably also isn't ideal maintenance-wise and I can't help to point out that by not having the first-level directory we could have probably avoided the need to do rebasing in both ends(plus the need to validate downloaded tar that we should probably also do).

Copy link
Member

Choose a reason for hiding this comment

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

This does not seem right. If the path is /myvolume/symlinktoroot/ it would be matched to the volume and writing would go to the readonly rootfs.

@jlhawn
Copy link
Contributor Author

jlhawn commented Jul 28, 2015

Is docker cp with symlinks supposed to follow the semantics of cp -a? Docs are bit contradictory saying it's cp -a but also that symlink is always/only followed if it's followed by a slash.

This should only apply to the source argument. From your example (thanks!) it looks like we've also erroneously applied it to the destination argument 😦. I'll fold this into this PR also (and add a test case for it based on your example).

@jlhawn jlhawn force-pushed the fix_cp_symlink branch 3 times, most recently from 266dadd to 6c25a12 Compare July 29, 2015 00:04
@jlhawn
Copy link
Contributor Author

jlhawn commented Jul 29, 2015

Okay, all updated again. Currently the Windows CI is on fire, so I guess we'll have to wait on that.

@jlhawn jlhawn force-pushed the fix_cp_symlink branch 2 times, most recently from 69a564e to 9417a29 Compare July 29, 2015 02:11
Copy link
Member

Choose a reason for hiding this comment

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

What's the definition of Name? If its always basename of Path then why return as a separate field at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it's the basename, while Path is the absolute path.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll go ahead ad remove the Path field. Your right, it's unnecessary given that the user has to provide a path in the first place.

Copy link
Member

Choose a reason for hiding this comment

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

Well, Path is cleaned and made absolute so it may actually have some value. Name, on the other hand, anyone can easily get from Path (if the definition remains as it is currently).

@tonistiigi
Copy link
Member

LGTM

1 similar comment
@icecrime
Copy link
Contributor

LGTM

Copy link
Contributor

Choose a reason for hiding this comment

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

ioutil.WriteFile?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated!

@jlhawn jlhawn force-pushed the fix_cp_symlink branch 2 times, most recently from f199312 to 05478d9 Compare July 30, 2015 18:06
@calavera
Copy link
Contributor

LGTM, but can we squash all those commits? all of the are related to the same thing and it would be easier to keep track of the for the release.

@jlhawn
Copy link
Contributor Author

jlhawn commented Jul 30, 2015

@calavera okay, I'll squash them together

[pkg/archive] Update archive/copy path handling

  - Remove unused TarOptions.Name field.
  - Add new TarOptions.RebaseNames field.
  - Update some of the logic around path dir/base splitting.
  - Update some of the logic behind archive entry name rebasing.

[api/types] Add LinkTarget field to PathStat

[daemon] Fix stat, archive, extract of symlinks

  These operations *should* resolve symlinks that are in the path but if the
  resource itself is a symlink then it *should not* be resolved. This patch
  puts this logic into a common function `resolvePath` which resolves symlinks
  of the path's dir in scope of the container rootfs but does not resolve the
  final element of the path. Now archive, extract, and stat operations will
  return symlinks if the path is indeed a symlink.

[api/client] Update cp path hanling

[docs/reference/api] Update description of stat

  Add the linkTarget field to the header of the archive endpoint.
  Remove path field.

[integration-cli] Fix/Add cp symlink test cases

  Copying a symlink should do just that: copy the symlink NOT
  copy the target of the symlink. Also, the resulting file from
  the copy should have the name of the symlink NOT the name of
  the target file.

  Copying to a symlink should copy to the symlink target and not
  modify the symlink itself.

Docker-DCO-1.1-Signed-off-by: Josh Hawn <[email protected]> (github: jlhawn)
@calavera
Copy link
Contributor

💚 Merging!

calavera added a commit that referenced this pull request Jul 30, 2015
Fix copying of symlinks in containers
@calavera calavera merged commit 030f61d into moby:master Jul 30, 2015
@jlhawn
Copy link
Contributor Author

jlhawn commented Jul 30, 2015

🎉

@calavera
Copy link
Contributor

cherry picked into #15091

@thaJeztah
Copy link
Member

Great work guys!

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.

6 participants