buildkit icon indicating copy to clipboard operation
buildkit copied to clipboard

Bug: Improper permissions handling on directories using `–chmod` in COPY command

Open l-qing opened this issue 8 months ago • 8 comments

Contributing guidelines and issue reporting guide

Well-formed report checklist

  • [x] I have found a bug that the documentation does not mention anything about my problem
  • [x] I have found a bug that there are no open or closed issues that are related to my problem
  • [x] I have provided version/information about my environment and done my best to provide a reproducer

Description of bug

Bug description

The command COPY --chmod=640 foo.bar /non-existent/path/foo.bar copies the foo.bar file to /non-existent/path/foo.bar, but it also sets the directory /non-existent/path/ permissions to 640. Prior to fixing #4945, the permissions for /non-existent/path/ were 755.

If I want users to access /non-existent/path/foo.bar, I now have to modify the above COPY command to COPY --chmod=750 foo.bar /non-existent/path/foo.bar, which gives the foo.bar file executable permissions. This outcome does not align with my expectations.

I expect that when creating a directory, regardless of the file permissions specified by the user, a -x permission should be set.

Reproduction

  • Take this Dockerfile
FROM busybox
COPY --chown=daemon:daemon --chmod=640 foo.bar /non-existent/path/foo.bar
  • touch foo.bar
  • DOCKER_BUILDKIT=1 docker build -t bla .
  • docker run --entrypoint /bin/sh --user=daemon --rm -it bla -c "ls /non-existent/path/foo.bar"

Result:

ls: /non-existent/path/foo.bar: Permission denied

Version information

buildkit: v0.21.0

l-qing avatar Apr 23 '25 13:04 l-qing

@thaJeztah wdyt?

tonistiigi avatar Apr 23 '25 18:04 tonistiigi

So I ran in the same issue, I had initially --chmod=444, but now all the implicitly created directories also have only read permissions. This means I cannot access any file inside the folder.

My workaround for the moment is to write --chmod=ugo=rX which I did not find documented, but I found in a github issue. For this the permissions of the implicitly created folders are still 755

kamulos avatar May 07 '25 12:05 kamulos

Hmm, right, interesting; so IIUC, the original issue that was fixed (and introduced this issue) was that the chmod didn't apply to directories;

  • https://github.com/moby/buildkit/issues/4945

The report was slightly ambiguous, but re-reading it, I think it was mostly for the second case mentioned;

COPY --chown=1:1 --chmod=750 bla /bla

Where in the above, bla was a directory, so the expectation was to copy the directory to /bla inside the image and set its permissions; it's a bit ambiguous on what the expectations is for files inside the directory (i.e., if the chmod should be recursive?).

I guess to some extent it could be a match to how install (install -D) would handle this? Existing directories should probably not be touched (don't think it currently does?) and use default permissions;

  -g, --group=GROUP   set group ownership, instead of process' current group
  -m, --mode=MODE     set permission mode (as in chmod), instead of rwxr-xr-x
  -o, --owner=OWNER   set ownership (super-user only)
touch foo.bar
install -m 0444 foo.bar /non-existent/path/foo.bar
install: cannot create regular file '/non-existent/path/foo.bar': No such file or directory

install -D -m 0444 foo.bar /non-existent/path/foo.bar
ls -la /non-existent/path/foo.bar
-r--r--r-- 1 root root 0 May  7 13:05 /non-existent/path/foo.bar
ls -la /non-existent/path/
total 8
drwxr-xr-x 2 root root 4096 May  7 13:05 .
drwxr-xr-x 3 root root 4096 May  7 13:05 ..
-r--r--r-- 1 root root    0 May  7 13:05 foo.bar

ls -la /non-existent/
total 12
drwxr-xr-x 3 root root 4096 May  7 13:05 .
drwxr-xr-x 1 root root 4096 May  7 13:05 ..
drwxr-xr-x 2 root root 4096 May  7 13:05 path
mkdir -p /existing/path/
chmod -R 0700 /existing
install -D -m 0444 foo.bar /existing/path/foo.bar

ls -la /existing/path/foo.bar
-r--r--r-- 1 root root 0 May  7 13:06 /existing/path/foo.bar

ls -la /existing/path/
total 8
drwx------ 2 root root 4096 May  7 13:06 .
drwx------ 3 root root 4096 May  7 13:06 ..
-r--r--r-- 1 root root    0 May  7 13:06 foo.bar

ls -la /existing/
total 12
drwx------ 3 root root 4096 May  7 13:06 .
drwxr-xr-x 1 root root 4096 May  7 13:06 ..
drwx------ 2 root root 4096 May  7 13:06 path

One thing to probably account for is the COPY --parents option; should using that option preserve mode from the source? Not sure what cp does there (and what COPY --parents currently does).

thaJeztah avatar May 07 '25 13:05 thaJeztah

My workaround for the moment is to write --chmod=ugo=rX which I did not find documented, but I found in a github issue. For this the permissions of the implicitly created folders are still 755

Hm, so that's a good point as well, and maybe the correct approach? Need to think about what's the most expected thing here (we definitely should look at the documentation though; was this already in the stable syntax, @tonistiigi ?)

thaJeztah avatar May 07 '25 13:05 thaJeztah

was this already in the stable syntax,

Non-octal chmod is stable since 1.14

tonistiigi avatar May 13 '25 23:05 tonistiigi

Docs update inbound

thompson-shaun avatar May 22 '25 15:05 thompson-shaun

Is perhaps related / similar to https://github.com/moby/buildkit/issues/3602 ?

ptoman-cisco avatar Jun 04 '25 13:06 ptoman-cisco

I experience similar behavior when the parent directories exists. With COPY --chmod=644 assets/ssh_known_hosts /etc/ssh/ the permissions of the existing parent directory /etc/ssh/ are changed from 755 to 644. When I remove --chmod=644, the permissions are unchanged.

docker info Client: Version: 28.3.3 Context: default Debug Mode: false Plugins: buildx: Docker Buildx (Docker Inc.) Version: 0.26.1 Path: /usr/lib/docker/cli-plugins/docker-buildx compose: Docker Compose (Docker Inc.) Version: 2.39.2 Path: /usr/lib/docker/cli-plugins/docker-compose

Server: Containers: 5 Running: 5 Paused: 0 Stopped: 0 Images: 45 Server Version: 28.3.3 Storage Driver: zfs Zpool: RPool Zpool Health: ONLINE Parent Dataset: RPool/docker Space Used By Parent: 94166941696 Space Available: 188206002176 Parent Quota: no Compression: zstd Logging Driver: json-file Cgroup Driver: systemd Cgroup Version: 2 Plugins: Volume: local Network: bridge host ipvlan macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog CDI spec directories: /etc/cdi /var/run/cdi Swarm: inactive Runtimes: io.containerd.runc.v2 runc Default Runtime: runc Init Binary: docker-init containerd version: 75cb2b7193e4e490e9fbdc236c0e811ccaba3376.m runc version: init version: de40ad0 Security Options: seccomp Profile: builtin cgroupns Kernel Version: 6.12.41-2-lts Operating System: Arch Linux OSType: linux Architecture: x86_64 CPUs: 32 Total Memory: 125.7GiB Name: rndws01 ID: 3184585c-23f9-40f8-94b5-fa9b53e449ef Docker Root Dir: /var/lib/docker Debug Mode: false Username: lmsjk Experimental: false Insecure Registries: ::1/128 127.0.0.0/8 Live Restore Enabled: false

lms-jk avatar Sep 08 '25 11:09 lms-jk