Granting user read/write permissions to a USB device in Ubuntu Linux on boot
Tags: udev, linux, usb, ubuntu
Published: 2021-05-03 08:27:57

---
title: Granting user read/write permissions to a USB device in Ubuntu Linux on boot
published: true
date: 2020-09-22 08:24:00 UTC
tags: udev, linux, usb, ubuntu
canonical_url: https://ramonh.dev/2020/09/22/usb-device-linux-startup/
---

One of my recent projects involved having a cash register print receipts to a thermal printer. In order to do this, we had a [Sinatra](http://sinatrarb.com/) server running on boot on the device that would, upon receiving a `POST` request, print out the [ESC/POS](https://github.com/escpos/escpos) data.

The way I did this was by creating a [custom service](https://medium.com/@benmorel/creating-a-linux-service-with-systemd-611b5c8b91d6) on Ubuntu that automatically starts on boot that runs the following script:

```
  $ su - <NON_ROOT_USER> -l -c \
    "cd /path/to/code && \
    bundle exec ruby app.rb /dev/usb/lp0"

```

In this script, we’re passing the file path `/dev/usb/lp0` to the server. This is the file descriptor for the thermal printer I wish to print to.

With all this set, what happens when I power on the device and send in a `POST` request?

Sadly, I get an error saying that I don’t have permission to write to the file`/dev/usb/lp0`. I can run the following command to fix this, though:

```
  $ sudo chown <NON_ROOT_USER> /dev/usb/lp0

```

Once I enter it, I can print to my heart’s (and paper capicity’s) content! However, once the device gets rebooted, the file will get re-created and not have the permissions I assigned. Well, darn.

I did find a solution that could help me, though!

# Custom udev rules

This was when I learned about [`udev`](https://wiki.debian.org/udev), the system for managing devices in Debian systems. With it, I can write specific rules for recognising devices:

> udev allows for rules that specify what name is given to a device, regardless of which port it is plugged into. For example, a rule to always mount a hard drive with manufacturer “iRiver” and device code “ABC” as /dev/iriver is possible. This consistent naming of devices guarantees that scripts dependent on a specific device’s existence will not be broken.

This is _exactly_ what I need! I can assign all usb devices plugged into the Linux machine to be owned by `NON_ROOT_USER`.

By following the instructions, I created a file in the directory `/etc/udev/rules.d` called `99-perm.rules` with the following line:

```
  SUBSYSTEM=="usb", OWNER="<NON_ROOT_USER>"

```

With this rule in place, rebooting the machine immediately made it work!

# Making it more secure

You might’ve been asking yourself:

> Does this mean all USB devices will be owned by `NON_ROOT_USER`?

And you would be absolutely correct. Maybe we want to only grant permissions for that one device, which we can totally do! We can base it on the serial number of the device. For example, for my thermal printer, I can find this out with the following command:

```
  $ udevadm -a /dev/usb/lp0

```

This’ll print out some results, including this line:

```
    ATTRS{serial}=="L29955839962630040"

```

Let’s open up `/etc/udev/rules.d/99-perm.rules` again, and replace the line we added with the following:

```
  SUBSYSTEM=="usb", ATTRS{serial}=="L29955839962630040", OWNER="<NON_ROOT_USER>"

```

See with this we’re specifically telling `udev` that we wanna own this specific thermal printer. A quick reboot and score, it works!

# What’s next?

A solid question! There is certainly more we can do to make sure this system works.

For example, if I were to have multiple thermal printers on a device, then it wouldn’t be guaranteed to have the name `lp0`. For this case we can use the`SYMLINK+=` attribute to specify a filename it’ll always definitely have. So instead of `lp0` or `lp1` I could point my script to `thermal-printer` or something:

```
  SUBSYSTEM=="usb", ATTRS{serial}=="L72010011070626380", OWNER="<NON_ROOT_USER>", SYMLINK+="usb/thermal-printer"

```

This will then create a [symbolic link](https://en.wikipedia.org/wiki/Symbolic_link) called `/dev/usb/thermal-printer` that I can point my Ruby server to!

Big thank you to [Daniel Drake](http://reactivated.net/writing_udev_rules.html) for this helpful guide on how to write rules for `udev`!

I’m eager to hear your thoughts or ideas for improvements, so [hit me up on Twitter](https://twitter.com/hola_soy_milk)!