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

file deleted regardless of permission #5944

Closed
1 of 3 tasks
jsgro opened this issue Sep 2, 2021 · 8 comments
Closed
1 of 3 tasks

file deleted regardless of permission #5944

jsgro opened this issue Sep 2, 2021 · 8 comments

Comments

@jsgro
Copy link

jsgro commented Sep 2, 2021

  • I have tried with the latest version of Docker Desktop
  • I have tried disabling enabled experimental features
  • I have uploaded Diagnostics
  • Diagnostics ID:

Expected behavior

When sharing a volume folder on the Macintosh using -v file permissions should be respected i.e. rm should not remove files without confirmation when user is NOT root.

Actual behavior

Even with permission set to -r--r--r-- and owned by non-root user file is deleted by rm without warning.
THis is also true if permissions are set from the Macintosh side.

Information

The problem is reproducible on Mac with: Ubuntu (20.04), CentOS (7) and Alpine (latest)
On Ubuntu and CentOS user can be added with useradd and a password set to login with command passwd.
On Alpine, files can be edited to allow new, unprivileged user by editing /etc/passwd, /etc/shadow, and /etc/group a password set to login with command passwd.

  • macOS Version: 10.14.6 (I also tried on 10.15.7 Catalina with same effect)
  • Intel chip or Apple chip: 3.5 GHz Intel Core i7
  • Docker Desktop Version: 3.6.0 5487

Steps to reproduce the behavior

Docker images used:

ubuntu 20.04 1318b700e415 5 weeks ago 72.8MB
centos 7 8652b9f0cb4c 9 months ago 204MB
alpine 3.10 965ea09ff2eb 22 months ago 5.55MB

How to reproduce. Show below for the Ubuntu docker (but it is the same effect for all 3.)

  1. On the Mac side in directory called e.g. Test
$ cat >  note.txt
this is a note
# Ctrl D to get out
$ chmod -w note.txt 
$ ls -l
total 8
-r--r--r--  1 jsgro  AD\Domain Users  15 Sep  2 12:26 note.txt
  1. Start Docker instance:
$ docker run -it --rm -v ${PWD}:/home ubuntu
root@fbe524440738:/# useradd dcuser 
root@fbe524440738:/# passwd dcuser
New password: 
Retype new password: 
passwd: password updated successfully
root@fbe524440738:/# login dcuser
Password: 
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.10.47-linuxkit x86_64)
[... truncated ]
No directory, logging in with HOME=/

$ grep dcuser /etc/passwd 
dcuser:x:1000:1000::/home/dcuser:/bin/sh
$ cd /home

Now verify presence and permissions of file:

$ ls -l
total 4
-r--r--r-- 1 dcuser dcuser 15 Sep  2 17:51 note.txt

Verify that permission prevents overwriting of file with a > redirect command: indeed it is not allowed.

$ ls > note.txt 
-sh: 8: cannot create note.txt: Operation not permitted

Now use rm to remove file:

$ rm note.txt
$ 

FILE IS DELETED and there is not warning. NOTE that the "no write" permission was set from the Mac.

CONCLUSION: User files on the Mac could be deleted.

NOTE: I tried best I could to reproduce this on Windows but the appropriate
response rm: remove write-protected regular file 'note.txt'? y was given.

Therefore this appears to be a specific Mac issue.

@jsgro
Copy link
Author

jsgro commented Sep 2, 2021

I just updated to Docker 4.0.0.12

I can reproduce this again exactly as described.
As stated, the same occurs on Catalina macOS 10.15.7

@djs55
Copy link
Contributor

djs55 commented Sep 3, 2021

@jsgro thanks for the report.

