Это первая часть серии из трёх. Здесь мы собираем базовый защищённый workspace на Linux. Я буду использовать Debian 13, сначала идём самым очевидным путём, затем видим его слабое место и исправляем модель
Статья ориентирована на Debian-based системы:
- Debian;
- Ubuntu;
- Linux Mint;
- ...
О чём эта история
Идея родилась довольно бытовая:
А что, если сделать отдельное рабочее пространство, защищённое...
Условно: ранним утром послышался чудесный басистый стук в дверь со словами: "поллюция!" — послышалось, наверное...
И о чудо! Стук продолжается, и шумы уже стали более "механическими".
С трясущимися ляхами ты уже несёшься к вундервафле под названием "компьютер", дабы очистить историю браузера и никто не узнал, как ты гуглил: "скачать стилер бесплатно", "как установить питон без вирусов онлайн", "мануал по мамонтизации от хакер2281337", а заодно смыть это всё с винта. Негодяй!
Человек — существо интеллектом одарённое, а потому держать в голове: Да ладно. Удалю всё по-быстрому. К тому же я ваще там всякие материалы смотрел\читал, я ультра-мега прошаренный — показалось очень благоразумной идеей.
Не тут-то было! Дверь уже открыта, и ты применяешь секретную боевую горизонтальную позу под названием "мордой в пол".
Все твои пароли добываются путём выполнения комплекса оздоровительных практик от вполне компетентных в этом вопросе докторов: интенсивные дыхательные практики; электро-ректальная терапия; комплекс термической обработки с экстремальными температурами; и много-много чего ещё неизведанного...
Осознавая всё это, тебе ничего не остаётся кроме как скорчить кислую мину:
Потому я и тут, расскажу и покажу, как создать не “секретную папку”, не “магическую защиту от всего”, а нормальный технический прототип без всяких сложностей. Но лишь по началу...
- есть обычная Linux-система;
- есть отдельный зашифрованный раздел;
- есть USB-флешка как физический фактор доступа;
- есть команды, которые открывают и закрывают workspace.
- SRW — Secure Runtime Wrapper
- Secure Workspace
Здесь мы сделаем базу.
Чем мы займёмся в этой части:
Мы пройдём три состояния.
Сначала — наивная, но полезная схема:
USB-флешка
└── srw.key
LUKS-раздел
└── /dev/mapper/srw_secure
└── ext4
└── /mnt/srw
Потом добавим первые команды:
srw-status
srw-unlock
srw-lock
А затем поймём важную вещь:
Если raw keyfile лежит на флешке, то сама флешка становится полноценным ключом.
То есть любой, у кого есть эта флешка, сможет открыть workspace.
Это действительно удобно и ничем не отличается от условного VeraCrypt с KeyFile
После этого мы перейдём к более правильной модели:
USB-флешка
└── srw.key.gpg зашифрованный keyfile
пароль пользователя
└── нужен, чтобы расшифровать srw.key.gpg
/run/srw/srw.key
└── временный raw keyfile в tmpfs
LUKS workspace
└── /mnt/srw
Итоговая формула: для открытия нужен USB + пароль
Важное ограничение
Сразу честно.
Если workspace уже открыт, то извлечение флешки само по себе не блокирует данные.
LUKS mapper уже открыт;
ключ dm-crypt уже находится в памяти ядра;
файловая система уже смонтирована;
процессы могут держать открытые файлы.
Поэтому нельзя обещать: вытащил флешку — и всё мгновенно стало недоступно.
Корректная формулировка: и флешка, и пароль нужны для открытия workspace; после закрытия workspace он снова недоступен без этих факторов.
Аварийное закрытие при извлечении флешки — это тема следующей части.
Предупреждение
Команды ниже могут уничтожить данные.
Особенно опасны:
mkfs.ext4 ...
cryptsetup luksFormat ...
dd if=/dev/urandom ...
Для первого прототипа лучше использовать:
- виртуальную машину;
- отдельную флешку;
- отдельный тестовый раздел;
- диск без важных данных.
Этап 1. Собираем самый простой LUKS workspace
На первом этапе делаем так:
USB-флешка с keyfile
↓
LUKS-раздел на диске
↓
ручное открытие workspace
↓
ручное закрытие workspace
Это ещё не финальная модель безопасности. Это первая рабочая версия, которая поможет нам увидеть проблему.
1.1. Устанавливаем зависимости
sudo apt update
sudo apt install cryptsetup util-linux psmisc udev systemd gnupg
Что зачем:
- cryptsetup — управление LUKS/dm-crypt;
- util-linux — lsblk, mount, umount, findmnt;
- psmisc — fuser, понадобится позже для panic-flow;
- udev — позже будет отслеживать извлечение флешки;
- systemd — позже будет запускать panic-service и управлять профилями;
- gnupg — понадобится для модели USB + пароль.
1.2. Определяем флешку и тестовый раздел
Смотрим устройства:
lsblk -o NAME,PATH,SIZE,FSTYPE,LABEL,UUID,PARTUUID,MOUNTPOINTS,MODEL
Примерный вывод:
sda /dev/sda 476G
├─sda1 /dev/sda1 512M vfat
├─sda2 /dev/sda2 100G ext4
└─sda3 /dev/sda3 20G
sdb /dev/sdb 58G
└─sdb1 /dev/sdb1 58G vfat USBDISK
В примерах дальше:
USB partition: /dev/sdb1
Secure partition: /dev/sda3
У вас имена будут другими.
1.3. Форматируем флешку
Для MVP используем ext4 и label
SRWKEY:sudo mkfs.ext4 -L SRWKEY /dev/sdb1
Проверяем:
lsblk -f
Ожидаем примерно:
sdb1 ext4 1.0 SRWKEY <UUID>
1.4. Монтируем флешку
Создаём точку монтирования:
sudo mkdir -p /mnt/srw-key
Монтируем по label:
sudo mount /dev/disk/by-label/SRWKEY /mnt/srw-key
Проверяем:
findmnt /mnt/srw-key
Пример:
TARGET SOURCE FSTYPE OPTIONS
/mnt/srw-key /dev/sdb1 ext4 rw,relatime
1.5. Создаём raw keyfile на флешке
Создаём случайный ключ длиной 4096 байт:
sudo dd if=/dev/urandom of=/mnt/srw-key/srw.key bs=4096 count=1
Задаём права:
sudo chmod 0400 /mnt/srw-key/srw.key
sudo chown root:root /mnt/srw-key/srw.key
sync
Проверяем:
sudo ls -l /mnt/srw-key/srw.key
Ожидаем:
-r-------- 1 root root 4096 ... /mnt/srw-key/srw.key
На этом этапе
srw.key — обычный raw keyfile. Пока это выглядит удобно. Позже именно это станет проблемой.1.6. Подготавливаем LUKS-раздел
Ещё раз проверяем, что выбран правильный раздел:
lsblk -o NAME,PATH,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINTS,MODEL
Форматируем тестовый раздел как LUKS2:
sudo cryptsetup luksFormat --type luks2 /dev/sda3
Команда попросит ввести
YES, а затем passphrase.Эта passphrase пока будет recovery-паролем. Она нужна, чтобы не потерять доступ во время разработки.
1.7. Открываем LUKS и создаём файловую систему
Открываем LUKS:
sudo cryptsetup open /dev/sda3 srw_secure
Проверяем:
sudo cryptsetup status srw_secure
Ожидаем:
/dev/mapper/srw_secure is active.
type: LUKS2
...
Создаём ext4 внутри открытого mapper:
sudo mkfs.ext4 -L SRWDATA /dev/mapper/srw_secure
Почему именно внутри mapper:
/dev/sda3
└── LUKS-контейнер
└── /dev/mapper/srw_secure
└── ext4
Если создать ext4 напрямую на
/dev/sda3, мы просто уничтожим LUKS.1.8. Монтируем workspace
sudo mkdir -p /mnt/srw
sudo mount /dev/mapper/srw_secure /mnt/srw
Создаём базовую структуру:
sudo mkdir -p /mnt/srw/projects
sudo mkdir -p /mnt/srw/.runtime
sudo mkdir -p /mnt/srw/.home
sudo mkdir -p /mnt/srw/.cache
sudo mkdir -p /mnt/srw/.config
sudo mkdir -p /mnt/srw/.local/share
sudo mkdir -p /mnt/srw/.local/state
Проверяем:
findmnt /mnt/srw
1.9. Добавляем keyfile в LUKS
Добавляем
srw.key как дополнительный способ открытия LUKS:sudo cryptsetup luksAddKey /dev/sda3 /mnt/srw-key/srw.key
Команда попросит существующий recovery-пароль LUKS.
Проверяем keyslots:
sudo cryptsetup luksDump /dev/sda3
Важный момент:
LUKS keyslot — это альтернативный способ получить доступ к master key.
Если есть пароль и keyfile, то логика такая:
пароль ИЛИ keyfile
А не такая:
пароль И keyfile
Запомним это. Скоро оно нам пригодится.
1.10. Проверяем открытие через USB keyfile
Закрываем workspace:
sudo umount /mnt/srw
sudo cryptsetup close srw_secure
Проверяем:
findmnt /mnt/srw || echo "workspace is not mounted"
sudo cryptsetup status srw_secure || echo "mapper is closed"
Открываем через keyfile:
sudo cryptsetup open /dev/sda3 srw_secure --key-file /mnt/srw-key/srw.key
Монтируем:
sudo mount /dev/mapper/srw_secure /mnt/srw
Проверяем:
findmnt /mnt/srw
Если всё получилось, базовое ядро работает.
1.11. Что мы получили на первом этапе
У нас есть:
флешка с raw keyfile;
LUKS-раздел;
workspace в /mnt/srw;
возможность открыть LUKS через USB.
На первый взгляд всё хорошо: без флешки keyfile недоступен.
Но на самом деле мы сделали флешку полноценным ключом.
Пока это просто заметка. Сначала завернём всё в CLI-слой, и проблема станет очевиднее.
Этап 2. Первый CLI-слой
Теперь вместо ручных команд делаем первые управляющие команды:
srw-status показать состояние workspace
srw-unlock открыть workspace
srw-lock закрыть workspace
Зачем это нужно?
Не ради красоты. А чтобы будущая система стала управляемой: конфиг отдельно, логика отдельно, потом можно добавлять профили, panic-сценарии и политики.
2.1. Сразу уходим от /dev/sdX
В ручных командах удобно писать:
/dev/sda3
/dev/sdb1
Но для архитектуры это плохо.
Имена
/dev/sdX могут меняться:- после перезагрузки;
- после подключения другого диска;
- после смены USB-порта;
- в виртуальной машине;
- при изменении порядка обнаружения устройств.
lsblk -o NAME,PATH,SIZE,FSTYPE,LABEL,UUID,PARTUUID,MOUNTPOINTS,MODEL
Или точечно:
sudo blkid /dev/sdb1
sudo blkid /dev/sda3
Пример:
/dev/sdb1: LABEL="SRWKEY" UUID="8a91d6fd-2532-49d1-b9c6-5be93d812111" TYPE="ext4"
/dev/sda3: UUID="2f4d8d1a-9a7c-44f4-9ad2-0f83e7a1b2ab" TYPE="crypto_LUKS"
2.2. Создаём /etc/srw/policy.conf
sudo mkdir -p /etc/srw
sudo chmod 0755 /etc/srw
sudo nano /etc/srw/policy.conf
Вставляем, заменив UUID на свои:
# Secure Runtime Wrapper MVP policy
# -----------------------------
# USB physical key
# -----------------------------
SRW_KEY_FS_UUID="<YOUR_KEY_DEVICE_UUID>"
SRW_KEY_LABEL="SRWKEY"
SRW_KEY_DEVICE="/dev/disk/by-uuid/${SRW_KEY_FS_UUID}"
SRW_KEY_MOUNTPOINT="/mnt/srw-key"
SRW_KEYFILE="/mnt/srw-key/srw.key"
# -----------------------------
# Encrypted workspace
# -----------------------------
SRW_LUKS_UUID="<YOUR_LUKS_ENV_UUID>"
SRW_LUKS_DEVICE="/dev/disk/by-uuid/${SRW_LUKS_UUID}"
SRW_MAPPER_NAME="srw_secure"
SRW_MAPPER_PATH="/dev/mapper/${SRW_MAPPER_NAME}"
SRW_WORKSPACE="/mnt/srw"
# -----------------------------
# Filesystem options
# -----------------------------
SRW_MOUNT_OPTIONS="rw,noatime"
# -----------------------------
# Lock behavior
# -----------------------------
SRW_UNMOUNT_KEY_ON_LOCK="yes"
Права:
sudo chmod 0644 /etc/srw/policy.conf
sudo chown root:root /etc/srw/policy.conf
2.3. Создаём srw-status
sudo nano /usr/local/sbin/srw-status
#!/usr/bin/env bash
set -euo pipefail
POLICY="/etc/srw/policy.conf"
if [ ! -r "$POLICY" ]; then
echo "SRW: policy file not found: $POLICY"
exit 1
fi
# shellcheck disable=SC1090
source "$POLICY"
echo "SRW status"
echo "----------"
echo "USB key"
echo " label: $SRW_KEY_LABEL"
echo " fs uuid: $SRW_KEY_FS_UUID"
echo " device: $SRW_KEY_DEVICE"
echo " mountpoint: $SRW_KEY_MOUNTPOINT"
if [ -e "$SRW_KEY_DEVICE" ]; then
echo " device found: yes"
else
echo " device found: no"
fi
if mountpoint -q "$SRW_KEY_MOUNTPOINT"; then
echo " mounted: yes"
else
echo " mounted: no"
fi
if [ -r "$SRW_KEYFILE" ]; then
echo " keyfile: readable"
else
echo " keyfile: not readable"
fi
echo
echo "LUKS workspace"
echo " luks uuid: $SRW_LUKS_UUID"
echo " device: $SRW_LUKS_DEVICE"
echo " mapper name: $SRW_MAPPER_NAME"
echo " mapper path: $SRW_MAPPER_PATH"
echo " mountpoint: $SRW_WORKSPACE"
if [ -e "$SRW_LUKS_DEVICE" ]; then
echo " device found: yes"
else
echo " device found: no"
fi
if /sbin/cryptsetup status "$SRW_MAPPER_NAME" >/dev/null 2>&1; then
echo " mapper open: yes"
else
echo " mapper open: no"
fi
if mountpoint -q "$SRW_WORKSPACE"; then
echo " mounted: yes"
else
echo " mounted: no"
fi
if findmnt "$SRW_WORKSPACE" >/dev/null 2>&1; then
echo
echo "Mount details:"
findmnt "$SRW_WORKSPACE"
fi
Права:
sudo chmod 0750 /usr/local/sbin/srw-status
sudo chown root:root /usr/local/sbin/srw-status
Проверяем:
sudo srw-status
2.4. Создаём srw-unlock
sudo nano /usr/local/sbin/srw-unlock
#!/usr/bin/env bash
set -euo pipefail
POLICY="/etc/srw/policy.conf"
if [ ! -r "$POLICY" ]; then
echo "SRW: policy file not found: $POLICY"
exit 1
fi
# shellcheck disable=SC1090
source "$POLICY"
echo "SRW: unlocking workspace"
# 1. Validate USB key device path
if [ ! -e "$SRW_KEY_DEVICE" ]; then
echo "SRW: USB key device not found: $SRW_KEY_DEVICE"
exit 1
fi
# 2. Validate LUKS device path
if [ ! -e "$SRW_LUKS_DEVICE" ]; then
echo "SRW: LUKS device not found: $SRW_LUKS_DEVICE"
exit 1
fi
# 3. Verify USB identity
ACTUAL_USB_UUID="$(/sbin/blkid -s UUID -o value "$SRW_KEY_DEVICE" 2>/dev/null || true)"
ACTUAL_USB_TYPE="$(/sbin/blkid -s TYPE -o value "$SRW_KEY_DEVICE" 2>/dev/null || true)"
if [ "$ACTUAL_USB_UUID" != "$SRW_KEY_FS_UUID" ]; then
echo "SRW: USB UUID mismatch"
exit 1
fi
if [ "$ACTUAL_USB_TYPE" != "ext4" ]; then
echo "SRW: USB filesystem type mismatch: $ACTUAL_USB_TYPE"
exit 1
fi
# 4. Verify LUKS identity
ACTUAL_LUKS_UUID="$(/sbin/blkid -s UUID -o value "$SRW_LUKS_DEVICE" 2>/dev/null || true)"
ACTUAL_LUKS_TYPE="$(/sbin/blkid -s TYPE -o value "$SRW_LUKS_DEVICE" 2>/dev/null || true)"
if [ "$ACTUAL_LUKS_UUID" != "$SRW_LUKS_UUID" ]; then
echo "SRW: LUKS UUID mismatch"
exit 1
fi
if [ "$ACTUAL_LUKS_TYPE" != "crypto_LUKS" ]; then
echo "SRW: LUKS device type mismatch: $ACTUAL_LUKS_TYPE"
exit 1
fi
# 5. Ensure USB key mountpoint exists
mkdir -p "$SRW_KEY_MOUNTPOINT"
# 6. Mount USB key if needed
if ! mountpoint -q "$SRW_KEY_MOUNTPOINT"; then
echo "SRW: mounting USB key $SRW_KEY_LABEL"
mount "$SRW_KEY_DEVICE" "$SRW_KEY_MOUNTPOINT"
else
echo "SRW: USB key already mounted"
fi
# 7. Check keyfile
if [ ! -r "$SRW_KEYFILE" ]; then
echo "SRW: keyfile is not readable: $SRW_KEYFILE"
exit 1
fi
# 8. Open LUKS mapper if needed
if ! /sbin/cryptsetup status "$SRW_MAPPER_NAME" >/dev/null 2>&1; then
echo "SRW: opening LUKS mapper"
/sbin/cryptsetup open "$SRW_LUKS_DEVICE" "$SRW_MAPPER_NAME" --key-file "$SRW_KEYFILE"
else
echo "SRW: LUKS mapper already open"
fi
# 9. Ensure workspace mountpoint exists
mkdir -p "$SRW_WORKSPACE"
# 10. Mount workspace if needed
if ! mountpoint -q "$SRW_WORKSPACE"; then
echo "SRW: mounting workspace"
mount -o "$SRW_MOUNT_OPTIONS" "$SRW_MAPPER_PATH" "$SRW_WORKSPACE"
else
echo "SRW: workspace already mounted"
fi
# 11. Ensure base workspace layout exists
mkdir -p \
"$SRW_WORKSPACE/projects" \
"$SRW_WORKSPACE/.runtime" \
"$SRW_WORKSPACE/.home" \
"$SRW_WORKSPACE/.cache" \
"$SRW_WORKSPACE/.config" \
"$SRW_WORKSPACE/.local/share" \
"$SRW_WORKSPACE/.local/state"
chmod 700 \
"$SRW_WORKSPACE/.runtime" \
"$SRW_WORKSPACE/.home" \
"$SRW_WORKSPACE/.cache" \
"$SRW_WORKSPACE/.config" \
"$SRW_WORKSPACE/.local" \
"$SRW_WORKSPACE/.local/share" \
"$SRW_WORKSPACE/.local/state" || true
echo "SRW: workspace unlocked"
Права:
sudo chmod 0750 /usr/local/sbin/srw-unlock
sudo chown root:root /usr/local/sbin/srw-unlock
2.5. Создаём srw-lock
sudo nano /usr/local/sbin/srw-lock
#!/usr/bin/env bash
set -euo pipefail
POLICY="/etc/srw/policy.conf"
if [ ! -r "$POLICY" ]; then
echo "SRW: policy file not found: $POLICY"
exit 1
fi
# shellcheck disable=SC1090
source "$POLICY"
echo "SRW: locking workspace"
# 1. Unmount workspace if mounted
if mountpoint -q "$SRW_WORKSPACE"; then
echo "SRW: unmounting workspace"
umount "$SRW_WORKSPACE"
else
echo "SRW: workspace is not mounted"
fi
# 2. Close LUKS mapper if present
if [ -e "$SRW_MAPPER_PATH" ]; then
echo "SRW: closing LUKS mapper"
/sbin/cryptsetup close "$SRW_MAPPER_NAME"
else
echo "SRW: LUKS mapper is already closed"
fi
# 3. Optionally unmount USB key
if [ "${SRW_UNMOUNT_KEY_ON_LOCK:-yes}" = "yes" ]; then
if mountpoint -q "$SRW_KEY_MOUNTPOINT"; then
echo "SRW: unmounting USB key"
umount "$SRW_KEY_MOUNTPOINT"
fi
fi
echo "SRW: workspace locked"
Права:
sudo chmod 0750 /usr/local/sbin/srw-lock
sudo chown root:root /usr/local/sbin/srw-lock
2.6. Проверяем цикл
sudo srw-unlock
sudo srw-status
echo "hello from secure workspace" | sudo tee /mnt/srw/projects/test.txt
sudo srw-lock
sudo srw-status
cat /mnt/srw/projects/test.txt
После
srw-lock файл недоступен, потому что /mnt/srw больше не содержит смонтированный encrypted workspace.Открываем снова:
sudo srw-unlock
cat /mnt/srw/projects/test.txt
Ожидаем:
hello from secure workspace
Этап 3. Неприятное открытие: флешка сама стала ключом
Вот теперь становится заметна проблема.
Мы закрываем workspace:
sudo srw-lock
Извлекаем флешку.
Потом вставляем её обратно и вызываем:
sudo srw-unlock
И workspace снова открывается без дополнительного пароля.
На первый взгляд это выглядит как баг.
Но это не баг. Это следствие нашей модели.
Мы сами добавили raw keyfile в LUKS:
sudo cryptsetup luksAddKey /dev/sda3 /mnt/srw-key/srw.key
И потом научили srw-unlock использовать его:
cryptsetup open ... --key-file /mnt/srw-key/srw.key
То есть текущая модель такая:
есть флешка → есть ключ → можно открыть workspace
Это защищает только сценарий: устройство забрали без флешки
Но плохо защищает сценарий: устройство и флешку забрали вместе
Значит, raw keyfile на флешке — слабая основа.
Нам нужна схема:
флешка + пароль
Этап 4. Переход на модель USB + пароль
Теперь меняем модель.
Было:
USB содержит raw LUKS keyfile:
/mnt/srw-key/srw.key
srw-unlock:
cryptsetup open ... --key-file /mnt/srw-key/srw.key
Стало:
USB содержит зашифрованный keyfile:
/mnt/srw-key/srw.key.gpg
srw-unlock:
1. спрашивает пароль;
2. расшифровывает srw.key.gpg во временный /run/srw/srw.key;
3. открывает LUKS через временный keyfile;
4. удаляет временный keyfile.
Схема:
/mnt/srw-key/srw.key.gpg encrypted keyfile на USB
+
пароль пользователя
↓
/run/srw/srw.key временный raw key в tmpfs
↓
cryptsetup open
↓
rm /run/srw/srw.key
Теперь флешка без пароля бесполезна.
4.1. Создаём encrypted keyfile
USB должен быть смонтирован, а старый
/mnt/srw-key/srw.key пока должен существовать.Создаём временный GPG home в
/run, чтобы не писать служебное состояние GPG в обычный $HOME:sudo mkdir -p /run/srw/gnupg
sudo chmod 0700 /run/srw/gnupg
Шифруем raw keyfile:
sudo GNUPGHOME=/run/srw/gnupg gpg \
--symmetric \
--cipher-algo AES256 \
--no-symkey-cache \
--output /mnt/srw-key/srw.key.gpg \
/mnt/srw-key/srw.key
GPG запросит пароль. Это будет новый пароль для открытия workspace вместе с USB.
Задаём права:
sudo chmod 0400 /mnt/srw-key/srw.key.gpg
sudo chown root:root /mnt/srw-key/srw.key.gpg
sync
Проверяем:
sudo ls -l /mnt/srw-key/srw.key.gpg
4.2. Тестируем расшифровку
sudo mkdir -p /run/srw
sudo chmod 0700 /run/srw
Пробуем расшифровать:
sudo GNUPGHOME=/run/srw/gnupg gpg \
--decrypt \
--no-symkey-cache \
--output /run/srw/srw.key.test \
/mnt/srw-key/srw.key.gpg
Проверяем размер:
sudo ls -l /run/srw/srw.key.test
Ожидаем 4096 байт.
Удаляем тестовый файл:
sudo rm -f /run/srw/srw.key.test
4.3. Обновляем policy.conf
Открываем:
sudo nano /etc/srw/policy.conf
Старую строку:
SRW_KEYFILE="/mnt/srw-key/srw.key"
заменяем на:
# Encrypted keyfile stored on USB
SRW_ENCRYPTED_KEYFILE="/mnt/srw-key/srw.key.gpg"
# Runtime key material, tmpfs
SRW_RUNTIME_DIR="/run/srw"
SRW_RUNTIME_KEYFILE="/run/srw/srw.key"
SRW_GNUPGHOME="/run/srw/gnupg"
Итоговый USB-блок теперь выглядит так:
# -----------------------------
# USB physical key
# -----------------------------
SRW_KEY_FS_UUID="<YOUR_KEY_DEVICE_UUID>"
SRW_KEY_LABEL="SRWKEY"
SRW_KEY_DEVICE="/dev/disk/by-uuid/${SRW_KEY_FS_UUID}"
SRW_KEY_MOUNTPOINT="/mnt/srw-key"
# Encrypted keyfile stored on USB
SRW_ENCRYPTED_KEYFILE="/mnt/srw-key/srw.key.gpg"
# Runtime key material, tmpfs
SRW_RUNTIME_DIR="/run/srw"
SRW_RUNTIME_KEYFILE="/run/srw/srw.key"
SRW_GNUPGHOME="/run/srw/gnupg"
4.4. Обновляем srw-status
Открываем:
sudo nano /usr/local/sbin/srw-status
Меняем блок:
if [ -r "$SRW_KEYFILE" ]; then
echo " keyfile: readable"
else
echo " keyfile: not readable"
fi
На:
if [ -r "$SRW_ENCRYPTED_KEYFILE" ]; then
echo " encrypted keyfile: readable"
else
echo " encrypted keyfile: not readable"
fi
Можно также добавить путь:
echo " encrypted keyfile path: $SRW_ENCRYPTED_KEYFILE"
4.5. Обновляем srw-unlock
Открываем:
sudo nano /usr/local/sbin/srw-unlock
После:
source "$POLICY"
добавляем cleanup:
cleanup_runtime_key() {
if [ -n "${SRW_RUNTIME_KEYFILE:-}" ] && [ -f "$SRW_RUNTIME_KEYFILE" ]; then
rm -f "$SRW_RUNTIME_KEYFILE"
fi
}
trap cleanup_runtime_key EXIT
Теперь старый блок:
# 7. Check keyfile
if [ ! -r "$SRW_KEYFILE" ]; then
echo "SRW: keyfile is not readable: $SRW_KEYFILE"
exit 1
fi
# 8. Open LUKS mapper if needed
if ! /sbin/cryptsetup status "$SRW_MAPPER_NAME" >/dev/null 2>&1; then
echo "SRW: opening LUKS mapper"
/sbin/cryptsetup open "$SRW_LUKS_DEVICE" "$SRW_MAPPER_NAME" --key-file "$SRW_KEYFILE"
else
echo "SRW: LUKS mapper already open"
fi
заменяем на:
# 7. Check encrypted keyfile
if [ ! -r "$SRW_ENCRYPTED_KEYFILE" ]; then
echo "SRW: encrypted keyfile is not readable: $SRW_ENCRYPTED_KEYFILE"
exit 1
fi
# 8. Prepare runtime directory
mkdir -p "$SRW_RUNTIME_DIR"
chmod 0700 "$SRW_RUNTIME_DIR"
mkdir -p "$SRW_GNUPGHOME"
chmod 0700 "$SRW_GNUPGHOME"
# 9. Decrypt keyfile into tmpfs
echo "SRW: decrypting USB keyfile"
GNUPGHOME="$SRW_GNUPGHOME" gpg \
--decrypt \
--no-symkey-cache \
--output "$SRW_RUNTIME_KEYFILE" \
"$SRW_ENCRYPTED_KEYFILE"
chmod 0400 "$SRW_RUNTIME_KEYFILE"
# 10. Open LUKS mapper if needed
if ! /sbin/cryptsetup status "$SRW_MAPPER_NAME" >/dev/null 2>&1; then
echo "SRW: opening LUKS mapper"
/sbin/cryptsetup open "$SRW_LUKS_DEVICE" "$SRW_MAPPER_NAME" --key-file "$SRW_RUNTIME_KEYFILE"
else
echo "SRW: LUKS mapper already open"
fi
# 11. Remove runtime key immediately after use
rm -f "$SRW_RUNTIME_KEYFILE"
Остальная часть скрипта — создание mountpoint, mount workspace и создание базовой структуры — остаётся прежней.
4.6. Проверяем новую модель
Закрываем workspace:
sudo srw-lock
Проверяем статус:
sudo srw-status
Открываем снова:
sudo srw-unlock
Теперь должен появиться запрос пароля от GPG.
Проверяем, что временный raw keyfile удалён:
sudo ls -l /run/srw/srw.key
Ожидаем:
No such file or directory
4.7. Первая попытка убрать старый способ открытия
Теперь кажется логичным: раз мы сделали srw.key.gpg, значит старый raw srw.key больше не нужен.
На этом месте я сделал ход, который выглядит правильно, но на самом деле ломает схему.
Сначала убеждаемся, что старый raw keyfile ещё на месте:
sudo ls -l /mnt/srw-key/srw.key
И дальше первая мысль такая:
source /etc/srw/policy.conf
sudo cryptsetup luksRemoveKey "$SRW_LUKS_DEVICE" /mnt/srw-key/srw.key
После этого удалить raw keyfile с флешки:
sudo rm -f /mnt/srw-key/srw.key
sync
На бумаге звучит красиво:
raw keyfile удалили
на флешке остался только srw.key.gpg
значит теперь нужен USB + пароль
Я думал, что удаляю "старую небезопасную копию ключа"
А удалил ключ, который должен был появляться из
srw.key.gpg4.8. Ошибка: мы удалили не файл, а сам способ открыть LUKS
После перезагрузки или повторной попытки открытия можно получить такое:
root@debian:~# srw-unlock
SRW: unlocking workspace
SRW: USB key already mounted
SRW: decrypting USB keyfile
gpg: AES256.CFB encrypted data
gpg: encrypted with 1 passphrase
SRW: opening LUKS mapper
No key available with this passphrase.
На первый взгляд хочется думать, что проблема в GPG-пароле.
Но GPG как раз отработал нормально:
gpg: encrypted with 1 passphrase
Ошибка приходит позже — от
cryptsetup.Что реально произошло:
srw.key.gpg успешно расшифровался
↓
получился временный /run/srw/srw.key
↓
cryptsetup попробовал открыть LUKS этим keyfile
↓
LUKS ответил: такого ключа у меня больше нет
Почему?
Потому что
srw.key.gpg — это зашифрованная копия того самого srw.key.А мы только что сделали:
cryptsetup luksRemoveKey "$SRW_LUKS_DEVICE" /mnt/srw-key/srw.key
То есть удалили из LUKS keyslot ровно тот raw key, который потом пытались достать из
srw.key.gpg.Мы спрятали ключ в зашифрованный файл
Потом удалили этот ключ из LUKS
Теперь зашифрованный файл расшифровывается в ключ, который больше ничего не открывает
Это хорошая ошибка. Не приятная, но полезная. Она показывает, что мы смешали два разных действия: удалить raw keyfile с флешки и удалить keyslot из LUKS
Для нашей модели это не одно и то же.
4.9. Как правильно думать о srw.key.gpg
Новая модель не означает, что raw key исчезает из LUKS.
Она означает, что raw key больше не хранится на флешке в открытом виде.
Правильная схема:
LUKS всё ещё знает raw key
↑
│
/run/srw/srw.key
↑
│ расшифровали на короткое время
│
/mnt/srw-key/srw.key.gpg
↑
│
USB + пароль GPG
То есть:
raw key должен оставаться валидным LUKS-ключом,
но не должен лежать открытым файлом на USB
Вот правильное действие:
sudo rm -f /mnt/srw-key/srw.key
sync
А вот это действие в данной модели делать нельзя:
sudo cryptsetup luksRemoveKey "$SRW_LUKS_DEVICE" /mnt/srw-key/srw.key
Потому что
srw.key.gpg после расшифровки даёт тот же самый raw key.4.10. Как восстановиться, если ошибка уже совершена
Если вы уже удалили keyslot через
luksRemoveKey, но у вас остался recovery-пароль LUKS, схему можно восстановить.Сначала подгружаем policy:
source /etc/srw/policy.conf
Расшифровываем
srw.key.gpg во временный repair-файл:mkdir -p /run/srw/gnupg
chmod 0700 /run/srw/gnupg
GNUPGHOME=/run/srw/gnupg gpg \
--decrypt \
--no-symkey-cache \
--output /run/srw/srw.key.repair \
"$SRW_ENCRYPTED_KEYFILE"
Проверяем размер:
ls -l /run/srw/srw.key.repair
Ожидаем примерно 4096 байт.
Задаём права:
chmod 0400 /run/srw/srw.key.repair
Добавляем этот keyfile обратно в LUKS:
cryptsetup luksAddKey "$SRW_LUKS_DEVICE" /run/srw/srw.key.repair
Команда попросит действующий recovery-passphrase.
После этого можно проверить напрямую:
cryptsetup open "$SRW_LUKS_DEVICE" srw_test --key-file /run/srw/srw.key.repair
cryptsetup close srw_test
И удалить временный repair key:
rm -f /run/srw/srw.key.repair
Теперь штатный сценарий снова должен работать:
sudo srw-unlock
Эта ошибка — хороший повод не вычищать статью до стерильного состояния.
В реальной архитектуре важны не только команды, но и понимание: что именно мы удаляем, где живёт ключ, и какой слой за что отвечает.
4.11. Финальное правило для этой части
Итак, правильное завершение перехода на USB + пароль:
sudo rm -f /mnt/srw-key/srw.key
sync
Но не удаляем этот ключ из LUKS.
На флешке должен остаться:
srw.key.gpg а не srw.keyТеперь штатный доступ требует:
USB + пароль GPG
Recovery-пароль LUKS пока остаётся.
Этап 5. umount — это не lock
Это отдельная важная ловушка.
Если выполнить только:
sudo umount /mnt/srw
то файловая система исчезнет из дерева каталогов, но LUKS mapper может остаться открытым:
/dev/mapper/srw_secure всё ещё активен
ключ dm-crypt всё ещё в памяти ядра
Проверить:
sudo cryptsetup status srw_secure
Если mapper активен, то повторный mount:
sudo mount /dev/mapper/srw_secure /mnt/srw
не попросит ни пароль, ни флешку.
Это нормально, потому что:
mount/umount управляют файловой системой
cryptsetup open/close управляют шифрованным устройством
Правильное закрытие:
sudo umount /mnt/srw
sudo cryptsetup close srw_secure
Поэтому
srw-lock должен закрывать оба слоя.Усилим srw-lock
В ранней версии
srw-lock закрывает mapper, но лучше явно проверять результат.Обновлённый вариант:
#!/usr/bin/env bash
set -euo pipefail
POLICY="/etc/srw/policy.conf"
if [ ! -r "$POLICY" ]; then
echo "SRW: policy file not found: $POLICY"
exit 1
fi
# shellcheck disable=SC1090
source "$POLICY"
echo "SRW: locking workspace"
if mountpoint -q "$SRW_WORKSPACE"; then
echo "SRW: unmounting workspace"
umount "$SRW_WORKSPACE"
else
echo "SRW: workspace is not mounted"
fi
if /sbin/cryptsetup status "$SRW_MAPPER_NAME" >/dev/null 2>&1; then
echo "SRW: closing LUKS mapper"
/sbin/cryptsetup close "$SRW_MAPPER_NAME"
else
echo "SRW: LUKS mapper is already closed"
fi
if /sbin/cryptsetup status "$SRW_MAPPER_NAME" >/dev/null 2>&1; then
echo "SRW: ERROR: mapper is still active"
echo "SRW: workspace is not fully locked"
exit 1
fi
if [ "${SRW_UNMOUNT_KEY_ON_LOCK:-yes}" = "yes" ]; then
if mountpoint -q "$SRW_KEY_MOUNTPOINT"; then
echo "SRW: unmounting USB key"
umount "$SRW_KEY_MOUNTPOINT"
fi
fi
echo "SRW: workspace locked"
Теперь если mapper остался активен, команда не делает вид, что всё хорошо.
Финальный тест первой части
Проверяем весь цикл:
sudo srw-unlock
sudo srw-status
echo "hello from secure workspace" | sudo tee /mnt/srw/projects/test.txt
sudo srw-lock
sudo srw-status
sudo srw-unlock
cat /mnt/srw/projects/test.txt
Что хотим увидеть:
srw-unlock спрашивает пароль GPG;
workspace открывается;
тестовый файл доступен;
srw-lock закрывает mount и LUKS mapper;
после повторного открытия файл снова доступен.
Что получилось в первой части
Мы прошли путь от простой идеи к более честной модели.
Сначала:
USB содержит raw keyfile
Потом поняли:
флешка сама стала полноценным ключом
После этого изменили схему:
USB содержит encrypted keyfile;
пароль нужен для расшифровки;
raw key появляется только временно в /run.
А затем поймали ещё одну ошибку:
если encrypted keyfile — это зашифрованная копия raw key,
то нельзя удалять этот raw key из LUKS keyslot
Итоговая модель первой части:
штатное открытие:
USB с srw.key.gpg + пароль GPG
recovery:
LUKS recovery-passphrase
То есть:
(USB + пароль) OR recovery-passphrase
Что осталось за кадром
Эта часть не решает всё.
Пока у нас нет:
- автоматической реакции на извлечение флешки;
- panic-сценария;
- завершения процессов, которые держат workspace;
- managed launchers вроде
srw-run,srw-shell,srw-code; - минимизации следов в
$HOME,/tmp, XDG-каталогах; - namespace-изоляции;
- AppArmor/SELinux/Landlock;
- централизованных политик.
Слегка погодя...
Во второй части начнётся самое интересное.
Мы уже умеем открывать и закрывать workspace. Но пока он живёт как обычный mountpoint: любое приложение может открыть файл, зависнуть, оставить дескриптор и помешать закрытию.
Дальше нужно ответить на вопросы:
что делать, если workspace занят процессами?
как найти тех, кто держит /mnt/srw?
что должен делать panic-сценарий?
как реагировать на извлечение флешки через udev?
...
То есть часть 2 будет про: panic-flow и автоматическую реакцию на потерю USB-ключа
А часть 3 — про: managed launchers, trace minimization и более аккуратную рабочую среду
Первая часть закончена: у нас есть база, которая больше не держится на наивном “флешка сама всё открывает”.
Важное объявление:
Данный цикл статей пишется и публикуется исключительно на моём энтузиазме без какого-либо коммерческого сопровождения.
LIMB