EDIT: This post was too wordy. The posts below are more to-the-point.
There seems to be a bug in appimagetool.
If I create an AppImage with appimagetool, it doesn't work well with firejail:
$ appimagetool-x86_64.AppImage ./Dummy.AppDir
$ firejail --appimage ./Dummy.AppImage
...
/bin/bash: /run/firejail/appimage/.appimage-XXXX/AppRun: Permission denied
However, if I manually create an AppImage manually, then it works fine with firejail:
$ mksquashfs Dummy.AppDir/ squashball
$ cat runtime-x86_64 squashball >Dummy.AppImage
$ chmod a+x Dummy.AppImage
$ firejail --appimage ./Dummy.AppImage
-> it works :)
Here is an example of how to fix an AppImage from appimagehub.com in order to use it with firejail, without having to use an unnecessarily weakened sandbox.
First, download the AppImage and make it executable. For this example I'm going to use Firefox 72.0.1 available here: https://www.appimagehub.com/p/1331794/
It seems the person who created this AppImage used appimagetool, because I get the "Permission denied" error:
$ firejail --appimage ./Firefox-x86_64-20200118153954.AppImage
...
/bin/bash: /run/firejail/appimage/.appimage-13951/AppRun: Permission denied
To get past the above error, we have to re-create the AppImage without using appimagetool (for the third step below, you need the appropriate runtime from here: https://github.com/AppImage/AppImageKit/releases):
$ ./Firefox-x86_64-20200118153954.AppImage --appimage-extract
$ mksquashfs squashfs-root/ squashball
$ cat runtime-x86_64 squashball >Firefox-fixed.AppImage
$ chmod a+x Firefox-fixed.AppImage
Now the "Permission denied" error goes away:
$ firejail --appimage ./Firefox-fixed.AppImage
...
Error: Access was denied while trying to open files in your profile directory.
This new error is because firejail is using the default profile (/etc/firejail/default.profile or /usr/local/etc/firejail/default.profile depending on your distro), which is quite restrictive. In this specific case, the problem is that the default profile includes _disable-programs.inc_, which blacklists _${HOME}/.cache/mozilla_ and _${HOME}/.mozilla_.
One way to "fix" this issue by adding the --noprofile flag. However, with --noprofile you end up with a very weak sandbox. A much better solution is to specify the profile that gives your specific application only the privileges it needs. For example:
firejail --appimage --profile=/usr/local/etc/firejail/firefox.profile ./Firefox-fixed.AppImage
Now everything works :-)
Now the question is, why does the manual mksquashfs invocation work while the one in appimagetool doesn't? Do you have any idea about that? Or does the --appimage-extract somehow "fix" the file permissions? Can you compare the permissions of the files in the non-working AppImage, in the extracted AppImage, and in the repacked AppImage?
Now the question is, why does the manual
mksquashfsinvocation work while the one inappimagetooldoesn't? Do you have any idea about that?
I wish I did. I took a look at appimagetool.c but at 1300+ lines it's a bit overwhelming to someone with only a working knowledge of C. I think it will make more sense for someone familiar with that code to compare it with the manual way (2 lines) of creating an AppImage.
Can you compare the permissions of the files in the non-working AppImage, in the extracted AppImage, and in the repacked AppImage?
Sure. Let's run --appimage-extract on two versions of Dummy.AppImage (first version was created manually, second version was created from same AppDir but using appimagetool). Here are the permissions of the resulting AppDirs:
bruno@box:~/Downloads$ ls -l Dummy.AppDir.manual
total 44
-rwxr-xr-x 1 bruno staff 10872 Mar 7 10:54 AppRun
-rw-r--r-- 1 bruno staff 86 Mar 7 10:54 dummy.desktop
-rw-r--r-- 1 bruno staff 22631 Mar 7 10:54 dummy.png
drwx------ 3 bruno staff 4096 Mar 7 10:54 usr/
bruno@box:~/Downloads$ ls -l Dummy.AppDir.appimagetool
total 44
-rwxr-xr-x 1 bruno staff 10872 Mar 7 10:55 AppRun
-rw-r--r-- 1 bruno staff 86 Mar 7 10:55 dummy.desktop
-rw-r--r-- 1 bruno staff 22631 Mar 7 10:55 dummy.png
drwx------ 3 bruno staff 4096 Mar 7 10:55 usr/
As you can see, the permissions are the same when the two AppImages are _extracted_. It seems the problem has to do with permissions _inside_ the AppImage.
If we could take a look inside the AppImage without actually extracting it, then perhaps we would see a difference.
If we could take a look inside the AppImage without actually extracting it
To do this, run the AppImage. While it is still running, look in /tmp for a new hidden directory called .mount12345 or similar. This mount point stores the contents of the AppImage when mounted with FUSE. Check file permissions with ls -l /tmp/.mount12345, etc.
Thank you, @shoogle. Here you go, guys:
# Permissions of running AppImage created manually:
bruno@box:/tmp$ ls -ld .mount_dummy-S9LG9R/
drwx------ 3 bruno staff 0 Mar 7 10:54 .mount_dummy-S9LG9R//
bruno@box:/tmp$ ls -l .mount_dummy-S9LG9R/
total 33
-rwxr-xr-x 1 bruno staff 10872 Mar 7 10:54 AppRun
-rw-r--r-- 1 bruno staff 86 Mar 7 10:54 dummy.desktop
-rw-r--r-- 1 bruno staff 22631 Mar 7 10:54 dummy.png
drwx------ 3 bruno staff 0 Mar 7 10:54 usr/
# Permissions of running AppImage created from same AppDir, using appimagetool:
bruno@box:/tmp$ ls -ld .mount_Dummy-Su4Gxo/
drwx------ 3 root root 0 Mar 7 10:54 .mount_Dummy-Su4Gxo//
bruno@box:/tmp$ ls -l .mount_Dummy-Su4Gxo/
total 33
-rwxr-xr-x 1 root root 10872 Mar 7 10:54 AppRun
-rw-r--r-- 1 root root 86 Mar 7 10:54 dummy.desktop
-rw-r--r-- 1 root root 22631 Mar 7 10:54 dummy.png
drwx------ 3 root root 0 Mar 7 10:54 usr/
The Dummy.AppDir directory itself as well as all the files and directories inside of it are owned by bruno:staff. So it seems the manual AppImage creation preserves the ownerships, while appimagetool changes them to root:root. I guess firejail doesn't like the root:root ownership and gives the "Permission denied" error.
@bdantas, interesting find! The problem seems to be a combination of two things:
So it could potentially be solved by changing ownership or by changing permissions.
Now the question is, why does the manual mksquashfs invocation work while the one in appimagetool doesn't?
That isn't the question you need to answer, young padawans. There is surely a version difference, we use a very specific and patched mksquashfs. Unless you use the exact same version appimagetool comes with and compare that, how can you blame appimagetool?
Edit: also pretty sure we call mksquashfs with some parameters, whereas you don't.
Easy to explain:
We do this because we don't want our usernames to be leaked inside the AppImage.
Now, the question is: Why does Firejail have any issues with root-owned files in an AppImage?
Why does Firejail have any issues with root-owned files in an AppImage?
Does Firejail run as root? If not then it wont be able to read inside the mount point. The mount point is rwx for root and nobody else. It doesn't matter that the binaries inside are r-x for everyone if the mount point is unreadabe by anyone except root.
In order to read (or execute) a file, you need read permission for all folders above that file in addition to read (or execute) permission for the file itself.
Does Firejail run as root? If not then it wont be able to read inside the mount point. The mount point is rwx for root and nobody else. It doesn't matter that the binaries inside are r-x for everyone if the mount point is unreadabe by anyone except root.
Huh? Since when are AppImages only root-usable? That can't be true.
mksquashfs -root-owned means that the files inside the squashfs are owned by root. As are the files in your /usr on your local system. This does not mean that only root can use them, though!
The mount point is rwx for root and nobody else.
This might be the root cause. Why is it so?
Does Firejail run as root?
As far as I know it is a suid binary.
@probonopd, right, but as I said before, it is the combination of being owned by root and not having read permission for anyone except the owner that matters. Sure it's fine for them to be owned by root, but only if other users have read permission too.
Sure it's fine for them to be owned by root, but only if other users have read permission too.
Yes, but I fail to understand whether this is something that is wrong in the squashfs in the AppImage, or something that is wrong in Firejail.
Or it could be something wrong in FUSE / runtime.c.
Perhaps https://github.com/netblue30/firejail/issues/2690#issuecomment-596133932?
There is surely a version difference, we use a very specific and patched
mksquashfs. Unless you use the exact same version appimagetool comes with and compare that, how can you blame appimagetool?
I think the reasoning here is backwards. How come the random version of mksquashfs that I'm using is not causing problems, whereas the hand-picked, patched version used inside appimagetool is causing problems?
We do this because we don't want our usernames to be leaked inside the AppImage.
No need to worry about that. The username is an abstraction for humans. The real owner of my files and directories is "1000", which my system looks up in /etc/passwd and translates to bruno (it's similar to the relationship between IP addresses and domain names). On your machine, the files and directories in my manually-created AppImage would appear to be owned by you or whichever username has the uid 1000 on your machine.
I think the reasoning here is backwards. How come the random version of mksquashfs that I'm using is not causing problems, whereas the hand-picked, patched version used inside appimagetool is causing problems?
It is not the version of mksquashfs, it is that we tell mksquashfs to have all files inside the squashfs owned by root. Apparently Firejail doesn't like this.
Does Firejail run as root? If not then it wont be able to read inside the mount point.
Yes, @shoogle. The firejail executable is owned by root and has SUID bit.
@bdantas
The firejail executable is owned by root and has SUID bit.
thanks for clarifying.
We do this because we don't want our usernames to be leaked inside the AppImage.
No need to worry about that. The username is an abstraction for humans. The real owner of my files and directories is "1000"
That's true for your user account on your machine, but it's not true for other users (who would be numbered 1001, 1002, etc.) and it's not true on distros like CentOS (I think) that start the numbering at something other than 1000 for the default user.
So if the squashfs stores a user ID then it needs to be zero (i.e. root). That is unless there is a generic way to say "owned by current user" without giving an ID?
The firejail executable is owned by root and has SUID bit.
Actually, that still doesn't explain everything. Imagine this:
./virus.sh # permission denied
firejail ./virus.sh # permission granted!
Surely Firejail must refuse to run executables that don't have execute permission for the current user, otherwise it would reduce security rather than increase it!
[some distros] start the numbering at something other than 1000 for the default user.
So if the squashfs stores a user ID then it needs to be zero (i.e. root). That is unless there is a generic way to say "owned by current user" without giving an ID?
@shoogle true enough. If one needs to pick a user ID that every linux system has, I think zero (for root) is the safest bet. My point was just that probonopd's concern (other people seeing your username) does not actually happen. If I send you a tarball with files owned by bruno:bruno on my machine (my user ID is 1000 and group ID is 1000) but there is no user ID 1000 or group ID 1000 on your machine, then you'd see something like this if you ran ls -l on the files on your end:
-rw-r--r-- 1 1000 1000 99 Mar 7 00:02 foo
No "bruno" anywhere in the output.
But this is not directly relevant. Sorry for the tangent.
BTW, I don't know of a generic way to say "owned by current user". I'll look into it but am not optimistic.
Surely Firejail must refuse to run executables that don't have execute permission for the current user, otherwise it would reduce security rather than increase it!
It is right that Firejail is setuid-root, and it uses root to mount the AppImage, but in the end it executes the binary as the user, after all privileges have been dropped.
Ok, so the issue is that FUSE ignores file system permissions by default, whereas Firejail does not.
Ok, so the issue is that FUSE ignores file system permissions by default, whereas Firejail does not.
To expand on this: if anyway file system permissions are ignored in FUSE mounts, could they be more generous in order to allow Firejail to execute the binaries?
If AppImage developers want to keep everything owned by root, that's fine (it makes sense given that zero is the only universal user ID). However, short of using the --noprofile flag, root ownership will only work with firejail if the permissions on /tmp/.mount_foo-12345 are at least 755.
What sets the 700 permission on that directory? I'm beginning to suspect it may be fuse or the AppImage runtime (not actually appimagetool) because the AppImages that I create manually end up with the same 700 permissions (the manually-created ones only differ by the fact that regular, non-root user owns that directory).
What sets the 700 permission on that directory?
Is the top-level directory even inside the squashfs image? What permissions does it have there?
Or are its permissions solely determined by the mountpoint to which the squashfs image gets mounted, i.e.,
?
@probonopd Filesystem mode is not passed to the squashfs mount anymore in current Firejail (the kernel would ignore it anyway): https://github.com/netblue30/firejail/blob/dc12ce6a06ba72d7b576864d2c981b00894e872b/src/firejail/appimage.c#L121
Instead, the permissions on the mount point are determined by the AppImage root directory.
Another observation: AppImages with more generous permissions on the root directory actually _do_ exist.
I found one example in QMediathekView: https://github.com/adamreichold/QMediathekView/releases
$ ls -ld /tmp/.mount_QMediaI6N9tf/
drwxrwxr-x 3 root root 0 Feb 15 09:14 /tmp/.mount_QMediaI6N9tf/
Strange; no matter what I do the squashfs-root inside the squashfs file seemingly ends up with drwxrwxr-x root/root:
me@host:~$ mkdir -p appdir/usr/share
me@host:~$ chmod 0700 appdir/
me@host:~$ /isodevice/Applications/mksquashfs appdir/* test.sfs -root-owned
Parallel mksquashfs: Using 2 processors
Creating 4.0 filesystem on test.sfs, block size 131072.
Exportable Squashfs 4.0 filesystem, gzip compressed, data block size 131072
compressed data, compressed metadata, compressed fragments,
compressed xattrs, compressed ids
duplicates are removed
Filesystem size 0.19 Kbytes (0.00 Mbytes)
87.11% of uncompressed filesystem size (0.22 Kbytes)
Inode table size 39 bytes (0.04 Kbytes)
59.09% of uncompressed inode table size (66 bytes)
Directory table size 23 bytes (0.02 Kbytes)
85.19% of uncompressed directory table size (27 bytes)
Number of duplicate files found 0
Number of inodes 2
Number of files 0
Number of fragments 0
Number of symbolic links 0
Number of device nodes 0
Number of fifo nodes 0
Number of socket nodes 0
Number of directories 2
Number of ids (unique uids + gids) 1
Number of uids 1
root (0)
Number of gids 1
root (0)
me@host:~$ /isodevice/Applications/unsquashfs -lls test.sfs
Parallel unsquashfs: Using 2 processors
0 inodes (0 blocks) to write
drwxrwxr-x root/root 28 2020-03-08 17:36 squashfs-root
drwxrwxr-x root/root 3 2020-03-08 17:36 squashfs-root/share
We need to inspect affected AppImages using unsquashfs -lls.
Let's do this with an appimagetool-produced AppImage:
me@host:~$ wget https://github.com/AppImage/AppImageKit/releases/download/12/appimagetool-x86_64.AppImage
me@host:~$ chmod +x appimagetool-x86_64.AppImage
me@host:~$ ./appimagetool-x86_64.AppImage --appimage-offset
188392
me@host:~$ /isodevice/Applications/unsquashfs -lls -o 188392 appimagetool-x86_64.AppImage | head -n 4
Parallel unsquashfs: Using 2 processors
28 inodes (126 blocks) to write
drwxr-xr-x root/root 108 2019-05-01 23:04 squashfs-root
Now, let's try this with the Firefox AppImage mentioned by @bdantas:
me@host:~$ ./Firefox-x86_64-20200118153954.AppImage --appimage-offset
188392
me@host:~$ /isodevice/Applications/unsquashfs -lls -o 188392 Firefox-x86_64-20200118153954.AppImage | head -n 4
Parallel unsquashfs: Using 2 processors
73 inodes (1596 blocks) to write
drwx------ root/root 110 2020-01-18 16:26 squashfs-root
Here we go. So the question is: __Why is this directory drwx------?__
I created a patch for firejail's appimage.c to help us figure out what's going on when --appimage flag is used. The patch makes firejail create a mountpoint with 755 permissions, show the mountpoint's permissions right after its creation, then show mountpoint's permissions again right after AppImage is mounted. Here is the patch:
--- appimage.c-original 2019-12-29 00:57:49.000000000 -0500
+++ appimage.c 2020-03-08 20:52:32.045074951 -0400
@@ -27,6 +27,8 @@
#include <fcntl.h>
#include <linux/loop.h>
#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
static char *devloop = NULL; // device file
static char *mntdir = NULL; // mount point in /tmp directory
@@ -95,16 +97,18 @@
close(ffd);
EUID_USER();
- // creates appimage mount point perms 0700
+ // creates appimage mount point perms 0755
if (asprintf(&mntdir, "%s/.appimage-%u", RUN_FIREJAIL_APPIMAGE_DIR, getpid()) == -1)
errExit("asprintf");
EUID_ROOT();
- mkdir_attr(mntdir, 0700, getuid(), getgid());
+ mkdir_attr(mntdir, 0755, getuid(), getgid());
+ // show mountpoint permissions before mounting the appimage
+ system("ls -la /run/firejail/appimage");
EUID_USER();
// mount
char *mode;
- if (asprintf(&mode, "mode=700,uid=%d,gid=%d", getuid(), getgid()) == -1)
+ if (asprintf(&mode, "mode=755,uid=%d,gid=%d", getuid(), getgid()) == -1)
errExit("asprintf");
unsigned long flags = MS_MGC_VAL|MS_RDONLY;
if (getuid())
@@ -118,10 +122,13 @@
}
else {
fmessage("Mounting appimage type 2\n");
- if (mount(devloop, mntdir, "squashfs", flags, NULL) < 0)
+ if (mount(devloop, mntdir, "squashfs", flags, mode) < 0)
errExit("mounting appimage");
}
+ // show mountpoint permissions after mounting the appimage
+ system("ls -la /run/firejail/appimage");
+
if (arg_debug)
printf("appimage mounted on %s\n", mntdir);
EUID_USER();
Here is the result with a test AppImage created with appimagetool:
$ firejail --appimage ./Dummy-x86_64.AppImage total 0
drwxr-xr-x 3 root root 60 Mar 8 20:54 .
drwxr-xr-x 11 root root 260 Mar 8 20:26 ..
drwxr-xr-x 2 bruno bruno 40 Mar 8 20:54 .appimage-21513
Mounting appimage type 2
total 0
drwxr-xr-x 3 root root 60 Mar 8 20:54 .
drwxr-xr-x 11 root root 260 Mar 8 20:26 ..
drwx------ 3 root root 94 Mar 7 10:54 .appimage-21513
Reading profile /usr/local/stow/firejail-hacked/etc/firejail/default.profile
Reading profile /usr/local/stow/firejail-hacked/etc/firejail/disable-common.inc
Reading profile /usr/local/stow/firejail-hacked/etc/firejail/disable-passwdmgr.inc
Reading profile /usr/local/stow/firejail-hacked/etc/firejail/disable-programs.inc
** Note: you can use --noprofile to disable default.profile **
Parent pid 21513, child pid 21520
** Warning: dropping all Linux capabilities **
Child process initialized in 625.90 ms
/bin/bash: /run/firejail/appimage/.appimage-21513/AppRun: Permission denied
Parent is shutting down, bye...
AppImage unmounted
As you can see, firejail creates a mountpoint with 755 permissions as requested, but after the mount step, permissions change to 700.
Ha! I think I figured it out.
Whenever a squashfs archive is created with mksquashfs, the squashball itself ends up with 644 permissions by default. However, when the squashball is _mounted_, the mountpoint's permissions will match those of the original directory.
So, for example, if Foo.AppDir directory has 700 permissions, /tmp/.mount12345 directory will have 700 permissions. If Foo.AppDir directory has 755 permissions, /tmp/.mount12345 directory with have 755 permissions.
When used with firejail's --appimage flag, it doesn't matter that firejail creates a mountpoint with 700 permissions because after the AppImage is mounted, the mountpoint permissions will change to match those of the AppDir from which the AppImage was created.
Somebody please check my work here, but I think it's really that simple.
If all the above is true, folks need to be careful with the "others" (third digit) permissions when creating AppImages to be used with firejail. Firejail mounts the AppImage as root but then lowers provileges to those of a regular user. If regular user is in the "others" category and there are XX0 permissions on Foo.AppDir (or anywhere inside Foo.AppDir), that'll be a problem.
So appimagetool is innocent, after all. It changes ownerships to root by design, and trusts the user to make sure the permissions on Foo.AppDir and everywhere else are correct.
Come to think of it, the surprising thing is that 700 root:root doesn't cause problems when running AppImages as regular user the normal way (without firejail). Presumably FUSE does not strictly enforce permissions, while firejail does.
The one possible "improvement" to appimagetool is that perhaps it should check the permissions of Foo.AppDir. If it is XX0 (e.g., 700), it might be desirable for appimagetool to automatically run chmod 755 Foo.AppDir before running mksquashfs and also warn the user to check the files and subdirectories inside Foo.AppDir (to make sure that any XX0 permissions are intentional).
Closing this issue doesn't make sense.
I don't think firejail should be mounting the AppImage as root. I also don't think anything within mountpoints should be readable to anybody except the user who owns the AppImage. Everything else is a privacy issue.
The runtime and appimagetool don't handle this explicitly. This needs to be changed. Both, but especially the runtime, should enforce 0700.
I blame firejail for mounting AppImages the wrong way. I don't see a reason to do so.
@TheAssassin I think you are reading this issue backwards.
I don't think firejail should be mounting the AppImage as root
https://github.com/AppImage/AppImageKit/issues/1032#issuecomment-596326208 shows it doesn't matter who mounts appimage and with what permissions it's mounted. Only the file permissions made during appimage creation matter and in affected cases they are 700 as https://github.com/AppImage/AppImageKit/issues/1032#issuecomment-596225173 shown.
I also don't think anything within mountpoints should be readable to anybody except the user who owns the AppImage. Everything else is a privacy issue.
So, without firejail appimage files are root owned and have 700 permissions and you can still use them which means what you wrote above doesn't apply to appimages without firejail.
Only firejail tries to enforce access permission and it's what causes failures. Another thing is why access to publicly available binaries violates privacy, for me it doesn't make sense.
The runtime and appimagetool don't handle this explicitly. This needs to be changed. Both, but especially the runtime, should enforce 0700.
As only firejail cares about those perms, making them more strict won't help for anything but will break more appimages for firejail. If those perms would be effective then many (most?) appimages created by appimage tool will break for everyone.
I blame firejail for mounting AppImages the wrong way. I don't see a reason to do so.
As shown above, only firejail works in accordance to your expectations so it's last thing you should blame.
When AppImages are used the normal way (without firejail) they are mounted by fuse. When they are used with firejail --appimage, they are mounted with C's mount library (look at firejail's appimage.c and notice the glaring absence of "fuse" anywhere).
I think the issue from firejail's perspective is that fuse is very privilege-hungry. In order to mount AppImages the normal way with fuse when using firejail (achieved by leaving out the --appimage flag), one has to severely weaken the sandbox by using --noprofile or manually editing the default profile so that almost everything is disabled--see https://github.com/netblue30/firejail/issues/3249.
My two cents is that I think firejail's approach makes sense. The point of using firejail is to create a sandbox. I don't use firejail with all my AppImages, only with a select few. With those few, I'm happy that firejail mounts AppImages in a way that is not privilege-hungry. The only "downside" to firejail's fuse-less way of mounting is that permissions are strictly enforced and requires attention at the AppDir stage.
The root ownership issue was an AppImage decision (mksquashfs -root-owned). As I showed with my manually-created AppImages, there is no "Permissions denied" issue, _ever_, if AppImages are created manually without the "-root-owned" flag to mksquashfs. The fact that firejail mounts the appimage as root does not introduce any pitfalls--that's a red herring.
Bottomline:
It's the combination of root ownership (due to AppImage design) + sloppy permissions on AppDir (due to lack of awareness on the part of person creating the AppImage) + running AppImage with firejail as regular user + strict privilege checking (due to firejail doing its job) that causes this "Permission denied" issue. The issue is solved by paying attention to permissions at the AppDir stage (particularly the permissions of the AppDir directory itself)--no patches to either AppImage or firejail are needed.
P.S. I'm just an AppImage and firejail user (highly appreciative of both projects, I might add) trying to understand the "Permission denied" issue. Everything is clear to me now. Hopefully I've helped the community by shedding some light on the issue. Cheers!
Mounting AppImages without FUSE is always possible, and the affected AppImages will be broken for _everyone_ in this case. This issue with permissions really extends beyond Firejail.
@smithsou, I'm not sure what breakage you're talking about. If I create an AppDir with 755 permissions on all directories (including the AppDir directory itself), then create an AppImage using appimagetool, the AppImage works with or without firejail and everyone is happy.
EDIT: Thanks, Vincent43. I get the smitsohu's point. Yes, if AppDir stays as 700 then that'll cause breakage whenever the AppImage is mounted without FUSE.
He probably meant the case when AppDir stays as 700.
$ chmod 755 Foo.AppDir
$ find ./Foo.AppDir/ -type d -exec chmod 755 {} \;
$ ARCH=x86_64 appimagetool-x86_86.AppImage ./Foo.AppDir
-> crowd-pleasing Foo-x86_64.AppImage is created :)
$ ./Foo-x86_64.AppImage # works!
$ firejail --appimage --profile=/etc/firejail/foo.profile ./Foo-x86_64.AppImage # works!
If the AppImage doesn't need any special privileges (e.g., HelloWorld-x86_64.AppImage), then okay to use default.profile, which creates a very strong (strongest, if I'm not mistaken) sandbox:
$ firejail --appimage ./HelloWorld-x86_64.AppImage # works!
If I create an AppDir with 755 permissions on all directories (including the AppDir directory itself), then create an AppImage using appimagetool, the AppImage works with or without firejail and everyone is happy.
Maybe appimagetool should change all directory permissions to 755 (or even 555) when it creates the AppImage?
Yes, enforcing perms as 755 or 644 (for data) by appimagetool sounds legit.
@shoogle yes, either 755 or 555 on all directories would fine for all use cases I can think of. If this were done automatically by appimagetool before running mksquashfs, that would be great.
I'm not sure whether also changing _file_ permissions automatically would be a help or a hindrance (I'd have to think about edge cases). It would probably be safest to leave _file_ permissions up to the person (or software) creating the AppImage.
If the dir is readable but file isn't then how does it help? If restricting file permissions in appimage doesn't have any benefits then why allow it? If appimage cares about sanitizing ownership with -root-owned then it may start carrying about sane permissions too. Relying on every single appimage creator paid attention to file permissions will result in status quo.
@Vincent43, you're absolutely right. I wouldn't change special permissions (e.g., some applications such as chromium need SUID on its sandbox binary) but having appimagetool automatically change all directory _and_ file permissions to 555 before the mksquashfs step would really squash (haha) this bug.
Would it be sufficient to set the AppDir itself to 0755 or do we observe this issue with other directories as well in the "known bad" AppImages?
My "known bad" AppImages had 700 on AppDir as well as 700 on subdirectories. Changing AppDir to 755 was not enough to fix--I needed to change subdirectories as well.
I wish we knew why this is so; can we contact the author of this AppImage and ask about their setup?
I wish we knew why this is so
Could be umask setting. It determines the default permissions on newly-created directories and files. Maybe some people's systems have umask setting that causes their newly-created foo.AppDir and subdirectories to have 700 permissions.
I wish we knew why this is so
Actually, I have a better explanation: --appimage-extract sets 700 permissions on extracted directories. I caught it red-handed.
Take any "known good" AppImage and extract it using foo.AppImage --appimage-extract. In the output AppDir (named "squashfs-root") you'll find 700 permissions on the root directory as well as in all subdirectories. If you rebuild an AppImage from "squashfs-root" without changing directory permissions back to something sensible, you end up with a "known bad" AppImage.
Could be umask setting. It determines the default permissions on newly-created directories and files. Maybe some people's systems have umask setting that causes their newly-created foo.AppDir and subdirectories to have 700 permissions.
This might be an explanation. Do you know any system that sets 0700 by default?
Actually, I have a better explanation: --appimage-extract sets 700 permissions on extracted directories. I caught it red-handed.
Please provide an example so that we can reproduce. Showing that a 0755 directory inside the squashfs ends up being extracted as 0700.
This might be an explanation. Do you know any system that sets 0700 by default?
I don't know any distro that uses that default, it's just a theoretical possibility. I think the real culprit is --appimage-extract.
Please provide an example so that we can reproduce. Showing that a 0755 directory inside the squashfs ends up being extracted as 0700.
Here you go:
http://files.dantas.airpost.net/public/HelloWorld-x86_64.AppImage
http://files.dantas.airpost.net/public/Gnumeric-x86_64.AppImage
http://files.dantas.airpost.net/public/Xscreensaver-x86_64.AppImage
I was careful to set 755 permissions on all directories within these AppImages. Extract them with --appimage-extract and you'll see that all directory permissions get converted to 700.
I'd like to officially report a bug and make a feature request:
Bug: --appimage-extract changes all directory permissions to 700. This is unexpected and causes problems (if squashfs-root is used as-is to create a new AppImage, the new AppImage _must_ be mounted with fuse or else non-root user gets hit with "Permission denied"). Desired behavior: --appimage-extract should not change any permissions, so that the contents of squashfs-root match exactly what was in the original AppImage.
Feature request: When creating an AppImage, appimagetool should change permissions on AppDir's directories (root AppDir directory and all subdirectories) and files to 555 or 755 before the mksquashfs step.
When creating an AppImage, appimagetool should change permissions on AppDir's directories (root AppDir directory and all subdirectories) and files to 555 or 755 before the mksquashfs step.
Strictly speaking, appimagetool should just convert an AppDir to an AppImage without changing it. Preparing a valid AppImage is out of its scope. So maybe we should just check whether all directories inside the AppDir (and the AppDir itself) are at least 0755 and exit otherwise with a warning. Imho better than just silently changing permissions. Wdyt?
I agree. Better for appimagetool to exit with a warning than to silently change things. I'd use 0555 as the minimum directory permissions.
While on the topic of silent changes, I noticed today's commit where --appimage-extract will put 0755 permissions on all extracted directories. That will certainly fix this bug, but wouldn't it be better for the AppImage to be extracted as-is without any silent changes? Would it complicate the code too much to have permissions in squashfs-root match what's inside the AppImage?
From a security point of view, changing the directory permissions would be fine. It could be logged. However, you all seem to forget that applications should not necessarily be readable by all users. This can easily be prevented by changing the owner of the mountpoint to the user's name, and setting its permissions to 0700.
I am pretty sure @probonopd's PR doesn't improve the situation, it's still firejail which should just mount the AppImage correctly. That'd easily fix the problem. You're too focused on discussing workarounds, which decrease the overall security.
This can easily be prevented by changing the owner of the mountpoint to the user's name, and setting its permissions to
0700.
That's not correct. When a squashfs archive gets mounted, the mount point's ownership and permissions get changed to those of the squashfs archive. Try it yourself or see the result of my experiment here: https://github.com/AppImage/AppImageKit/issues/1032#issuecomment-596320595.
Having sane ownership and permissions on the squashfs archive is critical because it's what you're stuck with. You can't change them after mounting, either, because squashfs is read-only.
you all seem to forget that applications should not necessarily be readable by all users.
We're talking about publicly available appimages, what are you trying to hide and from whom?
This can easily be prevented by changing the owner of the mountpoint to the user's name, and setting its permissions to 0700
You had a proof above that neither of mountpoint owner or mount permissions doesn't matter at all so if you don't believe other people please test yourself before commenting about it again. Moreover fuse is ignoring those permissions anyway. You can make files root owned and 700 and still unpriv user can read them. There is no security gained from stricter permissions.
it's still firejail which should just mount the AppImage correctly. That'd easily fix the problem.
Firejail is the one (only) who actually cares about those appimage permissions which seems important to you. It also proves that restricted permissions doesn't make sense as they will lock out users from their appimages. Also changing it in firejail isn't easy but very complicated.
Any mountpoint's ownership is either defined by the user through options (e.g., -o owner=<uid>), or some sort of default is used. I recommend you read man fusermount, which shows the existence of the -o flag for custom options, and man mount, which lists the available options. Processes running as root can set the owner to whatever they want.
Think about what you're claiming here. Your AppImages all only break if running through firejail. The pure fact the AppImages work without firejail show it must be on them to fix the issue, to be honest. Running via the regular runtime, they just work fine. If it was an issue inside the AppImages only, why should they run when being run as regular users? The runtime, running as a regular user process, just doesn't have the permission to tamper with the mountpoint. Therefore the mountpoint is owned by the current user.
It's pretty clear why firejail, a process running as root, creates root-owned mountpoints. It's also pretty clear that it's firejail which needs to add the necessary options to make sure the mountpoints end up owned by the original users. There's surely an API for that. After all mount etc. are just frontends which expose the respective APIs for CLI usage. It's just not used.
Any change to the current appimagetool codebase is simply a workaround therefore, and won't make existing AppImages run. It's up to firejail to fix their AppImage run stuff.
Most of this discussion misses that important point. I don't have too much time at the moment, but my previous comments tried to guide you in the right direction.
Please report the issue upstream to firejail, if you haven't done so yet. It's up to them to fix their AppImage support. This will also ensure that existing AppImages keep working. @probonopd's PR would only "fix it" (well, hack around firejail's problem) for newly built AppImages.
It's pretty clear why firejail, a process running as root, creates root-owned mountpoints.
That's not correct. The mount point is owned by regular user. I proved that here: https://github.com/AppImage/AppImageKit/issues/1032#issuecomment-596320595. Please look at the comment and try to understand it. The mount point becomes root-owned because the squashfs archive inside the AppImage is root-owned.
@TheAssassin This is my last post here. I thought I was helping out by pointing out an obvious bug (--appimage-extract changes all directory permissions to 700 for no good reason). My work is done.
Happy hacking!
@bdantas please don't be offended. You are helping us a tremendous lot and I would like to say a heartfelt "Thank You!" for your investigation and bug reporting.
While on the topic of silent changes, I noticed today's commit where
--appimage-extractwill put 0755 permissions on all extracted directories. That will certainly fix this bug, but wouldn't it be better for the AppImage to be extracted as-is without any silent changes?
As I wrote in the PR,
Desired behavior:
--appimage-extractshould not change any permissions, so that the contents ofsquashfs-rootmatch exactly what was in the original AppImage.Implemented here: Uses
0755. Implementing the desired behavior is more work, feel free to send another PR to implement it. For now, I think this already improves the situation a bit.
So yes, it would complicate the code a little bit to have permissions in squashfs-root match what's inside the AppImage, but if someone sends a good PR, we will definitely consider it!
Please report the issue upstream to firejail, if you haven't done so yet.
It's long been addressed there: https://github.com/netblue30/firejail/issues/2849
My change to the runtime does _not_ fix the Firejail situation. We still don't know why the permissions of the directories _inside some_ AppImages are 0700, but the consensus seems to be that appimagetool should fix/warn about that.
yes, it would complicate the code a little bit to have permissions in
squashfs-rootmatch what's inside the AppImage
In that case, extracting to 755 directories should be fine.
@probonopd it was my pleasure to investigate. I've been a fan of AppImage for a long time :)
I think the 0700 permissions inside some AppImages comes from the bug you fixed today. In the process of creating and testing an AppImage, if at any point the creator did --appimage-extract then created a new AppImage from squashfs-root, then the 700 permissions would creep in.
For as long as "known bad" AppImages are out in the wild, please tweak your test system on appimagehub to use firejail with --noprofile flag but without the --appimage flag.
@Vincent43:
We're talking about publicly available appimages, what are you trying to hide and from whom?
You're making too many assumptions. There are proprietary AppImages out there. Even if you don't like that. There is no technical reason to, by default, expose AppImages' contents to anyone but their owner. My opinion is that all code mounting AppImages isn't taking this into account.
Moreover fuse is ignoring those permissions anyway. You can make files root owned and 700 and still unpriv user can read them. There is no security gained from stricter permissions.
That entire sentence doesn't make sense. It's provably wrong. But I don't have time for this now...
Also changing it in firejail isn't easy but very complicated.
That's not an argument not to do it. You're like, "ah, changing the car's tyre is too complicated for me, I'll rather buy a new car with suitable tyres every 6 months"...
Regarding firejail's --appimage stuff:
@bdantas I read your code snippet as well as the firejail code, and saw that it just called mount there without the required options to mount. If I don't specify the options, as your example shows, the permissions inside the AppImage are applied as-is by the kernel.
Simply put: firejail's implementation is incorrect! And that is what I'm trying to point out. If I use the kernel's mount API to mount e.g., an AppImage, like firejail does, I am in charge of ensuring the owner of the file can access the contents. Plainly mounting the AppImage, as proven by @bdantas, does not produce the intended results.
To me, it seems like most of the discussion has focused on finding out how to "solve" the current problem, assuming firejail's mounting was correct. I already said it, if running the AppImages usually works but breaks with firejail, it's worth taking a look at them at first. I've done that, and I've found a problem there.
@TheAssassin This is my last post here. I thought I was helping out by pointing out an obvious bug (--appimage-extract changes all directory permissions to 700 for no good reason).
@bdantas surely found a problem in our AppImage's runtime, @probonopd took action, even if it's not really for long-term use. (@probonopd I'm a bit angry you didn't let me comment that you should've included a comment in-source about the bug and that your change is only a temporary workaround!).
My point is not that this is per se wrong. We just need to solve the problem for existing AppImages, not only implement some kind of workaround that'll fix problems in the future.
I could start quoting a lot of misleading comments here, really. Many of you seem to think that the contents of the filesystem image deterministically define the permissions when you mount it. But really, that is just not true. Let me quote man mount:




All of these options tamper with the permissions of the contents of a filesystem (image). Even if a file is marked as writable, if mounted with ro for instance, writing fails. Even if there were suid binaries, with nosuid this is zeroed out. You see, it's not necessarily the image which defines what permissions files have and who they're owned by. It's up to the kernel whether to use them or not. By default that might make sense, but in this case, not so much.
Both ro and nosuid are, by the way, even enforced by squashfs, the latter for security reasons. If nosuid wasn't used, I could ship suid binaries in AppImages and perform root actions without e.g., sudo. Malware creators might love that, I'd call it a security nightmare.
Moreover fuse is ignoring those permissions anyway. You can make files root owned and 700 and still unpriv user can read them. There is no security gained from stricter permissions.
That entire sentence doesn't make sense. It's provably wrong. But I don't have time for this now...
That sentence is provably right. The simple fact that you, right now, are able to execute affected AppImages via FUSE proves it. Or from the FUSE man pages:
By default FUSE doesn't check file access permissions, the filesystem is free to implement it's access policy or leave it to
the underlying file access mechanism (e.g. in case of network filesystems). This option enables permission checking, reโ
stricting access based on file mode. This is option is usually useful together with the allow_other mount option.
Any mountpoint's ownership is either defined by the user through options (e.g., -o owner=
), or some sort of default is used.
Right, but the available options depend on the filesystem. In case of squashfs, mount point and file ownership are not exposed via mount*, but are properties of the filesystem.
(*) and neither via loop ioctls (edited)
So for now my conclusion is that all directories, including the top-level one, in the AppImage squashfs must be 0755, then it should work with Firejail. Use unsquashfs -ll to check the permissions.
At least this is what seems to have worked for me. But it doesn't fix the already-existing AppImages with different permissions.
It's really firejail's task to get this to work.
My point is not that this is per se wrong. We just need to solve the problem for existing AppImages, not only implement some kind of workaround that'll fix problems in the future.
I'm really skeptical about solving this until we can agree about facts (not opinions).
That's got nothing to do with opinions. You completely ignore the fact that any workaround implemented on AppImages' side will not make older AppImages work. The error is introduced by firejail, after all the AppImages work normally without it (also one of these facts). The only way to fix this in a backwards compatible manner is to fix it in firejail. Only then older AppImages will work, too.
The error is introduced by firejail
Actually, not so sure.
after all the AppImages work normally without it (also one of these facts)
Might well be that the AppImage runtime does it wrong... and it should never have worked in the first place if the permissions of the directory are not at least 0755.
Not sure though.
One question is if AppImage wants to care about the case when it is mounted without FUSE.
The only way to fix this in a backwards compatible manner is to fix it in firejail. Only then older AppImages will work, too.
That's certainly right. As outlined in https://github.com/netblue30/firejail/issues/2690#issuecomment-509901803 , it is not impossible to address this in Firejail, but is not simple either.
The runtime has no influence on the mountpoint's permission; at most appimagetool. @bdantas's bug is about the runtime not respecting the existing permissions on extraction. You (dis)improved this by changing the 0700 for a 0755. However, that's formally incorrect as well in my point of view. But it's fine for now. I would've liked a temporary workaround warning comment nearby, though.
In any case, firejail needs to implement a workaround, not we. Again: my main concern is the lack of backwards compatibility with any chmod in appimagetool. Sure, it might work around the issue just fine for every AppImage built afterwards, but you should not forget the tons of old AppImages out there...
Let me show you why changing the pure access permissions inside the AppImage's squashfs payload is completely pointless with regards to this issue anyway:
> ls -al /tmp | grep FreeCA4
[...]
drwx------ 3 root root 0 Mai 22 2019 .mount_FreeCA4W4Vxg
# from the permissions we see it should be accessible only by root, right?
# FreeCAD was started by me, though, so I have access with ls
# let's see what happens if any other user tries to access the directory
> env LC_ALL=C sudo -u nobody ls -al /tmp/.mount_FreeCA4W4Vxg/
ls: cannot access '/tmp/.mount_FreeCA4W4Vxg/': Permission denied
# FUSE resp. squashfuse prohibits access to this directory, as some
# mount option that allows all users to access the directory is both
# enabled system wide (it's a security risk!) and used on the mountpoint
# let's try another one, shall we?
# permissions:
drwxr-xr-x 3 root root 0 Mai 19 2019 .mount_OpenSC4TyTLF/
> env LC_ALL=C sudo -u nobody ls -al /tmp/.mount_OpenSC4TyTLF
ls: cannot access '/tmp/.mount_OpenSC4TyTLF*': No such file or directory
# despite being "accessible to all users", as one might put it, a
# user-started FUSE mountpoint *cannot* be accessed by anyone
# *but this user*
If firejail mounted the directory as the user who started the process, it should work without having to enable insecure FUSE options or other hackery. It's a suid binary, it can do that.
I have written FUSE filesystems before (e.g., for AppImageLauncher), therefore I was aware of this behavior with regards to this whole permissions and access system. I hope my quick demo illustrates that it's not as easy as it seems at a first glance (e.g., with ls). sudo -u nobody is your friend.
I think firejail could fix the problem by launching the FUSE process with regular user permissions (right now it's in a section with evaluated (root) permissions. That should sort out the issue permanetly.
@Vincent43 I do not claim it's necessarily easy or anything, you might have better insight there. But the conclusion that firejail have to change something is not based on opinions. Nor is it that I don't want to invest time or anything. It's purely got to do with our focus on backwards compatibility. Since firejail does not use our runtime but, well, ships its own, it's pretty clear who's in charge of the mounting behavior. As said, I think it's possible even with firejail's way of mounting.
I am happy to provide feedback for a firejail fix. Please don't hesitate to contact me.
By the way, security and privacy wise I love FUSE's (maybe not too obvious) behavior by the way. There might be various reasons why I as a user might not want third parties to be able to access the AppImages I am using. By both putting them into some protected directory I own (0700) and FUSE's default behavior of not letting other users access my own mountpoints, I can prevent third parties of accessing them.
This is especially handy on multi-user systems, e.g., university or school networks, enterprise environments, ... It's not
P.S.: The FUSE option I was talking about is user_allow_other. It is a security risk to enable this, please do NOT do so unless you know what you are doing!
@smitsohu thank you for the cross-link over to firejail's tracker. @crass shows how it has to be done in firejail.
@probonopd and I just had a small discussion about why we have this issue. I think it's fair to say firejail has made the assumption that contents are world readable. That assumption isn't based on the AppImage specification, I don't know why it was made. Surely our spec is incomplete there (something we intend to fix with an upcoming type 3). FUSE mounting works, though.
We could implement the means to make this assumption made by firejail work, sure. But strictly speaking, our AppImages aren't violating any specification, and our tools are not the only ones which create AppImages. So even if we fix that, you'd have to reach out to all the other projects who create AppImages other than appimagetool to add this chmod stuff, then specify the permissions in the spec. And, what's inacceptable to me, you'd basically declare all existing AppImages to be broken.
I hope the firejail team finds a way to mount as regular user through FUSE. As said, happy to provide feedback, please don't hesitate to reach out to us!
@smitsohu I missed your question. Yes, I think we should care about this use case. But maybe not for type 2. The solution for root mounting via the kernel API is IMO to then mount AppImages with the -o uid=... -o gid=.... I am not sure if those options work with FUSE/squashfuse.
Can you please open an issue?
One question is if AppImage wants to care about the case when it is mounted without FUSE.
The solution for root mounting via the kernel API is IMO to then mount AppImages with the -o uid=... -o gid=.... I am not sure if those options work with FUSE/squashfuse.
That's a FUSE mount again :smile:
Can you please open an issue?
Will do.
That's got nothing to do with opinions
Well, that's my point. How FUSE works is _the fact_ and you claim it works differently than everyone else in this topic claims it does.
Your example shows that it doesn't matter if fuse dir has 700 or 755 permissions - it still can be opened by invoking user and can't be opened by other user. Why do you have such problem with 755 permissions then?
Because this does not help with existing AppImages. In not a single one of your comments you have picked up this problem. Backwards compatibility is important to our project. You keep ignoring this point over and over, and I am not sure why...
Apart from that; does firejail's mounting provide the same protection FUSE does? If I would run a (755) AppImage, could everyone just access the files?
No, you repeatedly claimed that:
You're too focused on discussing workarounds, which decrease the overall security.
Only now, when you proved yourself wrong then you switched argument to _Because this does not help with existing AppImages_ and claim everyone was ignoring it all the time. Sorry but if you want a productive discussion here, you (and everyone else) need to be honest.
The runtime should never have been changing directory permissions to 700 without any notice to the user. That was clearly a bug and I'm glad it's fixed. Thank you.
Ideally, appimage extraction via _--appimage-extract_ would behave similarly to the _unsquashfs_ utility, which extracts squashfs archives without changing any permissions. According to @probonopd, achieving this would overly complicate the runtime's source code, which is why hardwired directory permissions are used. I think hardwiring 755 is sensible and is a much better workaround (in the sense that it "works around" having to replicate unsquashfs functionality in the AppImage runtime) than using 700.
My two cents about backwards compatibility and "known bad" AppImages in the wild: I'd change nothing in firejail.
The ideal way to run an AppImage in firejail is to fix known-bad AppImages then use firejail's own "runtime" (C library mount), which requires fewer privileges than FUSE. The _--appimage_ flag is what tells firejail to use its own runtime. This works:
$ firejail --appimage KnownGood.AppImage
If AppImage is known bad and user does not know how to fix the permissions issue, it is already possible to "properly" (to use TheAssassin's expression) mount AppImages in firejail using the AppImage runtime and FUSE: Simply don't use the _--appimage_ flag. The downside is that FUSE is privilege-hungry and needs more privileges than what's granted by firejail's default profile. So this works:
$ firejail --noprofile KnownBad.AppImage
Apart from that; does firejail's mounting provide the same protection FUSE does? If I would run a (755) AppImage, could everyone just access the files?
The answer is actually yes. Looking at early versions of the code, there seems to have been a misconception that -o uid=... -o gid=... mount options would have any effect (they turned out to have not).
This in itself however is not necessarily an argument pro FUSE, as fixing access permissions within Firejail's current code is probably not a big deal (e.g. by adding per user directories).
Here is another example that gives "permission denied" when running with Firejail.
And sure enough, its top-level directory is root-owned and permissions are set not to be readable by anyone else, as can be shown with unsquashfs:
me@host:~$ /isodevice/Applications/unsquashfs -o 188456 -ll /home/me/Downloads/Gridsync-Linux.AppImage /
Parallel unsquashfs: Using 2 processors
0 inodes (0 blocks) to write
drwx------ root/root 112 2020-07-24 23:05 squashfs-root
References:
Most helpful comment
In that case, extracting to
755directories should be fine.@probonopd it was my pleasure to investigate. I've been a fan of AppImage for a long time :)
I think the
0700permissions inside some AppImages comes from the bug you fixed today. In the process of creating and testing an AppImage, if at any point the creator did--appimage-extractthen created a new AppImage fromsquashfs-root, then the700permissions would creep in.For as long as "known bad" AppImages are out in the wild, please tweak your test system on appimagehub to use
firejailwith--noprofileflag but without the--appimageflag.