LIMB
Вход Регистрация

тема

Secure Runtime Wrapper или защищаем Linux от криминалистов. Часть 1

MishkaUmka · · 8 ответов · 679 просмотров

Не удалось прогрузить медиа


Это первая часть серии из трёх. Здесь мы собираем базовый защищённый workspace на Linux. Я буду использовать Debian 13, сначала идём самым очевидным путём, затем видим его слабое место и исправляем модель

Статья ориентирована на Debian-based системы:
  • Debian;
  • Ubuntu;
  • Linux Mint;
  • ...
Если у вас Arch, Fedora, openSUSE или другой дистрибутив, сама идея остаётся такой же, но команды установки пакетов, пути до утилит и некоторые настройки могут отличаться.

О чём эта история
Идея родилась довольно бытовая:

А что, если сделать отдельное рабочее пространство, защищённое...

Условно: ранним утром послышался чудесный басистый стук в дверь со словами: "поллюция!" — послышалось, наверное...

И о чудо! Стук продолжается, и шумы уже стали более "механическими".

С трясущимися ляхами ты уже несёшься к вундервафле под названием "компьютер", дабы очистить историю браузера и никто не узнал, как ты гуглил: "скачать стилер бесплатно", "как установить питон без вирусов онлайн", "мануал по мамонтизации от хакер2281337", а заодно смыть это всё с винта. Негодяй!

Человек — существо интеллектом одарённое, а потому держать в голове: Да ладно. Удалю всё по-быстрому. К тому же я ваще там всякие материалы смотрел\читал, я ультра-мега прошаренный — показалось очень благоразумной идеей.

Не тут-то было! Дверь уже открыта, и ты применяешь секретную боевую горизонтальную позу под названием "мордой в пол".

Все твои пароли добываются путём выполнения комплекса оздоровительных практик от вполне компетентных в этом вопросе докторов: интенсивные дыхательные практики; электро-ректальная терапия; комплекс термической обработки с экстремальными температурами; и много-много чего ещё неизведанного...

Осознавая всё это, тебе ничего не остаётся кроме как скорчить кислую мину:

Не удалось прогрузить медиа


Потому я и тут, расскажу и покажу, как создать не “секретную папку”, не “магическую защиту от всего”, а нормальный технический прототип без всяких сложностей. Но лишь по началу...
  • есть обычная Linux-система;
  • есть отдельный зашифрованный раздел;
  • есть USB-флешка как физический фактор доступа;
  • есть команды, которые открывают и закрывают workspace.
Рабочее название:
  • SRW — Secure Runtime Wrapper
или проще:
  • Secure Workspace
В дальнейшем это может вырасти в управляемую защитную обёртку: с профилями запуска, panic-сценарием, минимизацией следов и нормальной политикой доступа. Но в первой части мы не забегаем вперёд.

Здесь мы сделаем базу.

Чем мы займёмся в этой части:

Мы пройдём три состояния.

Сначала — наивная, но полезная схема:

Rich (BB code):
USB-флешка
└── srw.key

LUKS-раздел
└── /dev/mapper/srw_secure
    └── ext4
        └── /mnt/srw

Потом добавим первые команды:
Bash:
srw-status
srw-unlock
srw-lock

А затем поймём важную вещь:
Если raw keyfile лежит на флешке, то сама флешка становится полноценным ключом.
То есть любой, у кого есть эта флешка, сможет открыть workspace.
Не удалось прогрузить медиа


Это действительно удобно и ничем не отличается от условного VeraCrypt с KeyFile

После этого мы перейдём к более правильной модели:

Rich (BB code):
USB-флешка
└── srw.key.gpg        зашифрованный keyfile

пароль пользователя
└── нужен, чтобы расшифровать srw.key.gpg

/run/srw/srw.key
└── временный raw keyfile в tmpfs

LUKS workspace
└── /mnt/srw

