Reject symlinked Containerfile and ignore files escaping build context#6886
Reject symlinked Containerfile and ignore files escaping build context#6886Honny1 wants to merge 1 commit into
Conversation
b845b72 to
e462928
Compare
|
PTAL @podman-container-tools/buildah-maintainers @podman-container-tools/buildah-reviewers |
| return foundCtrFile, nil | ||
| // isRegularFileInContext returns true if path is a regular file (or a symlink | ||
| // to one) whose real target is inside contextDir. | ||
| func isRegularFileInContext(contextDir, path string) bool { |
There was a problem hiding this comment.
(A drive-by, I have really only reviewed this part:) I think this is fine — works on Unix systems, and on Windows filepath.Rel rejects inputs which differ in the volume letter.
In principle, I think building an explicit os.Root and working within that would be structurally much safer, but looking at just this diff, that would probably be an invasive change and affect public API. (I didn’t investigate how difficult that would be.)
nalind
left a comment
There was a problem hiding this comment.
A subtle bug in how the symlinks get resolved, and I don't think I understand why some of these tests are using a subshell to run ln -s.
| } | ||
| if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) { | ||
| return false | ||
| } |
There was a problem hiding this comment.
This appears to run into the same issue @eriksjolund pointed out in #6885 (comment).
There was a problem hiding this comment.
If you mean "../../correctly-guessed/directory/name/Containerfile" pointing us back at "./Containerfile": I think to reject that we would truly need os.Root, or an equivalent manual use of openat2 RESOLVE_BENEATH (+ some implementation on non-Linux?) — or an entirely new manual symlink resolving code.
(filepath.SecureJoin is an ~equivalent of RESOLVE_IN_ROOT, it would not reject such links but interpret them differently.)
| RUN echo traversal-back-inside-works | ||
| _EOF | ||
| # Symlink path goes ../context/file -- traverses outside but real target resolves inside. | ||
| (cd ${contextdir} && ln -s ../context/file Dockerfile) |
There was a problem hiding this comment.
| (cd ${contextdir} && ln -s ../context/file Dockerfile) | |
| ln -s ../context/file ${contextdir}/Dockerfile |
| (cd ${contextdir} && ln -s ../context/file Dockerfile) | ||
|
|
||
| run_buildah build $WITH_POLICY_JSON ${contextdir} | ||
| assert "$output" =~ "traversal-back-inside-works" |
There was a problem hiding this comment.
I think this is expected to fail while attempting to read ${contextdir}/context/file.
| RUN test -f /dir/file | ||
| _EOF | ||
| touch ${contextdir}/file | ||
| (cd ${contextdir} && ln -s ../ign Dockerfile.dockerignore) |
There was a problem hiding this comment.
| (cd ${contextdir} && ln -s ../ign Dockerfile.dockerignore) | |
| ln -s ../ign ${contextdir}/Dockerfile.dockerignore |
mtrmac
left a comment
There was a problem hiding this comment.
Just a quick look: This is getting increasingly convoluted.
If the goal is to solve https://github.com/podman-container-tools/buildah/pull/6886/changes#r3363568131, I think refactoring to build around os.Root would ultimately be safer and much easier to prove correct. But it might be very invasive, I didn’t check – I’ll let Buildah experts make the decision on whether this should happen.
| return false | ||
| } | ||
| clean := filepath.Clean(target) | ||
| if !filepath.IsAbs(target) && (clean == ".." || strings.HasPrefix(clean, ".."+string(filepath.Separator))) { |
There was a problem hiding this comment.
Shouldn’t absolute symlinks be treated as a clear attempt to break out?
| if err != nil { | ||
| return false | ||
| } | ||
| info, err := os.Lstat(path) |
There was a problem hiding this comment.
Couldn’t path have symlinks pointing outside of the target directory in the middle? ./symlink-to-etc/password. where symlink-to-etc is ../../../../../…/etc?
| return false | ||
| } | ||
| clean := filepath.Clean(target) | ||
| if !filepath.IsAbs(target) && (clean == ".." || strings.HasPrefix(clean, ".."+string(filepath.Separator))) { |
There was a problem hiding this comment.
path being dir1/dir2/symlink with symlink pointing to ../Containerfile does not actually escape.
|
I used |
Fixes: podman-container-tools#6861 Fixes: podman-container-tools/podman#28749 Signed-off-by: Jan Rodák <hony.com@seznam.cz>
|
|
||
| run_buildah 125 build $WITH_POLICY_JSON ${contextdir} | ||
| expect_output --substring "cannot find Containerfile or Dockerfile" | ||
| assert "$output" !~ "SHOULD-NOT-RUN" |
There was a problem hiding this comment.
If we're treating ${contextdir} like the root of a container rootfs (which I think approximates the desired behavior for resolving symlinks found inside of a context directory), I would have expected the target of the symlink to be readable. I believe this is an example from #6861 (comment).
| mkdir -p ${tarsrc} | ||
| ln -s ${targetfile} ${tarsrc}/Dockerfile | ||
| local context_tar=${TEST_SCRATCH_DIR}/context.tar | ||
| tar -cf ${context_tar} -C ${tarsrc} Dockerfile |
There was a problem hiding this comment.
Might want a --owner=0:0 --numeric-owner here.
| ln -s ${targetfile} ${tarsrc}/Dockerfile | ||
| local contentdir=${TEST_SCRATCH_DIR}/content | ||
| mkdir -p ${contentdir} | ||
| tar -cf ${contentdir}/context.tar -C ${tarsrc} Dockerfile |
There was a problem hiding this comment.
Might want a --owner=0:0 --numeric-owner here.
Fixes: #6861
Fixes: podman-container-tools/podman#28749
What type of PR is this?
/kind bug
What this PR does / why we need it:
How to verify it
Which issue(s) this PR fixes:
Special notes for your reviewer:
Does this PR introduce a user-facing change?