igo95862 / bubblejail

Bubblewrap based sandboxing for desktop applications
250 stars 17 forks source link

Add encrypted 'home' support #37

Open donob4n opened 2 years ago

donob4n commented 2 years ago

Description

Hi, I'm currently using 'gocryptfs' and I feel that it could be nicely integrated into bubblejail, specially for automatically umount it after closing the sandbox (maybe it could auto close due inactivty?).

I open this issue for share some comments about it. I would like to do some draft for gocrypts having in mind that I should be easily integrated with other alternatives.

Gocrypts (and most encrypt fs) uses a folder for save the data encrypted, so it could just be saved on '~/.local/share/bubblejail/instances/instance/secret' and then mounted on its respective 'home'.

igo95862 commented 2 years ago

Hello.

What do you think the UI/UX would be?

If a user tries to run the instance that has an encrypted home they should be prompted a password entry?

Or should the password be stored somewhere? Kind of not very useful if the password would be stored in the instance configuration directory.

There is actually a freedesktop-secrets API that can be used to securely store passwords. It can be implemented to store the directories keys. I made the binds for it with my D-Bus library that I plan to integrate but I have heard that Alpine Linux does not like D-Bus.

I had an idea of some generic mechanism of instance home preprocessors. For example, creating a BtrFs subvolme.

igo95862 commented 2 years ago

BTW there is Open a blank issue. option when creating a new issue but it is kind of small and hard to notice so I guess I will add a template Suggestion/Idea

igo95862 commented 2 years ago

Keepassxc implemented the secrets API in 2.5.0: https://keepassxc.org/blog/2019-10-26-2.5.0-released/

(so it is not GNOME/KDE specific)

donob4n commented 2 years ago

Hello.

What do you think the UI/UX would be?

If a user tries to run the instance that has an encrypted home they should be prompted a password entry?

On creation the instance should be flagged as 'encrypted' and the password prompted for initalization. Then, on run it should check the 'encrypted' option and handle the mounting/unmounting before/after launching bwrap.

Or should the password be stored somewhere? Kind of not very useful if the password would be stored in the instance configuration directory.

There is actually a freedesktop-secrets API that can be used to securely store passwords. It can be implemented to store the directories keys. I made the binds for it with my D-Bus library that I plan to integrate but I have heard that Alpine Linux does not like D-Bus.

I think that there are two use cases for this. The first is having a last security layer in case the whole system gets compromised or stolen while it's unlocked. So having the encrypted sandbox stopped and unlock password saved outside seems mandatory.

The other use case is a secure way for share/backup some folder in public clouds like dropbox. In this scenario running it 24h or having a passworldess start seems ok.

I had an idea of some generic mechanism of instance home preprocessors. For example, creating a BtrFs subvolme.

I think that it would be nice, and probably very easy to achieve with it. But for some users maybe a more clearly UI just for encryption it's nice.

igo95862 commented 2 years ago

On creation the instance should be flagged as 'encrypted' and the password prompted for initalization.

It is possible. There is even python module to enter passwords: https://docs.python.org/3/library/getpass.html#getpass.getpass

The other use case is a secure way for share/backup some folder in public clouds like dropbox. In this scenario running it 24h or having a passworldess start seems ok.

To be honest this sounds like something backup script or software should be taking care of. There are instruction on how to run gocryptfs in reverse mode: https://wiki.archlinux.org/title/Gocryptfs#Example_using_reverse_mode

But for some users maybe a more clearly UI just for encryption it's nice.

This is what secrets API should be taking care of. Unless you think there should be another Qt application that should come with bubblejail which should popup when asks for password. But that would be very difficult to implement and maintain.

donob4n commented 2 years ago

The other use case is a secure way for share/backup some folder in public clouds like dropbox. In this scenario running it 24h or having a passworldess start seems ok.

To be honest this sounds like something backup script or software should be taking care of. There are instruction on how to run gocryptfs in reverse mode: https://wiki.archlinux.org/title/Gocryptfs#Example_using_reverse_mode

Yes, reverse mode has more sense in this scenario. Maybe it could be seen as a different bubblejail freature (remote backup?). I'm really only interested on extra protection for sensitive data, so asking for the password on each run seems better.

Maybe it's out of bubblejail scope but would be nice that it notifies if some process outside the sandbox tries to read/write something. It will probably need root permissions.

But for some users maybe a more clearly UI just for encryption it's nice.

This is what secrets API should be taking care of. Unless you think there should be another Qt application that should come with bubblejail which should popup when asks for password. But that would be very difficult to implement and maintain.

I would try to do a first draft without secrets/password storage.

igo95862 commented 2 years ago

@donob4n I created 11a886c0cc542224cd8a7023fea8fc7afdf31c9a in the branch gocryptfs-test

It is a very rough draft. The command line arguments are not final.

Creating instance with gocryptfs home directory:

create --profile generic --no-desktop-entry --home-plugin gocryptfs_home -- test

The gocryptfs will ask for password to create a new encrypted dir.

Running it:

bubblejail run test chromium