Итоговая формула: для открытия нужен USB + пароль

Важное ограничение

Сразу честно.

Если workspace уже открыт, то извлечение флешки само по себе не блокирует данные.

Не удалось прогрузить медиа


Rich (BB code):
LUKS mapper уже открыт;
ключ dm-crypt уже находится в памяти ядра;
файловая система уже смонтирована;
процессы могут держать открытые файлы.

Поэтому нельзя обещать: вытащил флешку — и всё мгновенно стало недоступно.

Корректная формулировка: и флешка, и пароль нужны для открытия workspace; после закрытия workspace он снова недоступен без этих факторов.

Аварийное закрытие при извлечении флешки — это тема следующей части.

Предупреждение

Команды ниже могут уничтожить данные.

Особенно опасны:
Bash:
mkfs.ext4 ...
cryptsetup luksFormat ...
dd if=/dev/urandom ...

Для первого прототипа лучше использовать:
  • виртуальную машину;
  • отдельную флешку;
  • отдельный тестовый раздел;
  • диск без важных данных.
Не используйте основной системный раздел.

Этап 1. Собираем самый простой LUKS workspace

На первом этапе делаем так:

Rich (BB code):
USB-флешка с keyfile
        ↓
LUKS-раздел на диске
        ↓
ручное открытие workspace
        ↓
ручное закрытие workspace

Это ещё не финальная модель безопасности. Это первая рабочая версия, которая поможет нам увидеть проблему.

1.1. Устанавливаем зависимости

Bash:
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. Определяем флешку и тестовый раздел

Смотрим устройства:
Bash:
lsblk -o NAME,PATH,SIZE,FSTYPE,LABEL,UUID,PARTUUID,MOUNTPOINTS,MODEL

Примерный вывод:

Rich (BB code):
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

В примерах дальше:
Rich (BB code):
USB partition:       /dev/sdb1
Secure partition:    /dev/sda3

У вас имена будут другими.

1.3. Форматируем флешку

Для MVP используем ext4 и label SRWKEY:
Bash:
sudo mkfs.ext4 -L SRWKEY /dev/sdb1

Проверяем:
Bash:
lsblk -f

Ожидаем примерно:
Rich (BB code):
sdb1 ext4 1.0 SRWKEY <UUID>

1.4. Монтируем флешку

Создаём точку монтирования:
Bash:
sudo mkdir -p /mnt/srw-key

Монтируем по label:
Bash:
sudo mount /dev/disk/by-label/SRWKEY /mnt/srw-key

Проверяем:
Bash:
findmnt /mnt/srw-key

Пример:

Rich (BB code):
TARGET       SOURCE    FSTYPE OPTIONS
/mnt/srw-key /dev/sdb1 ext4   rw,relatime

1.5. Создаём raw keyfile на флешке

Создаём случайный ключ длиной 4096 байт:
Bash:
sudo dd if=/dev/urandom of=/mnt/srw-key/srw.key bs=4096 count=1

Задаём права:

Bash:
sudo chmod 0400 /mnt/srw-key/srw.key
sudo chown root:root /mnt/srw-key/srw.key
sync

Проверяем:
Bash:
sudo ls -l /mnt/srw-key/srw.key

Ожидаем:
Rich (BB code):
-r-------- 1 root root 4096 ... /mnt/srw-key/srw.key

На этом этапе srw.key — обычный raw keyfile. Пока это выглядит удобно. Позже именно это станет проблемой.

1.6. Подготавливаем LUKS-раздел

Ещё раз проверяем, что выбран правильный раздел:
Bash:
lsblk -o NAME,PATH,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINTS,MODEL

Форматируем тестовый раздел как LUKS2:
Bash:
sudo cryptsetup luksFormat --type luks2 /dev/sda3

Команда попросит ввести YES, а затем passphrase.
Эта passphrase пока будет recovery-паролем. Она нужна, чтобы не потерять доступ во время разработки.

