Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remote,build: ignore if .containerignore or .dockerignore is a symlink outside of buildContext #16315

Merged

Conversation

flouthoc
Copy link
Collaborator

Drop support for remote use-cases when .containerignore or .dockerignore is a symlink pointing to arbitrary location on host.

remote,build: do not allow to process arbitrary symlinks on host for remote builds.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Oct 27, 2022

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: flouthoc

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Oct 27, 2022
@flouthoc flouthoc force-pushed the remote-ignore-symlink branch 3 times, most recently from 61be56a to f933c67 Compare October 27, 2022 09:16
@rhatdan
Copy link
Member

rhatdan commented Oct 27, 2022

Can you just use securejoin "github.com/cyphar/filepath-securejoin"?

I think this will throw an error if .dockerignore or .containerignore point at a link outside of the root path. That way I could have a link within the context dir.

@flouthoc
Copy link
Collaborator Author

I think this will throw an error if .dockerignore or .containerignore point at a link outside of the root path

@rhatdan I think SecureJoin does not throws error if link is outside the root path, it just resolves the symlink and concats with the root. So final path is created something as /root/some/abs/resolved/symlink. In such cases since path does not exists so no error is relyed to user it just silently ignores the .containerignore or .dockerignore file without notifying the user.

However it works correctly if symlink is inside the root or relative to root.

Following is the diff which i tried

diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index aabc7290d..63d354ad1 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -27,6 +27,7 @@ import (
        "github.com/containers/storage/pkg/ioutils"
        "github.com/docker/go-units"
        "github.com/hashicorp/go-multierror"
+       securejoin "github.com/cyphar/filepath-securejoin"
        jsoniter "github.com/json-iterator/go"
        "github.com/sirupsen/logrus"
 )