Generally on Unix the ability to rm a file depends on whether the user has write access to the parent directory, not write access to the file itself (more discussion and links here: https://unix.stackexchange.com/questions/48579/why-can-rm-remove-read-only-files ). Attempting to write to a readonly file (ls > note.txt) should fail with EPERM, printed as Operation not permitted.

The prompt (rm: remove write-protected regular file 'note.txt'?) comes from the /bin/rm program itself, rather than the filesystem. In alpine (for example) the default /bin/rm is busybox and here is the check: https://git.busybox.net/busybox/tree/libbb/remove_file.c#n85

	/* !ISDIR */
	if ((!(flags & FILEUTILS_FORCE)
	     && access(path, W_OK) < 0
	     && !S_ISLNK(path_stat.st_mode)
	     && isatty(0))
	 || (flags & FILEUTILS_INTERACTIVE)
	) {
		fprintf(stderr, "%s: remove '%s'? ", applet_name, path);
		if (!bb_ask_y_confirmation())
			return 0;
	}

so the user is prompted for files (!ISDIR)

  • if the user hasn't supplied -f to "force"
  • if stdin is a TTY (and probably attached to a user)
  • if the file isn't a symlink
  • and the kernel believes it has write access to the file: access(path, W_OK)

It's the write access check which is causing the issue. We use Linux FUSE to mount the host filesystem. There are 2 permission models: https://elixir.bootlin.com/linux/latest/source/fs/fuse/dir.c#L1197 . We delegate the permission checks to the server (by not setting default_permissions) because we want to avoid the situation where a user has a writable file on the host but can't get Linux to write to it because Linux believes the file owner/group is different. In this mode access(path, W_OK) will invoke the fuse_access API https://elixir.bootlin.com/linux/latest/source/fs/fuse/dir.c#L1160 .

However we also care about performance. Many filesystem options performed by Linux will be prefixed by a fuse_access call, which doubles the numbers of RPCs to the host in these workloads. These access results are really just hints, as the real access check has to be done within the write / open / rm call, since the permissions can change between the calls. Therefore we also disable FUSE_ACCESS on the server side by returning ENOSYS so no_access is set here https://elixir.bootlin.com/linux/latest/source/fs/fuse/dir.c#L1168 . The result is that Linux assumes access returns success, then tries the real operation to see whether it succeeds or not. The host does the access control against the real file permissions.

So it's a combination of

  • wanting to prevent spurious user/group permission errors in Linux; and
  • wanting to improve performance

results in the loss of the access hint and therefore the prompt from /bin/rm. Unfortunately we can't easily fix this without introducing other problems.

However all is not lost; if you would like 100% native Linux access control checks, you can store your data in a "named volume" which resides inside the Linux filesystem. For example:

docker volume create my-code
docker run -v my-code:/mnt alpine ls /mnt

Another possibility is to use "dev environments" https://docs.docker.com/desktop/dev-environments/ which store the code in Linux (so 100% native filesystem semantics) while also allowing you to seamlessly access everything from your IDE (as well as push/pull the environment to share it with colleagues etc)

@thaJeztah
Copy link
Member

Generally on Unix the ability to rm a file depends on whether the user has write access to the parent directory, not write access to the file itself (more discussion and links here: https://unix.stackexchange.com/questions/48579/why-can-rm-remove-read-only-files ).

Good point, I forgot about that one. I think you'll see the same behavior directly on Linux;

FROM alpine
RUN mkdir -p /test/readonly-dir /test/writable-dir \
 && chmod -R 0755 /test/readonly-dir \
 && chmod -R 0777 /test/writable-dir \
 && echo "file1" > /test/readonly-dir/readonly-file1.txt \
 && echo "file2" > /test/readonly-dir/readonly-file2.txt \
 && echo "file3" > /test/writable-dir/readonly-file3.txt \
 && echo "file4" > /test/writable-dir/readonly-file4.txt \
 && chmod 0555 /test/*/*.txt
RUN ls -laR /test/*
USER 1000:1000
RUN rm /test/readonly-dir/readonly-file1.txt || true
RUN rm /test/writable-dir/readonly-file3.txt || true
RUN echo "update" > /test/readonly-dir/readonly-file2.txt || true
RUN echo "update" > /test/writable-dir/readonly-file4.txt || true
docker build --no-cache --progress=plain .

#6 [3/7] RUN ls -laR /test/*
#6 sha256:02c470a2da32404fde66a1817ada4962acca703499f8f141022e9a3f9526a92b
#6 0.256 /test/readonly-dir:
#6 0.257 total 16
#6 0.257 drwxr-xr-x    2 root     root          4096 Sep  3 11:44 .
#6 0.257 drwxr-xr-x    4 root     root          4096 Sep  3 11:44 ..
#6 0.257 -r-xr-xr-x    1 root     root             6 Sep  3 11:44 readonly-file1.txt
#6 0.257 -r-xr-xr-x    1 root     root             6 Sep  3 11:44 readonly-file2.txt
#6 0.257
#6 0.257 /test/writable-dir:
#6 0.257 total 16
#6 0.257 drwxrwxrwx    2 root     root          4096 Sep  3 11:44 .
#6 0.257 drwxr-xr-x    4 root     root          4096 Sep  3 11:44 ..
#6 0.257 -r-xr-xr-x    1 root     root             6 Sep  3 11:44 readonly-file3.txt
#6 0.257 -r-xr-xr-x    1 root     root             6 Sep  3 11:44 readonly-file4.txt
#6 DONE 0.3s

#7 [4/7] RUN rm /test/readonly-dir/readonly-file1.txt || true
#7 sha256:0b9cc29596617a08bc27179723a26b7ef7146c2bf26533be8226a3b392450cb8
#7 0.275 rm: can't remove '/test/readonly-dir/readonly-file1.txt': Permission denied
#7 DONE 0.3s

#8 [5/7] RUN rm /test/writable-dir/readonly-file3.txt || true
#8 sha256:a7d587258ab4a4c51eeb2424f855cbb3e768be41a9a54e213bf35781b3b1e553
#8 DONE 0.3s

#9 [6/7] RUN echo "update" > /test/readonly-dir/readonly-file2.txt || true
#9 sha256:833daf528566a591cc8714bf547950c2493fa080517ea041811e2c63681c41b6
#9 0.279 /bin/sh: can't create /test/readonly-dir/readonly-file2.txt: Permission denied
#9 DONE 0.3s

#10 [7/7] RUN echo "update" > /test/writable-dir/readonly-file4.txt || true
#10 sha256:07be4ad9024cc5a12c9adfecb6033fd82283b761c3e3acb475357cafa4952e2d
#10 0.257 /bin/sh: can't create /test/writable-dir/readonly-file4.txt: Permission denied
#10 DONE 0.3s

@jsgro
Copy link
Author

jsgro commented Sep 3, 2021

Thank you @djs55 and @thaJeztah for the detailed explanations. And thanks for the Stack Exchange link with lots more explanations.

Perhaps I should have phrased it differently. It also makes sense that Directory permissions set as read-only would make a big difference. (Perhaps this is why in defunct VAX/VMS they had 4 permissions: RWED where E was the Edit privilege, considered separate to read/write.)

While rm can remove the file, on the Mac it does not even ask if it is OK.
On Docker-on-Windows, as well as actual CentOS7, there is a question that is asked
rm: remove write-protected regular file ‘note.txt’? and if the answer is y then it will be removed.

But on the Docker-Mac it does not even ask the question... so a command such as rm -r * would be quite deleterious.

I just wanted to point that out... that perhaps Mac users should be made aware of this "danger" in case they run Docker as a Non-Root user thinking that it is "safe" or "safer" (as obviously the default root user damages can be perhaps even greater.)

@thaJeztah
Copy link
Member

I think that's configurable, and may be set up on a desktop install of some linux distros, but likely not the default, which is what most containers use; https://stackoverflow.com/a/30208934

(answering from my phone, so haven't tried if that's what you mentioned ☺️)

@jsgro
Copy link
Author

jsgro commented Sep 3, 2021

Thanks @thaJeztah - Wow from your phone!

Yes, rm -i will ask the question. But casual users may not even know that and will likely use rm and on a Mac it will remove the file immediately without asking, which is different from "expected behavior based on Linux experience."

With -r--r--r-- or even just -r-------- permissions the expected behavior is to ask the "are you sure question" simply based on permissions. This behavior occurs on all Linux distributions that I have tried, and also in Docker-Widows.

On Docker-Mac the behavior is not what one would expect from experience with Linux/bash elswhere and that is the point I wanted to make. Perhaps users should be warned that it can happen.

@jsgro
Copy link
Author

jsgro commented Sep 7, 2021

I successfully tried the @djs55 docker volume commands:

docker volume create my-code
docker run -v my-code:/mnt alpine ls /mnt

I did see the "normal" behavior when using rm with the readd-only permissions.
I could list the volume my-code within the Docker Desktop as well as by the command line.

my-code is mounted to /mnt within the container.
I have not figure out to use such a my-code volume to be a shared volume already containing data, e.g. for an analysis or large files, or lots of files. But it might be possible.

I found a very good blog article about volumes: https://blog.container-solutions.com/understanding-volumes-docker

But I have not found the answer there about mounting a "docker volume" such as my-code with -v to share a Mac volume. Since this was not the main topic I'll close this comment thread. Thanks again for your participation.

@docker-robott
Copy link
Collaborator

Closed issues are locked after 30 days of inactivity.
This helps our team focus on active issues.

If you have found a problem that seems similar to this, please open a new issue.

Send feedback to Docker Community Slack channels #docker-for-mac or #docker-for-windows.
/lifecycle locked

@docker docker locked and limited conversation to collaborators Oct 7, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants