Если у вас паранойя…

от автора

Помните, был такой фильм: хакеры добыли некую секретную информацию, которую можно было прочитать только на определенном компьютере, находящемся в особо охраняемом помещении, потому что на других компьютерах ее нельзя было расшифровать…

На самом деле это довольно несложно делается, попробую показать на примере.
(«промышленные», сертифицированные и прочие решения не рассматриваются потому что потому)

Итак, условие номер один: информация из некой секретной папки не должна быть доступна посторонним.

Очевидное решение — шифрование. При этом также очевидно, что в зашифрованном виде ее могут украсть, так или иначе, например вместе с компьютером.

Но у шифрования есть минус: вы знаете пароль, и если вас настоятельно попросить — вы его скажете. Значит, это и будет условие номер два: вы не можете рассказать пароль. Захотите — но не сможете.

Однако, не зная пароля — вы должны получать при этом доступ к своим файлам.
Это означает, что открываться защищенная папка должна сама, но только при «штатной» работе, и не открываться, если что‑то пошло не так.
Это условие номер три — работает автоматически.

Можно привязать пароль к каким‑то аппаратным свойствам компьютера — но если выкрасть весь компьютер — будет выкраден и ключ к папке. Значит, ключ должен находиться вне компьютера, но в доступе к нему.
Но что, если злоумышленники украли не только один компьютер, но и весь офис? А вам не дают ничего делать.
Тогда ключ должен быть таким, чтобы третье лицо могло в любой момент уничтожить ключ, возможно даже не зная что именно делает, «не вызывая подозрения санитаров».
Это — четвертое условие: ключ физически не привязан к компьютеру и не должен выглядеть как ключ для постороннего наблюдателя, зато должен быть легко уничтожим.

Вот это сейчас и реализуем:

В качестве средства шифрования используем LUKS. Это непринципиально, просто cryptsetup уже есть.
Чтобы не знать пароль — нужно использовать ключевой файл. LUKS работает с 512-битными ключами — это 64 байта данных, запомнить и рассказать вы не сможете.
Управлять подключением секретного диска можно через PAM на компьютере — всё будет происходить аввтоматически.
Получить ключ можно по сети, да вот хотя бы по http с какого‑нибудь веб‑сервера. Причем это может быть хоть корпоративный сервер, хоть удаленный сайт, хоть плата esp8266 в стене за гипсокартоном.

И чтобы ключ не выглядел как ключ для посторонних — в качестве ключа можно использовать самый обычный файл, хоть картинку, хоть текст. Взяв от него 512-битный хеш — и получим тот самый уникальный секретный ключ.
А удалить его очень просто: заменить картинку на сайте, пусть даже на похожую. Как именно это сделать — совсем другой вопрос, но он решаемый.

Для этого пишем такой скрипт (/usr/local/bin/paranoid):

#!/bin/bash  # на всякий случай подключаем /sbin PATH=/sbin:$PATH  # установка общих переменных setvars(){   HOME_DIR=$(getent passwd "$USER" | cut -d: -f6)   MOUNT_POINT="$HOME_DIR/edisk"    TMPFS_DIR="/tmp/tmpfs_key_$USER"   IMG_FILE="$TMPFS_DIR/image.bin"   KEY_FILE="$TMPFS_DIR/secret.key"   CRYPT_NAME="edisk_$USER" }  # проверка наличия установленных программ checks(){   which awk grep mount umount wget sha512sum xxd cryptsetup mkfs.ext4   if [ $? -ne 0 ] ; then     # если чего-то нет - завершаем работу, но "успешно", чтобы не вызывать ошибок     exit 0   fi }  # читаем конфигурационный файд ля юзера getconfig(){   CONFIG="/etc/paranoid/users/$USER.conf"    DATA_DIR=$(sudo awk -F= '/^data_dir/ {gsub(/^ +| +$/, "", $2); print $2}' $CONFIG)   DISK_IMAGE=$(sudo awk -F= '/^disk_image/ {gsub(/^ +| +$/, "", $2); print $2}' $CONFIG)   SECRET=$(sudo awk -F= '/^secret/ {gsub(/^ +| +$/, "", $2); print $2}' $CONFIG)    if [ "x$DISK_IMAGE" = "x" ] || [ "x$SECRET" = "x" ] || [ "x$DATA_DIR" = "x" ] ; then     echo "No <disk_image> or <secret> found!"     exit 0   fi    DISK_IMAGE="$DATA_DIR/$DISK_IMAGE" }  # проверка на "открытость" is_mapped() {   ls /dev/mapper | grep -q "$CRYPT_NAME" }  # проверка на смонтированность is_mounted() {   mount | grep -q "$MOUNT_POINT" }  # самая главная часть - получение ключа fetch_key() {   mkdir -p "$TMPFS_DIR"   if [ $? -ne 0 ] ; then     exit 0   fi   sudo chmod 700 "$TMPFS_DIR"   # монтируем временную ФС   sudo mount -t tmpfs -o size=1M tmpfs "$TMPFS_DIR"    # получаем ключ по указанному URL   # ничего не мешает получать по 2-3 ключа из разных источников и   # конкатенировать их в один файл   wget -q -O "$IMG_FILE" "$SECRET"    if [ $? -ne 0 ] || [ ! -f "$IMG_FILE" ] ; then     echo "No key file loaded!"     sudo umount "$TMPFS_DIR"     rmdir "$TMPFS_DIR"     exit 0   fi    # формируем 512-битный ключ   sha512sum "$IMG_FILE" | awk '{print $1}' | xxd -r -p > $KEY_FILE    # затираем и удаляем источник - в памяти ничего не остается   fsize=$(stat -c "%s" "$IMG_FILE")   dd if=/dev/urandom of="$IMG_FILE" bs=$fsize count=1 status=none   rm -f "$IMG_FILE"    chmod 600 "$KEY_FILE" }  # очистка ключа - затираем и удаляем cleanup_key() {   if [[ -f "$KEY_FILE" ]]; then     dd if=/dev/urandom of="$KEY_FILE" bs=64 count=1 status=none     rm -f "$KEY_FILE"   fi    sudo umount "$TMPFS_DIR"   rmdir "$TMPFS_DIR" }  # процедура форматирования/создания диска format_file(){   setvars;   getconfig;    sudo mkdir -p $DATA_DIR    # если диска нет - создаем   if [ ! -f "$DISK_IMAGE" ] ; then     echo -n "Enter disk size (MB): "     read size; size=$(( $size * 1 ));     if [ $size -gt 0 ] ; then       sudo dd if=/dev/urandom of="$DISK_IMAGE" bs=1M count=$size status=progress     else       echo "Size must be greater than 0!"     fi   fi    # если есть и не используется - меняем ключ   if [ -f "$DISK_IMAGE" ] ; then     if is_mapped; then       echo "Encripted disk already opened"     else       fetch_key       echo "Open encryped disk..."       sudo cryptsetup luksFormat "$DISK_IMAGE" "$KEY_FILE"       sudo cryptsetup luksOpen "$DISK_IMAGE" "$CRYPT_NAME" --key-file "$KEY_FILE"       sudo mkfs.ext4 "/dev/mapper/$CRYPT_NAME"       sudo cryptsetup luksClose "$CRYPT_NAME"       cleanup_key     fi   fi }  # процедура монтирования mount_disk() {   setvars;   getconfig;    if [ -f "$DISK_IMAGE" ] ; then     if is_mapped; then       echo "Encripted disk already opened"     else       fetch_key       echo "Open encryped disk..."       sudo cryptsetup luksOpen "$DISK_IMAGE" "$CRYPT_NAME" --key-file "$KEY_FILE"       cleanup_key     fi      if is_mapped; then       if is_mounted; then         echo "Already mounted $MOUNT_POINT."       else         echo "Mount..."         mkdir -p "$MOUNT_POINT"         if [ $? -eq 0 ] ; then           sudo mount /dev/mapper/"$CRYPT_NAME" "$MOUNT_POINT"           sudo chown $USER "$MOUNT_POINT"           sudo chmod 700 "$MOUNT_POINT"         fi       fi     else      echo "Incorrect key"     fi   fi }  # процедура отключения диска unmount_disk() {   setvars;    if [ ! $UMOUNT_FORCE ] ; then     # проверка на наличие рабочих сессий - для автоотключения     USER_SESSIONS=$(who | grep -w "$USER" | wc -l)     USER_PROCESSES=$(ps -u "$USER" --no-headers | wc -l)   fi    if [ "$USER_SESSIONS" -gt 1 ] || [ "$USER_PROCESSES" -gt 0 ]; then     echo "User $USER logged in ($USER_SESSIONS sessions, $USER_PROCESSES processes)"   else     echo "Last session $USER ended"      if is_mounted; then       echo "Unmount..."       sudo umount "$MOUNT_POINT"       rmdir "$MOUNT_POINT"     else       echo "Already unmounted."     fi      if is_mapped; then       echo "Close LUKS-volume..."       sudo cryptsetup luksClose "$CRYPT_NAME"     else       echo "LUKS-volume closed."     fi    fi   fi }  usage(){   cat <<EOF Configurations:  /etc/paranoid/users/<username>.conf: ============================= data_dir = /var/spool/paranoid disk_image = data.dump secret = http://url/secret_file.ext =============================  Interactive mode:  $0 format    Create new or format existing cryptodisk with SECRET  $0 format <username>   Create new or format existing cryptodisk with SECRET for user <username>  $0 mount   Mount cryptodisk (if exists)  $0 unmount   Unmount cryptodisk (if mounted)   PAM mode:  /etc/pam.d/common-session: session optional  pam_exec.so $0  If used fscrypt - do it twice, before and after pam_fscrypt.so EOF }  #============================= checks; uid=$(id -u)  case "$1" in   mount)     mount_disk     ;;   unmount)     UMOUNT_FORCE=1     unmount_disk     ;;   format)     if [ "x$2" != "x" ] ; then       if [ $uid -eq 0 ] ; then         USER="$2"         echo "Run for user $USER"       else         echo "User change ignored"         exit 0       fi     fi     format_file;     ;;   *)     case "$PAM_TYPE" in       open_session)         USER=$PAM_USER         mount_disk         ;;       close_session)         USER=$PAM_USER         unmount_disk         ;;       *)         usage         ;;     esac     ;; esac 

