Как автоматически монтировать (зашифрованные) разделы USB-накопителя с использованием udev и systemd

Вопрос или проблема

У меня есть следующие требования:

  • Если вставить определенную флешку, она должна автоматически обнаруживаться и монтироваться. Информация о том, где ее необходимо смонтировать, будет в /etc/fstab (конечно).
  • Если флешка будет выдернута, должна автоматически произойти полная очистка; в результате чего не должно остаться следов, таких как то, что она была смонтирована (или все еще смонтирована) и/или какие-либо незавершенные единицы systemd.
  • Если раздел(ы), о которых идет речь, будут смонтированы вручную с помощью umount, тогда также все должно быть очищено, даже если флешка все еще присутствует; ее извлечение из разъема должно очистить только то, что не удалось очистить ранее (вероятно, единицы .device).

Для достижения вышеописанного в чистом виде вставка флешки должна привести к запуску пользовательской единицы systemd, которую мы затем настроим для выполнения остальных действий.

Что требуется для написания этого?

Предположим, у нас есть флешка с тремя разделами:
/dev/sdc1 и /dev/sdc2 – которые зашифрованы LUKS,
и /dev/sdc3 – который не зашифрован.

Например,

>sudo blkid /dev/sdc{1,2,3}
/dev/sdc1: UUID="de9a000f-fa60-4050-8d11-864e97829b8a" TYPE="crypto_LUKS" PARTUUID="219578f5-5905-41cd-895d-7b937ac0756a"
/dev/sdc2: UUID="012059c1-6b88-4937-889a-a21448740492" TYPE="crypto_LUKS" PARTUUID="59c7f6f3-a5cb-41af-82fa-b515031d85d5"
/dev/sdc3: LABEL="README" UUID="9d262deb-2343-4096-bccf-bb26ed4415ad" BLOCK_SIZE="1024" TYPE="ext2" PARTUUID="165db3da-4ebb-4f59-ab09-ee5e5b26ed43"

Здесь UUID используется в /dev/disk/by-uuid:

>ls -l /dev/disk/by-uuid | grep sdc
lrwxrwxrwx 1 root root 10 Dec 22 18:27 012059c1-6b88-4937-889a-a21448740492 -> ../../sdc2
lrwxrwxrwx 1 root root 10 Dec 22 18:27 9d262deb-2343-4096-bccf-bb26ed4415ad -> ../../sdc3
lrwxrwxrwx 1 root root 10 Dec 22 18:27 de9a000f-fa60-4050-8d11-864e97829b8a -> ../../sdc1

Поскольку имя устройства /dev/sdc является довольно случайным, мы должны использовать только UUID с этого момента.

Чтобы вручную смонтировать незашифрованный раздел, нам нужно выполнить (убедитесь, что на /mnt и/или /mnt/usb ничего уже не смонтировано!):

UUID=9d262deb-2343-4096-bccf-bb26ed4415ad
SERIAL=408D5CBECAC3E7C0E9170AEC
sudo /bin/mkdir -p /mnt/usb/$SERIAL/part3
sudo mount -t ext2 /dev/disk/by-uuid/$UUID /mnt/usb/$SERIAL/part3

Где я выбрал использовать значение ID_SERIAL_SHORT usb-устройства, которое можно получить, например:

>udevadm info -n /dev/sdc3 | grep -E '(ID_SERIAL_SHORT|ID_FS_UUID)='
E: ID_SERIAL_SHORT=408D5CBECAC3E7C0E9170AEC
E: ID_FS_UUID=9d262deb-2343-4096-bccf-bb26ed4415ad

Давайте запомним точку монтирования в /etc/fstab, добавив в этот файл:

UUID=9d262deb-2343-4096-bccf-bb26ed4415ad   /mnt/usb/408D5CBECAC3E7C0E9170AEC/part3 ext2    rw,noauto   0 0

Обратите внимание, что после этого мы можем сделать:

sudo mount /dev/sdc3

и это будет корректно смонтировано, пока это блочное устройство имеет ожидаемый UUID.

Другие два раздела, /dev/sdc{1,2}, зашифрованы и сначала нужно

UUID1=573bfda0-69f5-4fb9-9d7e-333a70a51710
UUID2=6d7bdc1d-3eb0-4774-a9fb-3d1ac1027010
echo $LUKS_PASS | sudo cryptsetup -q luksOpen /dev/disk/by-uuid/$UUID1 usb-$UUID1
echo $LUKS_PASS | sudo cryptsetup -q luksOpen /dev/disk/by-uuid/$UUID2 usb-$UUID2