1.7. Открываем LUKS и создаём файловую систему

Открываем LUKS:
Bash:
sudo cryptsetup open /dev/sda3 srw_secure

Проверяем:
Bash:
sudo cryptsetup status srw_secure

Ожидаем:

Rich (BB code):
/dev/mapper/srw_secure is active.
  type:    LUKS2
  ...

Создаём ext4 внутри открытого mapper:
Bash:
sudo mkfs.ext4 -L SRWDATA /dev/mapper/srw_secure

Почему именно внутри mapper:
Rich (BB code):
/dev/sda3
└── LUKS-контейнер
    └── /dev/mapper/srw_secure
        └── ext4

Если создать ext4 напрямую на /dev/sda3, мы просто уничтожим LUKS.

1.8. Монтируем workspace

Bash:
sudo mkdir -p /mnt/srw
sudo mount /dev/mapper/srw_secure /mnt/srw

Создаём базовую структуру:
Bash:
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

Проверяем:
Bash:
findmnt /mnt/srw

Не удалось прогрузить медиа


1.9. Добавляем keyfile в LUKS

Добавляем srw.key как дополнительный способ открытия LUKS:
Bash:
sudo cryptsetup luksAddKey /dev/sda3 /mnt/srw-key/srw.key

Команда попросит существующий recovery-пароль LUKS.

Проверяем keyslots:
Bash:
sudo cryptsetup luksDump /dev/sda3

Важный момент:
Rich (BB code):
LUKS keyslot — это альтернативный способ получить доступ к master key.

Если есть пароль и keyfile, то логика такая:
пароль ИЛИ keyfile

А не такая:
пароль И keyfile

Запомним это. Скоро оно нам пригодится.

1.10. Проверяем открытие через USB keyfile

Закрываем workspace:
Bash:
sudo umount /mnt/srw
sudo cryptsetup close srw_secure

Проверяем:
Bash:
findmnt /mnt/srw || echo "workspace is not mounted"
sudo cryptsetup status srw_secure || echo "mapper is closed"

Открываем через keyfile:
Bash:
sudo cryptsetup open /dev/sda3 srw_secure --key-file /mnt/srw-key/srw.key

Монтируем:
Bash:
sudo mount /dev/mapper/srw_secure /mnt/srw

Проверяем:
Bash:
findmnt /mnt/srw

Если всё получилось, базовое ядро работает.

1.11. Что мы получили на первом этапе

У нас есть:
Rich (BB code):
флешка с raw keyfile;
LUKS-раздел;
workspace в /mnt/srw;
возможность открыть LUKS через USB.

На первый взгляд всё хорошо: без флешки keyfile недоступен.
Но на самом деле мы сделали флешку полноценным ключом.
Пока это просто заметка. Сначала завернём всё в CLI-слой, и проблема станет очевиднее.

Этап 2. Первый CLI-слой

Теперь вместо ручных команд делаем первые управляющие команды:
Rich (BB code):
srw-status   показать состояние workspace
srw-unlock   открыть workspace
srw-lock     закрыть workspace

Зачем это нужно?

Не ради красоты. А чтобы будущая система стала управляемой: конфиг отдельно, логика отдельно, потом можно добавлять профили, panic-сценарии и политики.

2.1. Сразу уходим от /dev/sdX

В ручных командах удобно писать:
Rich (BB code):
/dev/sda3
/dev/sdb1

Но для архитектуры это плохо.

Имена /dev/sdX могут меняться:
  • после перезагрузки;
  • после подключения другого диска;
  • после смены USB-порта;
  • в виртуальной машине;
  • при изменении порядка обнаружения устройств.
Получаем UUID:
Bash:
lsblk -o NAME,PATH,SIZE,FSTYPE,LABEL,UUID,PARTUUID,MOUNTPOINTS,MODEL

Или точечно:
Bash:
sudo blkid /dev/sdb1
sudo blkid /dev/sda3