You can only run it from terminal currently because it depends on gocryptfs asking for password. In release version it definitely will need D-Bus Secrets API.

Nonie689 commented 2 years ago

If you use ext4 you can use a transparent encryption [folder-only-encrpytion] with a custom pass for every folder in the ext4 system.

I wouldn't recomment gocryptfs!!!

But I am not sure, if this work if you use the systemd-homed encrypted by ext4 plus the subfolder encryption by ext4! But you could add a ext4 filesystem and mount them in the:

~/.local/share/bubblejail/instances/

and add then for every instances an custom password!

donob4n commented 2 years ago

@donob4n I created 11a886c in the branch gocryptfs-test

It is a very rough draft. The command line arguments are not final. ... Running it:

bubblejail run test chromium

Wow I just tested it and looks great.

You can only run it from terminal currently because it depends on gocryptfs asking for password. In release version it definitely will need D-Bus Secrets API.

Well, probably is the best option for normal users (maybe the more pytonic way too?), but as an intent of minimalist user I like pinentry[1] concept wich has gtk and qt backends and even a dmenu. It also has at least one python package[2].

It looks that using -extpass CMD gocrypts should call pinentry easily. So it doesn't require even python handling of it.

[1] - https://www.gnupg.org/related_software/pinentry/index.html [2] - https://pypi.org/project/pynentry/

donob4n commented 2 years ago

If you use ext4 you can use a transparent encryption [folder-only-encrpytion] with a custom pass for every folder in the ext4 system.

Well, I'm currently using btrfs and also feel that this option would loose some portability.

I wouldn't recomment gocryptfs!!!

Probably this is not the best place for discussing this but if you have some sources supporting it I will take a look.

igo95862 commented 2 years ago

It looks that using -extpass CMD gocrypts should call pinentry easily. So it doesn't require even python handling of it

Good find.

Should be pretty simple to implement.

donob4n commented 2 years ago

Well, pinentry process options or commands from STDIN. So an basic execution for a 'foo' password looks like:

echo "GETPIN" | pinentry

OK pinentry-bemenu 0.9.0
D foo
OK

So maybe passing it to sed (I'm trying but I haven't got it yet), or maybe handling the stdout with python.

EDIT: I get this working: echo "GETPIN" | pinentry | sed -nr '/^D (.+)/s//\1/p' But not sure how to pass to subprocess.call, maybe gocryptfs doesn't support a shell command and needs to wrapper it on a script.

donob4n commented 2 years ago

It seems that others instances fail due missing home_plugins dir:

FileNotFoundError: [Errno 2] No such file or directory: '/home/donoban/.local/share/bubblejail/instances/..../home_plugins

igo95862 commented 2 years ago

It seems that others instances fail due missing home_plugins dir:

FileNotFoundError: [Errno 2] No such file or directory: '/home/donoban/.local/share/bubblejail/instances/..../home_plugins

Do you have entire stack trace?

donob4n commented 2 years ago

It seems that others instances fail due missing home_plugins dir: FileNotFoundError: [Errno 2] No such file or directory: '/home/donoban/.local/share/bubblejail/instances/..../home_plugins

Do you have entire stack trace?

