Classic confinement review request for fwup

@niemeyer - ping re hotplugging and use of classic for now.

Ping again regarding this snap and classic.

@niemeyer - ping regarding this snap and classic.

@jdstrand It sounds like something could be made along the lines of the udisks2 interface for this snap. Meanwhile, if we want to get going quickly, would the raw-usb interface be enough of a start? It offers access to /dev/ttyUSB*.

Thanks for reviving this request.

fwup needs write access to /dev/sdc, /dev/mmcblk0, etc. or where ever the MicroSD card/removable drive shows up. I don’t think USB access will help. It looks like udisks2 is for detecting and managing mounts. I can’t tell that it helps in gaining write access to the raw disk.

The udisks2 interface grants access to those. It also grants much more, which is undue, but it might make sense to have something like a block-devices interface specifically for that.

1 Like

… said interface might even have a default behavior that excludes known disks mounted in the system, outside of /media. That would be quite nice, as it would offer snaps access to those external devices, without compromising the basic system data.

An advance +1 to granting auto-connection said interface to fwup by default, excluding the system devices.

cc @zyga-snapd

1 Like

If a disk is not mounted in the system and is then granted by the new interface would mounting that filesystem result in revoking said permission?

It’s okay if the answer is simplistic on the first implementation. Even if the logic is basic it would already prevent access into the root filesystem, which is better than the alternative of granting access into everything when that interface is connected.

1 Like

Please consider lvm, raid, etc when looking for ‘mounted in the system’.

That said, it’s probably worth looking to see if udev has tags that we can depend on, kinda like how it has ID_INPUT_JOYSTICK, but for removable media. We might be able to concoct something similar to joystick and the new device-buttons interface there.

1 Like

@zyga-snapd - can you comment on your investigation?

I looked at the udev database on my systems searching for anything that would indicate that a block device is mounted but found nothing of the sort. I then investigated systemd’s device units with similar outcome. Using systemctl status dev-sda2.device I could detect the actual kernel device being followed by that unit. On my openSUSE VM this was sys-devices-pci0000:00-0000:00:10.0-host2-target2:0:0-2:0:0:0-block-sda-sda2.device. The unit with the longer name was active. Since I had a /dev/sda1 FAT partition as well I decided to unmount that to see what happens then. Unfortunately there was no difference with the /dev/sda1 partition mounted or unmounted.

Having found nothing useful here I started looking at /proc/self/mountinfo and /proc/mounts. I was looking for events that we could monitor for to detect that something is mounted or unmounted. According to proc(5) we can open /proc/mounts for reading and obtain a priority event when the mount table changes. I decided to test that quickly with the following program:

zyga@yantra:~/mount-watch> cat main.c 
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
	int epoll_fd, mounts_fd, n;
	struct epoll_event event;

	epoll_fd = epoll_create1(EPOLL_CLOEXEC);
	if (epoll_fd < 0) {
		perror("cannot create epoll object");
		return 1;
	}

	mounts_fd = open("/proc/mounts", O_RDONLY|O_CLOEXEC);
	if (mounts_fd < 0) {
		perror("cannot open /proc/mounts");
		return 2;
	}

	memset(&event, 0, sizeof event);
	event.events |= EPOLLPRI;
	event.data.u32 = 0xCAFE;
	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, mounts_fd, &event) < 0) {
		perror("cannot add /proc/mounts file descriptor to epoll");
		return 3;
	}

	for (;;) {
		memset(&event, 0, sizeof event);
		n = epoll_wait(epoll_fd, &event, 1, 0); 
		if (n < 0) {
			perror("cannot wait for epoll event");
			return 4;
		}
		if (n == 1) {
			if (event.data.u32 == 0xCAFE) {
				printf("priority event on /proc/mounts\n");
			}
		}
	}

	return 0;
}

To compile the program and invoke it as non-root user. The program correctly detects changes to the mount table.

With this knowledge we could do as follows:

  • Teach snapd to monitor mount events and track the set of mounted block devices.
  • Create an interface that allows interaction with block devices that are not mounted.
  • React to mount changes and modify device control groups accordingly.

Some questions and comments:

  • Assume that a device was previously unmounted and “available” though the interface. When said device is mounted how do we know if it was mounted by a snap or by the rest of the system. If the device was mounted by a snap we should keep access as-is. If a device was mounted by the system we should revoke access.
  • We could manipulate control groups directly or we could attach a PBF program and a shared table that contains whitelisted devices (or blacklisted devices) and manipulate the map from snapd. I haven’t checked this option but it is interesting since it doesn’t involve as many racy operations - just react to mount events and update the BPF map. The whole process is atomic across all the snaps. Should

I’d prefer not to add an opaque backend for the security policy for some devices and not others. IMO, it is bad enough that we are updating a device cgroup rather than an inode label that AppArmor can look at (ie, what we want to move to long term); I’d rather not add more complexity in this area. It should be fine to update the udev rules file and call trigger on block devices.

@zyga-snapd - what is the status of this?

I’m not actively working on this. @pedronis, could we have this in the backlog / TODO list - specifically improved management of block devices for unmounted filesystems.

If no one is actively working to address this snap being able to work in strict mode, then we should reconsider the original request for classic confinement. @pedronis and/or @niemeyer - it wasn’t clear to me that there was a clear call on if this should be made classic in the meantime. Should it?

The new interface work doesn’t seem completely trivial. Is not also clear to me how good/bad would be the timeliness of changing the access based whether the block device is mounted or not?

I don’t think granting classic in the interim would be misleading OTOH if the target is core devices it wouldn’t really help but the op requested classic to start with so I suppose that’s not the case/fully the case.

Classic support for now would be great! Thanks!

FYI, https://snapcraft.io/fwup doesn’t seem to exist (I guess cause the snap is private?)

While I appreciate the discussion in this thread, it isn’t immediately apparent why a firmware updater would need access to all block devices on the system (or classic for that matter), however, we don’t have anything better.

Perhaps the description could be updated to say something about this access? Eg, currently the description in the store says: “Configurable embedded Linux firmware update creator and runner”. Perhaps say something along the lines of: “Configurable embedded Linux firmware update creator and runner. Supports writing firmware to MicroSD cards, foo, bar, …”?

That said, the requirements are understood. @evan, @Wimpress, @popey, @Igor - can one of you perform the vetting?

Sorry, I made fwup private to avoid confusion in case anyone found it. Until the set of responses over the past few days, I had thought things had dropped off everyone’s radar.