Пример:
Rich (BB code):
/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

Bash:
sudo mkdir -p /etc/srw
sudo chmod 0755 /etc/srw
sudo nano /etc/srw/policy.conf

Вставляем, заменив UUID на свои:
Rich (BB code):
# 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"

Права:
Bash:
sudo chmod 0644 /etc/srw/policy.conf
sudo chown root:root /etc/srw/policy.conf

2.3. Создаём srw-status

Bash:
sudo nano /usr/local/sbin/srw-status
Bash:
#!/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

Права:
Bash:
sudo chmod 0750 /usr/local/sbin/srw-status
sudo chown root:root /usr/local/sbin/srw-status

Проверяем:
Bash:
sudo srw-status

2.4. Создаём srw-unlock

Bash:
sudo nano /usr/local/sbin/srw-unlock

Bash:
#!/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"

Права:
Bash:
sudo chmod 0750 /usr/local/sbin/srw-unlock
sudo chown root:root /usr/local/sbin/srw-unlock

2.5. Создаём srw-lock

Bash:
sudo nano /usr/local/sbin/srw-lock

Bash:
#!/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"

Права:
Bash:
sudo chmod 0750 /usr/local/sbin/srw-lock
sudo chown root:root /usr/local/sbin/srw-lock

2.6. Проверяем цикл

Bash:
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.

Открываем снова:
Bash:
sudo srw-unlock
cat /mnt/srw/projects/test.txt

Ожидаем:
Rich (BB code):
hello from secure workspace

Этап 3. Неприятное открытие: флешка сама стала ключом

Вот теперь становится заметна проблема.

Мы закрываем workspace:
Bash:
sudo srw-lock

Извлекаем флешку.

Потом вставляем её обратно и вызываем:
Bash:
sudo srw-unlock

И workspace снова открывается без дополнительного пароля.

На первый взгляд это выглядит как баг.
Но это не баг. Это следствие нашей модели.

Мы сами добавили raw keyfile в LUKS:
Bash:
sudo cryptsetup luksAddKey /dev/sda3 /mnt/srw-key/srw.key

И потом научили srw-unlock использовать его:
Bash:
cryptsetup open ... --key-file /mnt/srw-key/srw.key

То есть текущая модель такая:
Rich (BB code):
есть флешка → есть ключ → можно открыть workspace

Это защищает только сценарий: устройство забрали без флешки

Но плохо защищает сценарий: устройство и флешку забрали вместе


Значит, raw keyfile на флешке — слабая основа.

Нам нужна схема:
Rich (BB code):
флешка + пароль

Этап 4. Переход на модель USB + пароль

Теперь меняем модель.

Было:
Rich (BB code):
USB содержит raw LUKS keyfile:
  /mnt/srw-key/srw.key

srw-unlock:
  cryptsetup open ... --key-file /mnt/srw-key/srw.key

Стало:
Rich (BB code):
USB содержит зашифрованный keyfile:
  /mnt/srw-key/srw.key.gpg

srw-unlock:
  1. спрашивает пароль;
  2. расшифровывает srw.key.gpg во временный /run/srw/srw.key;
  3. открывает LUKS через временный keyfile;
  4. удаляет временный keyfile.

Схема:
Rich (BB code):
/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:
Bash:
sudo mkdir -p /run/srw/gnupg
sudo chmod 0700 /run/srw/gnupg

Шифруем raw keyfile:
Bash:
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.

Задаём права:
Bash:
sudo chmod 0400 /mnt/srw-key/srw.key.gpg
sudo chown root:root /mnt/srw-key/srw.key.gpg
sync

Проверяем:
Bash:
sudo ls -l /mnt/srw-key/srw.key.gpg

4.2. Тестируем расшифровку

Bash:
sudo mkdir -p /run/srw
sudo chmod 0700 /run/srw