Теперь нужно создать конфиг для нашего юзера — вон как там написано:

/etc/paranoid/users/vasya.conf:

data_dir = /var/spool/paranoid
disk_image = data.dump
secret = http://image172.jpg

Если работаем под vasya:

paranoid format

Если под рутом:

paranoid format vasya

Указываем размер диска в мегабайтах, и если всё на своих местах — файл будет создан, зашифрован и отформатирован.
Подключаем — отключаем:

paranoid mount
paranoid unmount

Для автоматизации внесем записи в /etc/pam.d/common-session:

session optional pam_exec.so /usr/local/bin/paranoid

Если использовалось fscrypt для шифрования домашнего каталога — то дважды:

session optional pam_exec.so /usr/local/bin/paranoid
session optional pam_fscrypt.so
session optional pam_exec.so /usr/local/bin/paranoid

Смысл в том, что скрипты вызываются и при входе, и при выходе — но при входе он сработает только после fscrypt, а при выходе его надо вызывать до fscrypt.
Повторный запуск скрипту не мешает.

Теперь при логине в систему в домашнем каталоге юзера появляется примонтированный секретный диск, который исчезает при выходе из системы.
Либо можно принудительно его закрыть командой.
Если по любой причине ключевой файл недоступен или изменился — диск останется зашифрованным.
Если при этом запустить paranoid format — то еще и перешифрованным под новый ключ, с потерей всего что там было ранее записано.

Более того, файл диска не обязан быть именно файлом на диске — это может быть вообще подключаемая флешка или ISCSI-диск (data_dir = «/dev», disk_image = «sdb1» или подобное).
Если он в наличии — будет молча примонтирован, если нет — то нет.


ссылка на оригинал статьи https://habr.com/ru/articles/890002/