выполнять, где я предполагаю, что переменная окружения LUKS_PASS содержит пароль, необходимый для расшифровки раздела LUKS. UUID здесь – это те, которые возвращает blkid:

>sudo blkid /dev/sdc{1,2}
/dev/sdc1: UUID="573bfda0-69f5-4fb9-9d7e-333a70a51710" TYPE="crypto_LUKS" PARTUUID="caa57a9d-7389-4990-a243-34b19a179368"
/dev/sdc2: UUID="6d7bdc1d-3eb0-4774-a9fb-3d1ac1027010" TYPE="crypto_LUKS" PARTUUID="10813450-6dcc-4700-9e88-d0577c0c9aeb"

Это создает устройство /dev/mapper/usb-$UUID (для каждого раздела), каждое с собственным UUID:

>sudo blkid /dev/mapper/usb*
/dev/mapper/usb-573bfda0-69f5-4fb9-9d7e-333a70a51710: LABEL="gold1-2024-12-21" UUID="67f056be-dbbc-4f7a-979d-6ff077d16e93" BLOCK_SIZE="4096" TYPE="ext2"
/dev/mapper/usb-6d7bdc1d-3eb0-4774-a9fb-3d1ac1027010: LABEL="gold2-2024-12-21" UUID="94bfcc55-6b52-431f-bc76-3aa198c107c7" BLOCK_SIZE="4096" TYPE="ext2"

Именно эти UUID мы хотим добавить в /etc/fstab.

Чтобы облегчить жизнь, я написал следующий скрипт, который выводит необходимую конфигурацию /etc/fstab при запуске после вставки USB (для моего конкретного случая):

# Получить путь к устройству.
DEVPATH=$(/bin/ls /dev/disk/by-id/usb-Kingston_DataTraveler* | grep -E -v -- '-part[0-9]+$')
# Получить серийный номер флешки.
ID_SERIAL_SHORT=$(udevadm info -n $DEVPATH | grep ID_SERIAL_SHORT | sed -e 's/.*ID_SERIAL_SHORT=//')
# Пройтись по всем существующим разделам.
for p in $(/bin/ls $DEVPATH-part*); do
  # Извлечь строку "part?".
  PART=$(echo $p | sed -r -e 's/.*-(part[0-9]+)$/\1/')
  # Получить UUID блочного устройства этого раздела, если это зашифрованный раздел LUKS.
  UUID=$(blkid --match-token TYPE=crypto_LUKS $p | sed -e 's/.* UUID="\([^ ]*\)".*/\1/' || true)
  if [ -n "$UUID" ]; then
    # Расшифровать раздел.
    echo $LUKS_PASS | sudo cryptsetup -q luksOpen /dev/disk/by-uuid/$UUID usb-$UUID
    # Получить UUID зашифрованного раздела.
    UUID2=$(blkid /dev/mapper/usb-$UUID | sed -e 's/.* UUID="\([^ ]*\)".*/\1/')
    echo -e "UUID=$UUID2\t/mnt/usb/$ID_SERIAL_SHORT/$PART\text2\trw,noauto\t0 0"
    sudo cryptsetup luksClose usb-$UUID
  else
    # Получить UUID незашифрованного раздела.
    UUID=$(blkid $p | sed -e 's/.* UUID="\([^ ]*\)".*/\1/')
    echo -e "UUID=$UUID\t/mnt/usb/$ID_SERIAL_SHORT/$PART\text2\trw,noauto\t0 0"
  fi
done

После экспорта LUKS_PASS убедитесь, что вы запустили выше указанный скрипт с использованием sudo -E, чтобы сохранить окружение. Пример вывода:

>sudo -E ./foo.sh
UUID=db97c4b0-8f92-4edc-bacb-dc90e62de2e2       /mnt/usb/408D5CBECAC3E7C0E9170AEC/part1 ext2    rw,noauto       0 0
UUID=c45c6a7a-1c0f-49c4-bcfb-15766114daa1       /mnt/usb/408D5CBECAC3E7C0E9170AEC/part2 ext2    rw,noauto       0 0
UUID=9d262deb-2343-4096-bccf-bb26ed4415ad       /mnt/usb/408D5CBECAC3E7C0E9170AEC/part3 ext2    rw,noauto       0 0

С установленным /etc/fstab и точно зная, какие команды нужны; осталось автоматизировать это с помощью udev и systemd units.

Вопрос в том: как?

Ответ или решение

Авто-монтаж зашифрованных разделов USB-накопителя с использованием udev и systemd