Пробуем расшифровать:
Bash:
sudo GNUPGHOME=/run/srw/gnupg gpg \
  --decrypt \
  --no-symkey-cache \
  --output /run/srw/srw.key.test \
  /mnt/srw-key/srw.key.gpg

Проверяем размер:
Bash:
sudo ls -l /run/srw/srw.key.test

Ожидаем 4096 байт.

Удаляем тестовый файл:
Bash:
sudo rm -f /run/srw/srw.key.test

4.3. Обновляем policy.conf

Открываем:
Bash:
sudo nano /etc/srw/policy.conf

Старую строку:
Rich (BB code):
SRW_KEYFILE="/mnt/srw-key/srw.key"

заменяем на:
Rich (BB code):
# 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-блок теперь выглядит так:
Rich (BB code):
# -----------------------------
# 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

Открываем:
Bash:
sudo nano /usr/local/sbin/srw-status

Меняем блок:
Rich (BB code):
if [ -r "$SRW_KEYFILE" ]; then
  echo "  keyfile:      readable"
else
  echo "  keyfile:      not readable"
fi

На:
Rich (BB code):
if [ -r "$SRW_ENCRYPTED_KEYFILE" ]; then
  echo "  encrypted keyfile: readable"
else
  echo "  encrypted keyfile: not readable"
fi

Можно также добавить путь:
Bash:
echo "  encrypted keyfile path: $SRW_ENCRYPTED_KEYFILE"

4.5. Обновляем srw-unlock

Открываем:
Bash:
sudo nano /usr/local/sbin/srw-unlock

После:
Bash:
source "$POLICY"

добавляем cleanup:
Rich (BB code):
cleanup_runtime_key() {
  if [ -n "${SRW_RUNTIME_KEYFILE:-}" ] && [ -f "$SRW_RUNTIME_KEYFILE" ]; then
    rm -f "$SRW_RUNTIME_KEYFILE"
  fi
}

trap cleanup_runtime_key EXIT

Теперь старый блок:
Rich (BB code):
# 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

заменяем на:
Rich (BB code):
# 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:
Bash:
sudo srw-lock

Проверяем статус:
Bash:
sudo srw-status

Открываем снова:
Bash:
sudo srw-unlock

Теперь должен появиться запрос пароля от GPG.

Проверяем, что временный raw keyfile удалён:
Bash:
sudo ls -l /run/srw/srw.key

Ожидаем:
Rich (BB code):
No such file or directory

4.7. Первая попытка убрать старый способ открытия

Теперь кажется логичным: раз мы сделали srw.key.gpg, значит старый raw srw.key больше не нужен.

На этом месте я сделал ход, который выглядит правильно, но на самом деле ломает схему.

Сначала убеждаемся, что старый raw keyfile ещё на месте:
Bash:
sudo ls -l /mnt/srw-key/srw.key

И дальше первая мысль такая:
Bash:
source /etc/srw/policy.conf
sudo cryptsetup luksRemoveKey "$SRW_LUKS_DEVICE" /mnt/srw-key/srw.key

После этого удалить raw keyfile с флешки:
Bash:
sudo rm -f /mnt/srw-key/srw.key
sync

На бумаге звучит красиво:
Rich (BB code):
raw keyfile удалили
на флешке остался только srw.key.gpg
значит теперь нужен USB + пароль

Я думал, что удаляю "старую небезопасную копию ключа"

А удалил ключ, который должен был появляться из srw.key.gpg

Не удалось прогрузить медиа


4.8. Ошибка: мы удалили не файл, а сам способ открыть LUKS

После перезагрузки или повторной попытки открытия можно получить такое:
Rich (BB code):
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 как раз отработал нормально:
Rich (BB code):
gpg: encrypted with 1 passphrase

Ошибка приходит позже — от cryptsetup.