Traceback (most recent call last): File "/usr/bin/bubblejail", line 14, in bubblejail_main() File "/usr/lib/python3.10/site-packages/bubblejail/bubblejail_cli.py", line 418, in bubblejail_main args.func(args) File "/usr/lib/python3.10/site-packages/bubblejail/bubblejail_cli.py", line 125, in run_bjail run_instace( File "/usr/lib/python3.10/site-packages/bubblejail/bubblejail_cli.py", line 109, in run_instace async_run( File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run return loop.run_until_complete(main) File "/usr/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete return future.result() File "/usr/lib/python3.10/site-packages/bubblejail/bubblejail_instance.py", line 265, in async_run_init async with init: File "/usr/lib/python3.10/site-packages/bubblejail/bubblejail_instance.py", line 544, in aenter for plugin_path in self.plugins_dir.iterdir(): File "/usr/lib/python3.10/pathlib.py", line 1017, in iterdir for name in self._accessor.listdir(self): FileNotFoundError: [Errno 2] No such file or directory: '/home/donoban/.local/share/bubblejail/instances/....../home_plugins'

igo95862 commented 2 years ago

I forgot that old instances are not going to have home_plugins. Looks trivial to fix.

igo95862 commented 2 years ago

Should be fixed now.

https://github.com/igo95862/bubblejail/commit/6aaf100eaceca1c3130e22a6ba08f99a4c378e69#diff-bd0ff776003d17d9b61bf00dea31c1a7621b9074e6780917c5dcf38cc65ba967R545

Nonie689 commented 2 years ago

If you use ext4 you can use a transparent encryption [folder-only-encrpytion] with a custom pass for every folder in the ext4 system.

Well, I'm currently using btrfs and also feel that this option would loose some portability.

I wouldn't recomment gocryptfs!!!

Probably this is not the best place for discussing this but if you have some sources supporting it I will take a look.

Here is an tutorial...

https://wiki.gentoo.org/wiki/Ext4_encryption

jim3692 commented 3 months ago

Hello all.

I just came to this issue as I would like to lock some apps on my system.

If I understood the patch correctly, it mounts the encrypted filesystem on the host and then binds it to bubblejail instance. This makes the files available to all the processes of the user, until the instance is closed.

I was wondering whether it's possible to mount the encrypted filesystem directly inside the bubblewrap's mount namespace, hiding it from the host system.

I will do some more research and I will post back.

igo95862 commented 3 months ago

Do you mean something like a FUSE filesystem? Probably possible but I have not experimented with it.

rusty-snake commented 3 months ago

I was wondering whether it's possible to mount the encrypted filesystem directly inside the bubblewrap's mount namespace, hiding it from the host system.

You seem to have a misunderstanding about bubblewraps security model.

jim3692 commented 3 months ago

I hadn't realized that /proc/$pid/root is accessible by anyone.

I was only aware of nsenter requiring sudo and thought this idea is secure.

Back to the drawing board...

igo95862 commented 3 months ago

nsenter does not require sudo unless your kernel has unpriviledged user namespaces disables.

jim3692 commented 3 months ago

My kernel (linux-zen on Arch) has unprivileged namespaces enabled (CONFIG_USER_NS_UNPRIVILEGED=y), but I need sudo to enter the mount namespace of a bubblejailed app.

$ nsenter --mount=/proc/<pid from bubblejailed app>/ns/mnt bash
> nsenter: reassociate to namespace 'ns/mnt' failed: Operation not permitted

$ sudo nsenter --mount=/proc/<pid from bubblejailed app>/ns/mnt bash
> [root@arch /]#
igo95862 commented 3 months ago

If you use --user-parent option that I added to nsenter you can easily enter bubblejail namespaces:

nsenter --preserve-credentials --user-parent --mount --target <pid of sandboxed app>

When --dev option is used with bwrap it will create an intermediate user namespace only accessible with ioctl. The --user-parent option will fetch the parent user namespace from the mount namespace and enter it before entering the mount namespace.

The reason the root works is probably because it holds the CAP_SYS_ADMIN for all descendant namespaces.

jim3692 commented 3 months ago

Thank you for the explanation on nsenter.

As it turns out, namespaces may not be a good option for implementing encryption at runtime.

So, I came up with a completely different, hacky, solution.

I remembered how proot works. It overrides syscalls via LD_PRELOAD.

My idea is to override the filesystem related calls (stat, open, etc.) and handle the encryption and decryption at the application level, without actually mounting anything.

Since, this will override the syscalls, instead of mounting an encrypted filesystem, there will be no way for other programs to read the raw files.

This certainly complicates things a lot and seems very difficult to maintain and secure.

rusty-snake commented 3 months ago

It overrides syscalls via LD_PRELOAD.

LD_PRELOAD overrides library functions not syscalls. Programs calling the raw syscalls will break.

Since, this will override the syscalls, instead of mounting an encrypted filesystem, there will be no way for other programs to read the raw files.

Read my comment above regarding security model again. The child is less privileged. You can easily ptrace or whatever it.

In order to protect a process from other processes you either have to move the process you want to protect upwards / sidewards or move all processes from whom you want to protect downwards. You can not move a process downwards to secure it. Moving downwards secures everything from it. In practice this means you have to switch to a different UID.

jim3692 commented 3 months ago

Sorry @rusty-snake, at first I hadn't fully understood your security model comment. Now I get it.

Also, I made a mistake. proot is based on ptrace, not LD_PRELOAD. Source: https://github.com/proot-me/proot/blob/master/doc/proot/manual.rst#description

In practice this means you have to switch to a different UID.

So, the only solution is to daemonize bubblejail as root (or make it setuid) and have it execute the jails as different UID, on the X/Wayland session of the current user?

rusty-snake commented 3 months ago

Your welcome.

to daemonize bubblejail as root (or make it setuid) and have it execute the jails as different UID

Simplified yes. uid switching can be implemented in several ways

So, the only solution is to

SELinux would also be an solution I think. But much more complicated and not supported everywhere.

X

Lets programs control other programs.

igo95862 commented 3 months ago

Not entirely sure from what you are trying to encrypt files from. Is it against other sandboxes or your user overall?

jim3692 commented 3 months ago

Not entirely sure from what you are trying to encrypt files from. Is it against other sandboxes or your user overall?

There are apps, holding private data, that allow their home data to be copied and used in another machine without any additional steps. I do not know if I am allowed to publicly disclose the names of those apps.

On top of that, some online games are known to have RCE vulnerabilities, allowing attackers to get access to that data.

I am trying to understand what is the best approach to protect apps, with sensitive data, from anything that may be running on a user level.

But it's probably easier to just sandbox the games, and not the sensitive apps. The other way around seemed easier to configure, as I know which apps hold sensitive data, but I cannot be sure about all of the vulnerable software on my PC.

igo95862 commented 3 months ago

Yes, you should sandbox the less trusted applications like games or web browsers. The descendant namespaces cannot join parent namespaces.