gokrazy / tools

this repository contains the gok CLI tool of gokrazy
https://gokrazy.org
BSD 3-Clause "New" or "Revised" License
50 stars 27 forks source link

gokr-packer: improve permission situation #9

Closed stapelberg closed 5 years ago

stapelberg commented 5 years ago

One way to use the gokr-packer is to pass a filename to the -overwrite parameter and have it write an image. While this works without any additional permissions, there are two significant advantages to passing a device to -overwrite:

  1. gokr-packer can automatically determine the size of the device instead of requiring the -target_storage_bytes parameter.
  2. A lot fewer bytes have to be copied, massively speeding up the whole process.

We have a number of options as to how to obtain the required permissions:

  1. Grant capabilities which effectively mean gokr-packer is suid root (status quo): sudo setcap CAP_SYS_ADMIN,CAP_DAC_OVERRIDE=ep $(go env GOPATH)/bin/gokr-packer. This silently doesn’t work on file systems which are mounted nosuid, such as ecryptfs when using Ubuntu’s home directory encryption.
  2. Grant write permission to the target devices: sudo setfacl -m u:${USER}:rw /dev/mmc* /dev/sd*. This doesn’t stick: once you unplug the SD card, the permission change is lost.
  3. Add the user to the disk group, effectively granting write permission to all disk devices. This requires logging in again (or using newgrp) and might be too coarse-grained. Debian decided against using groups for disk access for (not concretely specified) security reasons: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=751892
  4. Have gokr-packer call sudo for the write part only. Note that calling sudo gokr-packer is not desired, as gokr-packer calls out to the go tool, which would then operate on root’s $GOPATH, not the user’s.
  5. Use udisks via dbus to format the target device. Writing the image to the target device requires authentication until https://github.com/storaged-project/udisks/issues/590 is fixed. Authentication might not be available — it requires that the user is running a policykit authentication agent. Additionally, old versions of udisks are buggy in that they are unable to format the device twice in a row.
option grain sticky re-read parttable requires write needs password
setcap coarse until gokr-packer update yes suid file system no
setfacl fine until SD card re-plug no - no
disk group coarse yes no - no
sudo fine yes yes sudo by default
udisks fine yes yes recent udisks yes
stapelberg commented 5 years ago

Dumping this here in case we want to pick up the udisks approach at some point:

import "github.com/godbus/dbus"

func udisks() error {
    conn, err := dbus.SystemBus()
    if err != nil {
        return err
    }
    const (
        linux    = "0x83"
        squashFS = linux // SquashFS does not have a dedicated type
    )
    // TODO: set bootable flag?
    options = map[string]dbus.Variant{
        // Need to specify the partition type explicitly, otherwise udisks
        // creates an extended partition for the last (/perm) partition:
        "partition-type": dbus.MakeVariant("primary"),
    }
    obj = conn.Object("org.freedesktop.UDisks2", "/org/freedesktop/UDisks2/block_devices/sda")
    if err := obj.Call("org.freedesktop.UDisks2.PartitionTable.CreatePartition", 0,
        uint64(8192*512), // offset, start at 8192 sectors
        uint64(100*MB),   // size
        "0xc",            // type
        "",               // name
        options).Err; err != nil {
        return fmt.Errorf("UDisks2.CreatePartition(/dev/sda): %v", err)
    }
    if err := obj.Call("org.freedesktop.UDisks2.PartitionTable.CreatePartition", 0,
        uint64(8192*512+100*MB), // offset, start after partition 1
        uint64(500*MB),          // size
        squashFS,                // type
        "",                      // name
        options).Err; err != nil {
        return fmt.Errorf("UDisks2.CreatePartition(/dev/sda): %v", err)
    }
    if err := obj.Call("org.freedesktop.UDisks2.PartitionTable.CreatePartition", 0,
        uint64(8192*512+600*MB), // offset, start after partition 2
        uint64(500*MB),          // size
        squashFS,                // type
        "",                      // name
        options).Err; err != nil {
        return fmt.Errorf("UDisks2.CreatePartition(/dev/sda): %v", err)
    }
    if err := obj.Call("org.freedesktop.UDisks2.PartitionTable.CreatePartition", 0,
        uint64(8192*512+1100*MB), // offset, start after partition 2
        uint64(500*MB),           // size, TODO
        linux,                    // type
        "",                       // name
        options).Err; err != nil {
        return fmt.Errorf("UDisks2.CreatePartition(/dev/sda): %v", err)
    }

    return nil
}