Что реально произошло:
Rich (BB code):
srw.key.gpg успешно расшифровался
↓
получился временный /run/srw/srw.key
↓
cryptsetup попробовал открыть LUKS этим keyfile
↓
LUKS ответил: такого ключа у меня больше нет

Почему?

Потому что srw.key.gpg — это зашифрованная копия того самого srw.key.

А мы только что сделали:
Rich (BB code):
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 больше не хранится на флешке в открытом виде.

Правильная схема:
Rich (BB code):
LUKS всё ещё знает raw key
        ↑
        │
/run/srw/srw.key
        ↑
        │ расшифровали на короткое время
        │
/mnt/srw-key/srw.key.gpg
        ↑
        │
USB + пароль GPG

То есть:
Rich (BB code):
raw key должен оставаться валидным LUKS-ключом,
но не должен лежать открытым файлом на USB

Вот правильное действие:
Bash:
sudo rm -f /mnt/srw-key/srw.key
sync

А вот это действие в данной модели делать нельзя:
Bash:
sudo cryptsetup luksRemoveKey "$SRW_LUKS_DEVICE" /mnt/srw-key/srw.key

Потому что srw.key.gpg после расшифровки даёт тот же самый raw key.

4.10. Как восстановиться, если ошибка уже совершена

Если вы уже удалили keyslot через luksRemoveKey, но у вас остался recovery-пароль LUKS, схему можно восстановить.

Сначала подгружаем policy:
Bash:
source /etc/srw/policy.conf

Расшифровываем srw.key.gpg во временный repair-файл:
Bash:
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"

Проверяем размер:
Bash:
ls -l /run/srw/srw.key.repair

Ожидаем примерно 4096 байт.

Задаём права:
Bash:
chmod 0400 /run/srw/srw.key.repair

Добавляем этот keyfile обратно в LUKS:
Rich (BB code):
cryptsetup luksAddKey "$SRW_LUKS_DEVICE" /run/srw/srw.key.repair

Команда попросит действующий recovery-passphrase.

После этого можно проверить напрямую:
Bash:
cryptsetup open "$SRW_LUKS_DEVICE" srw_test --key-file /run/srw/srw.key.repair
cryptsetup close srw_test

И удалить временный repair key:
Bash:
rm -f /run/srw/srw.key.repair

Теперь штатный сценарий снова должен работать:
Bash:
sudo srw-unlock

Эта ошибка — хороший повод не вычищать статью до стерильного состояния.
В реальной архитектуре важны не только команды, но и понимание: что именно мы удаляем, где живёт ключ, и какой слой за что отвечает.

4.11. Финальное правило для этой части

Итак, правильное завершение перехода на USB + пароль:
Bash:
sudo rm -f /mnt/srw-key/srw.key
sync

Но не удаляем этот ключ из LUKS.

На флешке должен остаться: srw.key.gpg а не srw.key

Теперь штатный доступ требует:
Rich (BB code):
USB + пароль GPG

Recovery-пароль LUKS пока остаётся.

Этап 5. umount — это не lock

Это отдельная важная ловушка.

Если выполнить только:
Bash:
sudo umount /mnt/srw

то файловая система исчезнет из дерева каталогов, но LUKS mapper может остаться открытым:
Rich (BB code):
/dev/mapper/srw_secure всё ещё активен
ключ dm-crypt всё ещё в памяти ядра

Проверить:
Bash:
sudo cryptsetup status srw_secure

Если mapper активен, то повторный mount:
Bash:
sudo mount /dev/mapper/srw_secure /mnt/srw

не попросит ни пароль, ни флешку.

Это нормально, потому что:
Rich (BB code):
mount/umount управляют файловой системой
cryptsetup open/close управляют шифрованным устройством

Правильное закрытие:
Bash:
sudo umount /mnt/srw
sudo cryptsetup close srw_secure

Поэтому srw-lock должен закрывать оба слоя.

Усилим srw-lock
В ранней версии srw-lock закрывает mapper, но лучше явно проверять результат.