@@ -754,16 +755,16 @@ func errIfSymlink(path string) error {
 }
 
 func parseDockerignore(root string) ([]string, error) {
-       path := filepath.Join(root, ".containerignore")
-       err := errIfSymlink(path)
+       path, err := securejoin.SecureJoin(root, ".containerignore")
+       //err := errIfSymlink(path)
        if err != nil {
                return nil, err
        }
        ignore, err := os.ReadFile(path)
        if err != nil {
                var dockerIgnoreErr error
-               path = filepath.Join(root, ".dockerignore")
-               symlinkErr := errIfSymlink(path)
+               path, symlinkErr := securejoin.SecureJoin(root, ".dockerignore")
+               //symlinkErr := errIfSymlink(path)
                if symlinkErr != nil {
                        return nil, symlinkErr
                }

@flouthoc
Copy link
Collaborator Author

That way I could have a link within the context dir.

@rhatdan even though secure-join is not working as expected but I have modified function to errIfSymlinkOutsideRoot which should handle this use-case.

@rhatdan
Copy link
Member

rhatdan commented Oct 27, 2022

But wouldn't securejoin work ok since when you attempt to open the path, it would fail with file does not exist?

If I set .containerignore->/etc/shadow; don't you get an error like
/ROOTDIR/etc/shadow does not exist

@flouthoc
Copy link
Collaborator Author

But wouldn't securejoin work ok since when you attempt to open the path, it would fail with file does not exist?

If I set .containerignore->/etc/shadow; don't you get an error like /ROOTDIR/etc/shadow does not exist

@rhatdan I think It does not fails cause parseDockerIgnore ignores IsNotExists https://github.com/containers/podman/blob/main/pkg/bindings/images/build.go#L751, I think its because the ignore files are not direct input by users but detected by podman internally.

@rhatdan
Copy link
Member

rhatdan commented Oct 27, 2022

OK
LGTM
But tests are failing.

@flouthoc
Copy link
Collaborator Author

@rhatdan They were flakes CI is green now.

@flouthoc flouthoc changed the title remote,build: error if .containerignore or .dockerignore is a symlink remote,build: error if .containerignore or .dockerignore is a symlink outside of buildContext Oct 27, 2022
@rhatdan
Copy link
Member

rhatdan commented Oct 27, 2022

@containers/podman-maintainers PTAL

pkg/bindings/images/build.go Outdated Show resolved Hide resolved
pkg/bindings/images/build.go Outdated Show resolved Hide resolved
@flouthoc flouthoc requested a review from mheon October 27, 2022 19:44
@flouthoc flouthoc force-pushed the remote-ignore-symlink branch 2 times, most recently from 17a6bd0 to f9ae030 Compare October 28, 2022 04:45
@eriksjolund
Copy link
Contributor

I tried out f9ae030
It's possible to detect that the directory /etc/testdir exists on the host (that is outside of the context directory).
The build fails when the directory does not exist. I don't know if this matters or not.

$ pwd
/home/test/srcdir
$ ls -la
total 8
drwxr-xr-x. 2 test test   62 Oct 28 07:31 .
drwx------. 8 test test 4096 Oct 28 07:24 ..
-rw-r--r--. 1 test test   13 Oct 28 07:30 Dockerfile
lrwxrwxrwx. 1 test test   42 Oct 28 07:22 .dockerignore -> /etc/testdir/../../proc/self/cwd/emptyfile
-rw-r--r--. 1 test test    0 Oct 28 07:21 emptyfile
$ cat Dockerfile
FROM scratch
$

case 1: The directory /etc/testdir exists

$ ls -ld /etc/testdir
drwxr-xr-x. 2 root root 6 Oct 28 07:32 /etc/testdir
$ pwd
/home/test/srcdir
$ podman --remote build --no-cache .
STEP 1/1: FROM scratch
COMMIT
--> 1ef5892c2c0
1ef5892c2c024279067db4deba42eb22e528d4876ddc9bc084294b2363aff67d
$

case 2: The directory /etc/testdir does not exist

$ ls -ld /etc/testdir
ls: cannot access '/etc/testdir': No such file or directory
$ pwd
/home/test/srcdir
$ podman --remote build --no-cache .
Error: unable to resolve symlink /home/test/srcdir/.dockerignore: lstat /etc/testdir: no such file or directory
$

about the Podman version

$ podman version
Client:       Podman Engine
Version:      4.3.0-dev
API Version:  4.3.0-dev
Go Version:   go1.18.6
Git Commit:   f9ae0309e4213bc9094054bde0249c17bd681736
Built:        Fri Oct 28 07:52:08 2022
OS/Arch:      linux/amd64

@flouthoc
Copy link
Collaborator Author

@eriksjolund That was one of the reason to mask this error #16315 (comment) , I think we should mask this error.

@flouthoc
Copy link
Collaborator Author

@eriksjolund Try again plz.

@eriksjolund
Copy link
Contributor

@eriksjolund Try again plz.

@flouthoc I'm not able to do that right now but I could check during the weekend. I'm also a bit curious to investigate if there is any way to combine github.com/cyphar/filepath-securejoin with some additional checks in parseDockerIgnore().

Another thing: I think it would be more secure to use the resolved path resolvedPath

resolvedPath, _ := filepath.EvalSymlinks(path)

when reading the file. The symlinks might have changed after the check (TOCTOU).

@eriksjolund
Copy link
Contributor

I think it would be good to aim for docker buildkit compatibility (i.e. running DOCKER_BUILDKIT=1 docker build ...)

It looks like docker resolves a .dockerignore symlink as if the build context directory is the root of the file system.

$ docker --version
Docker version 20.10.17, build 100c701
$ pwd
/Users/test/dir
$ ls -la
total 16
drwxr-xr-x  7 test  test  224 Oct 28 20:04 .
drwxr-xr-x  3 test  test   96 Oct 28 19:42 ..
lrwxr-xr-x  1 test  test    7 Oct 28 20:04 .dockerignore -> /ignore
-rw-r--r--  1 test  test   41 Oct 28 19:42 Dockerfile
-rw-r--r--  1 test  test    0 Oct 28 19:42 a1
-rw-r--r--  1 test  test    0 Oct 28 19:42 a2
-rw-r--r--  1 test  test    3 Oct 28 20:03 ignore
$ cat Dockerfile
FROM docker.io/library/alpine
COPY / /dir
$ cat ignore
a1
$ DOCKER_BUILDKIT=1 docker build -q --no-cache -t test .
sha256:4eda78871f8fe0e92e4190fdbe27477b5e7e7cb74d860e8e3e721e38a24523eb
$ docker run --rm test ls /dir
Dockerfile
a2
ignore
$ ln -sf ../ignore .dockerignore
$ ls -la
total 16
drwxr-xr-x  7 test  test  224 Oct 28 20:04 .
drwxr-xr-x  3 test  test   96 Oct 28 19:42 ..
lrwxr-xr-x  1 test  test    7 Oct 28 20:14 .dockerignore -> ../ignore
-rw-r--r--  1 test  test   41 Oct 28 19:42 Dockerfile
-rw-r--r--  1 test  test    0 Oct 28 19:42 a1
-rw-r--r--  1 test  test    0 Oct 28 19:42 a2
-rw-r--r--  1 test  test    3 Oct 28 20:03 ignore
$ DOCKER_BUILDKIT=1 docker build -q --no-cache -t test .
sha256:9f77e227de43139bba6edcca4543909d39efca7bd410f641b2facc0655bf5a01
$ docker run --rm test ls /dir
Dockerfile
a2
ignore
$

@eriksjolund
Copy link
Contributor

@flouthoc with your new commit 4b242b2 the error message became

Error: /home/test/srcdir/.dockerignore cannot be a symlink outside of build context

instead of the previous result from #16315 (comment)

Error: unable to resolve symlink /home/test/srcdir/.dockerignore: lstat /etc/testdir: no such file or directory

About using github.com/cyphar/filepath-securejoin

from #16315 (comment)

In such cases since path does not exists so no error is relyed to user it just silently ignores the .containerignore or .dockerignore file without notifying the user.

One alternative could be to use filepath-securejoin and at the same time introduce new a command-line option for allowing .dockerignore to be a symlink.
Users using a symlink .dockerignore would then be surprised by a failing build instead of a non-working .dockerignore when they upgrade the Podman version.
After a few Podman releases, allowing symlink .dockerignore could be made the default setting.

A side-note regarding Buildkit: At first sight it seems that filepath-securejoin behaves in the same way as Buildkit regarding following symlinks. That conclusion is just drawn from the two Buildkit tests in
#16315 (comment)
(In other words, the behaviour might differ when looking more closely)

About using filepath.EvalSymlinks(path)

@flouthoc 4b242b2 (that is using filepath.EvalSymlinks(path)) is fine by me. Paths outside of the build context directory may be traversed but I think that commit changes the current Podman functionality the least.

@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 22, 2023
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 23, 2023
@flouthoc
Copy link
Collaborator Author

I think this should become green now.

@rhatdan
Copy link
Member

rhatdan commented Jan 24, 2023

@flouthoc are bud tests failing or are they flakes?

Drop support for remote use-cases when `.containerignore` or
`.dockerignore` is a symlink pointing to arbitrary location on host.

Signed-off-by: Aditya R <arajan@redhat.com>
@flouthoc
Copy link
Collaborator Author

I think last test is a flake.

@rhatdan
Copy link
Member

rhatdan commented Jan 26, 2023

@containers/podman-maintainers PTAL

@rhatdan
Copy link
Member

rhatdan commented Jan 26, 2023

@eriksjolund PTAL

@rhatdan
Copy link
Member

rhatdan commented Jan 27, 2023

@vrothberg @mheon PTAL

@rhatdan
Copy link
Member

rhatdan commented Jan 29, 2023

@giuseppe @flouthoc @vrothberg PTAL

@eriksjolund
Copy link
Contributor

(feedback from reading the code)

It looks like the resulting excludes from ParseDockerignore may be dependent on the order of the items in the array containerfiles. I'm thinking of the situation when podman build --file containerfile1 --file containerfile2 . is used and at the same time the files containerfile1.containerignore and containerfile2.containerignore exist. Maybe this is expected behavoiur.

@flouthoc
Copy link
Collaborator Author

@eriksjolund Yes this is expected and keeping the original behavior but if it needs to be changed I think it should be part of a new issue.

The original issue is also fixed at buildah end here so this PR only makes sure that we dont tar up symlink from podman-remote : containers/buildah#4468

@eriksjolund
Copy link
Contributor

@eriksjolund Yes this is expected and keeping the original behavior but if it needs to be changed I think it should be part of a new issue.

SGTM

Yesterday I tried out local (i.e. non-remote) Podman and got different results when using either
the filename

.dockerignore

or the filename

Dockerfile.dockerignore

If the file Dockerfile.dockerignore is a symlink pointing to for instance ../testfile,
local podman will use ../testfile

If I repeat the experiment but use the filename .dockerignore instead of the filename Dockerfile.dockerignore, in other words

$ rm Dockerfile.dockerignore
$ ln -s ../testfile .dockerignore

local podman will not use ../testfile

I think restricting local podman would be good but the scope for this PR is the remote case.

@sanmai-NL
Copy link
Contributor

How is 'an arbitrary location outside the build context' defined? A symlink that resolves to a path outside the build context?

@Mingli-Yu
Copy link

Does the issue resolved in podman 4.4.2? Thanks!

@rhatdan
Copy link
Member

rhatdan commented Mar 27, 2023

@flouthoc @eriksjolund is this ready to merge?

@eriksjolund
Copy link
Contributor

@flouthoc @eriksjolund is this ready to merge?

LGTM

@rhatdan
Copy link
Member

rhatdan commented Mar 28, 2023

/lgtm

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Mar 28, 2023
@openshift-merge-robot openshift-merge-robot merged commit c8eeab2 into containers:main Mar 28, 2023
@github-actions github-actions bot added the locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments. label Sep 4, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 4, 2023
@lsm5
Copy link
Member

lsm5 commented Dec 4, 2023

/cherrypick v4.4.1-rhel

@openshift-cherrypick-robot
Copy link
Collaborator

@lsm5: new pull request created: #20901

In response to this:

/cherrypick v4.4.1-rhel

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. kind/api-change Change to remote API; merits scrutiny lgtm Indicates that a PR is ready to be merged. locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments. release-note
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants