Prevent accidental erasure of internal drive while using dd to write USB flash drive

It is risky to issue a command like sudo dd if=image of=/dev/sdb bs=1M due to the risk of typing sdb to something else (e.g. internal drive). If an error has been made, the internal hard drive may get erased. This post proposes a solution by using udev rules, which will allow read/write access to USB drive without sudo and no password required. These udev rules does not affect SATA-based storage devices.

The challenge

There’s been once that one of my colleagues have erased his internal drive by accident while using dd by specifying the wrong device node. Every time we issue command such as sudo dd if=image.raw of=/dev/sdb bs=1M, there is a risk involved, and being careful is not enough.

The following udev rules will change the group ownership of the device node of USB storage devices, enable the member of a specific group to read/write USB flash drive and card readers directly, with no need to issue command sudo. It can

  • Make our work easier by not asking for the password.
  • The write attempt will fail if we specify the wrong device node.
  • Initiating sudo dd will require a password by default.

The attempt will fail because these udev rules only apply to USB storage devices, not SATA-based block devices.

Edit /etc/udev/rules.d/99-usb-storage.rules

ACTION=="add",KERNEL=="sd*",SUBSYSTEM=="block",ENV{DEVTYPE}=="disk|partition",SUBSYSTEMS=="usb",DRIVERS=="usb-storage|uas",GOTO="usb_storage_label_1"
GOTO="usb_storage_label_end"

LABEL="usb_storage_label_1"
KERNEL=="sd[a-z]",      ENV{DEVTYPE}=="disk",                          GOTO="usb_storage_label_2"
KERNEL=="sd[a-z][0-9]*",ENV{DEVTYPE}=="partition",ATTR{partition}=="*",GOTO="usb_storage_label_2"
GOTO="usb_storage_label_end"

LABEL="usb_storage_label_2"
GROUP="sudo"
GOTO="usb_storage_label_end"

LABEL="usb_storage_label_end"

How it works

  1. The first two lines limit the udev rules only apply to USB storage device using driver usb-storage or uas.
  2. The section after label usb_storage_label_1 further filter the type of device node (disk or partition) and the name of device node. (just because I’m paranoid)
  3. Section usb_storage_label_2 change the group of the device node to sudo. So the member of group sudo can have read/write access to these device nodes without issuing command sudo.

Assuming sdb is a USB storage device, it will make the device node looks like

$ ll /dev/sd*
brw-rw---- 1 root disk 8,  0 2018-07-23 17:50:00 /dev/sda
brw-rw---- 1 root sudo 8, 16 2018-07-23 19:04:18 /dev/sdb
brw-rw---- 1 root sudo 8, 17 2018-07-23 19:04:29 /dev/sdb1
brw-rw---- 1 root sudo 8, 18 2018-07-23 19:04:18 /dev/sdb2

A more advanced version

If you have USB flash drive which carries important data, you may add it to the black list to avoid accidental erasure. In this example, it avoid applying GROUP="sudo" to

  1. USB disk which is formatted as encrypted volume (LUKS)
  2. USB partition (e.g., /dev/sdx1) which is formatted as encrypted volume. Be aware this does not protect the disk (/dev/sdx) from accidental erasure.
  3. A Kingston USB disk and partition 2 with the serial number specified. Partition 1 is not protected.
  4. A Seagate USB disk and partition with a specific serial number.
  5. Disk and partition device node of ThinkPad X280 microSD card reader.

Edit /etc/udev/rules.d/99-usb-storage.rules

ACTION=="add",KERNEL=="sd*",SUBSYSTEM=="block",ENV{DEVTYPE}=="disk|partition",SUBSYSTEMS=="usb",DRIVERS=="usb-storage|uas",GOTO="usb_storage_label_1"
GOTO="usb_storage_label_end"

LABEL="usb_storage_label_1"
KERNEL=="sd[a-z]",      ENV{DEVTYPE}=="disk",                          GOTO="usb_storage_label_2"
KERNEL=="sd[a-z][0-9]*",ENV{DEVTYPE}=="partition",ATTR{partition}=="*",GOTO="usb_storage_label_2"
GOTO="usb_storage_label_end"

LABEL="usb_storage_label_2"

# Blacklist LUKS disk and partitions
ENV{ID_FS_TYPE}=="crypto_LUKS",GOTO="usb_storage_label_end"
ENV{ID_FS_USAGE}=="crypto",GOTO="usb_storage_label_end"

# Blacklist Kingston USB - disk and partition 2
ENV{ID_SERIAL}=="Kingston_DataTraveler_3.0_0123456789ABCDEF-0:0",ENV{DEVTYPE}=="disk",GOTO="usb_storage_label_end"
ENV{ID_SERIAL}=="Kingston_DataTraveler_3.0_0123456789ABCDEF-0:0",ENV{DEVTYPE}=="partition",ENV{ID_PART_ENTRY_NUMBER}=="2",GOTO="usb_storage_label_end"

# Blacklist Seagate 3.5 inch HDD
ENV{ID_SERIAL}=="Seagate_Expansion_Desk_01234567-0:0",GOTO="usb_storage_label_end"

# Blacklist ThinkPad X280 built-in SD card reader
ENV{ID_PATH}=="pci-0000:00:14.0-usb-0:3:1.0-scsi-0:0:0:0",GOTO="usb_storage_label_end"

GROUP="sudo"
GOTO="usb_storage_label_end"

LABEL="usb_storage_label_end"

There is a limitation on blacklist implementation as it’s hard to cover the disk (parent) of a LUKS partition. Not quite sure whether it’s possible to improve this.

Appendix

Tested on Ubuntu Desktop 18.04 (bionic)

To avoid automount in Ubuntu 18.04, install dconf-editor and modify the following values

  • org.gnome.desktop.media-handling.automount
  • org.gnome.desktop.media-handling.automount-open

Leave a Reply

Your email address will not be published. Required fields are marked *