Обновлённый вариант:
Bash:
#!/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 остался активен, команда не делает вид, что всё хорошо.

Финальный тест первой части

Проверяем весь цикл:
Bash:
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

Что хотим увидеть:
Rich (BB code):
srw-unlock спрашивает пароль GPG;
workspace открывается;
тестовый файл доступен;
srw-lock закрывает mount и LUKS mapper;
после повторного открытия файл снова доступен.

Не удалось прогрузить медиа


Что получилось в первой части

Мы прошли путь от простой идеи к более честной модели.

Сначала:
Rich (BB code):
USB содержит raw keyfile

Потом поняли:
Rich (BB code):
флешка сама стала полноценным ключом

После этого изменили схему:
Rich (BB code):
USB содержит encrypted keyfile;
пароль нужен для расшифровки;
raw key появляется только временно в /run.

А затем поймали ещё одну ошибку:
Rich (BB code):
если encrypted keyfile — это зашифрованная копия raw key,
то нельзя удалять этот raw key из LUKS keyslot

Итоговая модель первой части:
Rich (BB code):
штатное открытие:
  USB с srw.key.gpg + пароль GPG

recovery:
  LUKS recovery-passphrase

То есть:
Rich (BB code):
(USB + пароль) OR recovery-passphrase

Что осталось за кадром

Эта часть не решает всё.

Пока у нас нет:
  • автоматической реакции на извлечение флешки;
  • panic-сценария;
  • завершения процессов, которые держат workspace;
  • managed launchers вроде srw-run, srw-shell, srw-code;
  • минимизации следов в $HOME, /tmp, XDG-каталогах;
  • namespace-изоляции;
  • AppArmor/SELinux/Landlock;
  • централизованных политик.
И это нормально. Это только начало

Слегка погодя...

Во второй части начнётся самое интересное.

Мы уже умеем открывать и закрывать workspace. Но пока он живёт как обычный mountpoint: любое приложение может открыть файл, зависнуть, оставить дескриптор и помешать закрытию.

Дальше нужно ответить на вопросы:
Rich (BB code):
что делать, если workspace занят процессами?
как найти тех, кто держит /mnt/srw?
что должен делать panic-сценарий?
как реагировать на извлечение флешки через udev?
...

То есть часть 2 будет про: panic-flow и автоматическую реакцию на потерю USB-ключа

А часть 3 — про: managed launchers, trace minimization и более аккуратную рабочую среду


Первая часть закончена: у нас есть база, которая больше не держится на наивном “флешка сама всё открывает”.

Важное объявление: 
Данный цикл статей пишется и публикуется исключительно на моём энтузиазме без какого-либо коммерческого сопровождения.
👍33 ❤️4 🔥8 👏6 😢9 💯4
Отличная статья! Интересная интерпретация крипто-контейнеров на базе LUKS. Очень понравилось что много практики и особенно разбор ошибок при настройке.

Жду продолжения =) Было бы здорово в следующих частях добавить  чтобы при вводе специальной фразы происходило уничтожение ключа. Так как в реальности пароль могут попытаться просто "выбить".
👍2
Ответhipeople

Отличная статья! Интересная интерпретация крипто-контейнеров на базе LUKS. Очень понравилось что много практики и особенно разбор ошибок при настройке. Жду продолжения =) Было бы здорово в следующих частях добавить…

Спасибо, приятно!
Как и указал в статье 

Эта ошибка — хороший повод не вычищать статью до стерильного состояния.

В реальной архитектуре важны не только команды, но и понимание: что именно мы удаляем, где живёт ключ, и какой слой за что отвечает.

Касательно твоего предложения по поводу "уничтожение ключа" ... — Идея очень занятная. Я уже думал над ней, но решил не добавлять в прототип. Однако теперь у меня появился повод задуматься. Это все же отличный совет. Спасибо!