Автоматизация процессов монтирования и размонтирования USB-накопителей — востребованная задача в управлении системами. В данном руководстве мы рассмотрим, как автоматически монтировать зашифрованные и незашифрованные разделы USB-накопителя с использованием механизма udev и управления сервисами через systemd. Мы будем рассматривать USB-накопитель, содержащий три раздела: два зашифрованных с помощью LUKS и один незашифрованный.

Шаг 1: Подготовка системы

Перед началом убедитесь, что у вас установлены необходимые инструменты:

sudo apt install cryptsetup

Шаг 2: Настройка fstab

Вам нужно будет добавить информацию о ваших разделе в файл /etc/fstab. Запишите UUID для каждого раздела с помощью команды blkid:

sudo blkid /dev/sdc{1,2,3}

Добавьте три строки в /etc/fstab для указания того, где монтировать разделы. Например:

UUID=db97c4b0-8f92-4edc-bacb-dc90e62de2e2       /mnt/usb/408D5CBECAC3E7C0E9170AEC/part1 ext2    rw,noauto       0 0
UUID=c45c6a7a-1c0f-49c4-bcfb-15766114daa1       /mnt/usb/408D5CBECAC3E7C0E9170AEC/part2 ext2    rw,noauto       0 0
UUID=9d262deb-2343-4096-bccf-bb26ed4415ad       /mnt/usb/408D5CBECAC3E7C0E9170AEC/part3 ext2    rw,noauto       0 0

Шаг 3: Скрипт для обработки динамических изменений

Создайте bash-скрипт для обработки событий подключения и отключения:

#!/bin/bash

DEVPATH=$(ls /dev/disk/by-id/usb-Kingston_DataTraveler* | grep -v -- '-part[0-9]+$')
ID_SERIAL_SHORT=$(udevadm info -n $DEVPATH | grep ID_SERIAL_SHORT | sed -e 's/.*ID_SERIAL_SHORT=//')

for p in $(ls $DEVPATH-part*); do
    PART=$(echo $p | sed -r -e 's/.*-(part[0-9]+)$/\1/')
    UUID=$(blkid --match-token TYPE=crypto_LUKS $p | sed -e 's/.* UUID="\([^ ]*\)".*/\1/' || true)

    if [ -n "$UUID" ]; then
        echo $LUKS_PASS | sudo cryptsetup -q luksOpen /dev/disk/by-uuid/$UUID usb-$UUID
        UUID2=$(blkid /dev/mapper/usb-$UUID | sed -e 's/.* UUID="\([^ ]*\)".*/\1/')
        echo -e "UUID=$UUID2\t/mnt/usb/$ID_SERIAL_SHORT/$PART\text2\trw,noauto\t0 0"
        sudo cryptsetup luksClose usb-$UUID
    else
        UUID=$(blkid $p | sed -e 's/.* UUID="\([^ ]*\)".*/\1/')
        echo -e "UUID=$UUID\t/mnt/usb/$ID_SERIAL_SHORT/$PART\text2\trw,noauto\t0 0"
    fi
done

Убедитесь, что скрипт исполняемый:

chmod +x /path/to/your/script.sh

Шаг 4: Создание udev правил

Создайте файл /etc/udev/rules.d/99-usb-mount.rules и добавьте следующее правило:

ACTION=="add", KERNEL=="sd*[0-9]", ENV{ID_SERIAL_SHORT}=="408D5CBECAC3E7C0E9170AEC", RUN+="/path/to/your/script.sh"

Это правило запускает ваш скрипт при подключении USB-накопителя с указанным серийным номером.

Шаг 5: Настройка systemd

Создайте единичный файл systemd для управления процессом:

sudo nano /etc/systemd/system/usb-mount.service

Заполните его следующим содержимым:

[Unit]
Description=Auto Mount USB Stick
After=local-fs.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/path/to/your/script.sh
ExecStop=/bin/umount /mnt/usb/408D5CBECAC3E7C0E9170AEC/part*

[Install]
WantedBy=multi-user.target

Шаг 6: Включение и запуск сервиса

После создания службы, активируйте её:

sudo systemctl enable usb-mount.service

Теперь вы можете протестировать, подключив USB-накопитель. Убедитесь, что разделы монтируются автоматически. Если они были размонтированы, проверьте выполнение скрипта через systemd.

Заключение

Это пошаговое руководство поможет вам создать систему авто-монтажа зашифрованных и незашифрованных разделов USB-накопителя на базе udev и systemd. С помощью этого решения вы получите стабильное и чистое управление вашим хранилищем, что значительно упростит ежедневные задачи работы с USB-устройствами.

Оцените материал
Добавить комментарий

Капча загружается...