Я уже работаю над второй частью. На холодную голову заметил несколько вполне очевидных ошибок в первой части, например: пусть и кратковременный, но дроп ключа на диск, вместо использования "pipe" — "|"
Если есть предложить или раскритиковать что еще — я весь внимание

Пара глаз никогда не бывает лишней :)
👍4
Хороший и достаточно точный материал, подробно раскрывающий тему.. больше мне тут сказать нечего =)

Одно только замечание, оставляйте на будущее пользователям предупреждения про такие штуки, как LUKS или те же веракрипт и проч. Образы виртуалок часто под ним ломаются, иногда так же себя ведет закрытый под пасс файл-бд от кипасса.. следует быть аккуратнее и все бекапнуть).

Неточностей, ломающих гайд, нет, так что, лишь пожелаю удачи в дальнейшем творчестве! Будем ждать следующих материалов).
👍3
Спасибо за ответ!
ОтветleakinN_nname

следует быть аккуратнее и все бекапнуть).

 Верно подмечено, это легко забыть или упустить вовсе по незнанию — добавлю акцент в продолжении
👍3
ОтветMishkaUmka

Условно: ранним утром послышался чудесный басистый стук в дверь со словами: "поллюция!" — послышалось, наверное...

а как твой способ поможет если полиция попросит флешку и пароль?
👍2
Ответcybergangster

а как твой способ поможет если полиция попросит флешку и пароль?

Первая часть делается по принципу "чтобы работало" — Proof of Concept.
Так делается всегда при разработке. Засорять "mvp", а конкретно Подготовительную часть сразу всем функционалом — нецелесообразно
Это отнюдь не значит, что эта тема не поднимется в будущем. Это подмечено в статье:
В дальнейшем это может вырасти в управляемую защитную обёртку: с профилями запуска, panic-сценарием, минимизацией следов и нормальной политикой доступа. Но в первой части мы не забегаем вперёд.
Здесь мы сделаем базу.
И:
То есть часть 2 будет про: panic-flow и автоматическую реакцию на потерю USB-ключа

А часть 3 — про: managed launchers, trace minimization и более аккуратную рабочую среду
Также важно учесть, что никакой публичный или приватный гайд не гарантирует полную защищённость при абсолютно любом сценарии. Перед задержанием оперативники всегда пробивают вас и готовятся к задержанию — если решат штурмовать со светошумовой гранатой, у вас будет не больше двух секунд, чтоб среагировать. Поэтому каждый должен самостоятельно настраивать свой опсек в зависимости от своих целей и задач с полным пониманием, какие методы воздействия к нему могут быть применены.

Если быть проще, то вот аналогия: статья описывает, как настроить дверь, но не описывает, как правильно ставить камеры, где нанимать охрану и т. д.
👍5
ОтветMishkaUmka

Первая часть делается по принципу "чтобы работало" — Proof of Concept. Так делается всегда при разработке. Засорять "mvp", а конкретно Подготовительную часть сразу всем функционалом — нецелесообразно Это отнюдь не…

Материал на самом деле интересный вы демонстрируете как создать систему доступ к которой возможен только имея на руках флешку и пароль. Но вся схема рушится, если придут с обыском и попросят флешку, воткнут ее, попросят пароль) Нужно делать двойное дно. Воткнул флешку - появилась система рабочая или же воткнул флешку ввел один пароль появилась одна система, ввел другой пароль появилась вторая система (левая которую можно показать кому угодно).
👍5 ❤️1 🔥1
Ответcybergangster

Материал на самом деле интересный вы демонстрируете как создать систему доступ к которой возможен только имея на руках флешку и пароль. Но вся схема рушится, если придут с обыском и попросят флешку, воткнут ее, попросят…

Как раз будущие части будут об этом и не только!
Ждите продолжение
👍2

Гостевой просмотр. Чтобы отвечать и ставить реакции, войдите или зарегистрируйтесь.