Многие системные администраторы не раз «изобретали велосипед», для выполнения некоторых, казалось бы, вполне тривиальных задач. Подробную историю, навеянную мне логами приехавшего на профилактику сервера и восстановленную по служебной документации, об изобретении одного из таких велосипедов, я и хочу поведать. Осторожно много букв и(з) консоли!
Примерно 2 года назад мне для работы был предоставлен сервер в стоечном исполнении, содержащий операционную систему GNU/linux (читай достаточно древнюю ubuntu), и 2 USB флеш-накопителя производства фирмы Transcend обьёмом 16 гигабайт (уж не знаю, зачем такой объём) для изготовления ключевых носителей.
Была поставлена задача: зашифровать разделы с базами данных находящиеся на сервере для предотвращения несанкционированного доступа.
Для реализации задачи была выбрана технология шифрования разделов LUKS.
Также понадобились следующие инструменты:
- Внешний носитель (usb hdd), содержащий загружаемую операционную систему GNU/linux и достаточное количество свободного пространства;
- Компьютер с операционной системой arch-linux для написания и тестирования скриптов;
Первоначально планировалось воспользоваться встроенным в дистрибутив алгоритмом монтирования и подключения шифрованных файловых систем, однако то ли недоcтаток опыта, то ли какие-то другие причины привели меня в тупик. Ну да обо всём по порядку.
В начале, конечно же, я загрузился с внешнего носителя и сделал полный бекап всех данных с сервера, заодно мной был изучен файл /etc/fstab. Так как пароля суперпользователя никто не знал сразу после снятия бэкапа я модифицировал файл /etc/shadow, вписав туда хеш известного мне пароля в строку пользователя root. Получив, таким образом, доступ в установленную систему я взялся за дело:
Подключился к сети:
# dhclient
Установил утилиту cryptsetup, включающую в себя инструменты для работы системы шифрования LUKS:
# apt-get update
# apt-get install cryptsetup
Сгенерировал псевдослучайную последовательность из 256 символов, записанную в файл "/tmp/key":
# dd if=/dev/random of=/tmp/key bs=1 count=256
К слову tmp монтировался в оперативу. Установил тип второго раздела первого тома в crypto_LUKS и создал виртуальный раздел "/dev/mapper/dtb", отображаемый в реальный зашифрованный при помощи алгоритмов LUKS:
# cryptsetup luksFormat /dev/sdb2 /tmp/key
# cryptsetup luksOpen /dev/sdb2 dtb
На виртуальном разделе, отображаемом в зашифрованный, создал новую файловую систему ext4, и смонтировал в директорию /mnt:
# mkfs.ext4 /dev/mapper/dtb
# mount /dev/mapper/dtb /mnt
В целях выполнения переноса данных, при помощи команды:
# /etc/init.d/postgresql stop
остановил демон portagesql, и помощи программы Midnight Commander скопировал все файлы из /var/lib/pgsql в /mnt, после чего каталог /var/lib/pgsql очистил. Шифрованный второй раздел первого тома перемонтировал в директорию/var/lib/pgsql:
# umount /dev/mapper/dtb
# mount /dev/mapper/dtb /var/lib/pgsql
Файлик "/tmp/key", используемый в качестве ключа шифрования раздела, отправил на одну из флешек.
При помощи программы Midnight Commander и утилит коммандной оболочки в файл /etc/cryptsetup была внесена конфигурация зашифрованного раздела. Однако, в ходе тестовой перезагрузки было установлено что из за особенностей загрузки операционной системы, она не может смонтировать ключевой носитель из /etc/fstab до того момента как происходит обработка файла /etc/cryptsetup, в результате зашифрованный раздел оказывается не примонтированным, так как он не может быть смонтирован без файла ключа.
Необходимые инструкции (монтирование ключевого носителя, активация шифрованного раздела, размонтирование ключевого раздела и монтирование зашифрованного раздела) были внесены при помощи соответствующих команд в файл /etc/rc.local
Это действие дало положительные результаты (после окончания загрузки раздел был примонтирован в нужное место) однако файл rc.local запускался намного позже демона postgresql что могло привести к непредсказуемым последствиям так как файлы базы данных монтировались прямо во время работы демона.
Проанализировав сложившуюся ситуацию, а так же приняв во внимание необходимость зашифровать раздел с резервными копиями и раздел подкачки, для предотвращения несанкционированного доступа к информации из базы данных и ключам, которые могли оказаться в пространстве подкачки, было принято решение создать специальный стартовый скрипт. Который бы монтировал ключевое устройство и шифрованные разделы на раннем этапе загрузки, до момента запуска демона portagesql и появления необходимости в подкачке.
В результате был создан специальный стартовый bash-скрипт который выполнял всё вышеуказанные функции, а именно
- Производил поиск и монтирование ключевого устройства
- В случае обнаружения и успешного монтирования ключевого устройства подключал и монтировал зашифрованные разделы
- Размонтировал ключевое устройство для безопасного извлечения
- Независимо от результата предыдущих операций обеспечивал бы монтирование раздела подкачки на зашифрованный раздел
Также в процессе написания и отладки скрипта удалось реализовать следующие возможности:
- Возмоность централизованного управления всеми зашифрованными по алгоритму luks томами при помощи специального файла с простым синтаксисом схожим с синтаксисом файла fstab
- Теоретическая возможность использовать неограниченное количество ключей для неограниченного количества зашифрованных разделов (на самом сушествуют определённые ограничения связанные с исчерпанием системных ресурсов)
- Возможность создавать различные независимые ключевые носители с различным набором ключевых файлов
- Возможность монтировать и размонтировать различные наборы зашифрованных дисков
В процессе написания скрипта было установлено, что в репозиториях ubuntu содержится устаревшая версия пакета cryptsetup, (не умела генерить раздел с заданным UUID) поэтому актуальная версия программы была собрана из исходных кодов и установлена вместо стандартного пакета ubuntu.
Bash-скрипт был установлен в /etc/init.d/encryptdb. После установки скрипта производились различные отладочные действия, не изменяющие общего состояния системы. После отладки скрипт, при помощи команды update-rc.d encryptdb defaults, был активирован для запуска в автоматическом режиме. Вместе со скриптом были установлены файлы настроек /etc/keycrypt/keycryptab и /etc/keycrypt/keydrv.
Приложения:
-
Скрипт encryptdb с комментариями на русском языке
#!/bin/bash ### BEGIN INIT INFO # Provides: cryptdisks # Required-Start: checkroot cryptdisks-early # Required-Stop: umountroot cryptdisks-early # Should-Start: udev mdadm-raid lvm2 # Should-Stop: udev mdadm-raid lvm2 # X-Start-Before: checkfs # X-Interactive: true # Default-Start: 2 3 4 5 # Default-Stop: 0 6 # Short-Description: Setup remaining encrypted block devices. # Description: ### END INIT INFO # Этот скрипт находит сьёмные носители, содержащие файл ключа, # подкключает и монтирует зашифрованные файловые системы и раздел подкачки KEYCRYPTAB_DIR=/etc/keycrypt # Рабочая директория KEYCRYPTAB_FILE=$KEYCRYPTAB_DIR/keycryptab # Этот файл содержит параметры монтирования всех разделов # которые необходимо монтировать при помощи ключей # В этом файле обязательно должен быть финальный перевод строки или комментарий в конце # Синтаксис: # <uuid раздела> <имя файла ключа> <имя радела для links> <точка монтирования> # Для раздела подкачки в качестве точки монтирования нужно указать "swap". # Имя файла ключа для раздела подкачки может содержать всего 2 значиния: "none" и "random" # Use the option "none" is not recommended. # Examples: # 11111111-2222-3333-4444-555555555555 random swap1 swap # #safety swap partition. It no need any keyfile, but must be encrypted # 12345678-1234-4321-1234-567890123456 harry.key harry /home/harry # #home directory for Harry, Harry have the "harry.key" in his flash drive # 66666666-9999-8888-7777-000000000000 ntldr public "/var/ftp" # #publuc directory, all staff have the "ntldr" file in flash drives KEYDRIVER_FILE=$KEYCRYPTAB_DIR/keydrv # Этот файл содержит параметры ключевых носителей # В этом файле обязательно должен быть финальный перевод строки или комментарий в конце # Синтаксис: # <uuid> <timeout> <dotmount> # Examples: # kkkkkkkk-kkkk-kkkk-kkkk-kkkkkkkkkkkk 20 /media/keys SWAPCLEAN_FILE=$KEYCRYPTAB_DIR/swapclean.flg # NOTE!!! If u are not using the encription swap # u need to run "dd if=/dev/urandom of=/u/swap/partition". # Otherwise encryption will not make sense. # I was include this functional on the skript, but # it will take a very long time to load OS. #=====================Begin of script===================== # Do not edit next if you are not sure what you are doing! #DBG="on" # Разкоментировать строку выше для вывода отладки UMOUNT_FLAG="" # менять на свой страх и риск # Функция вывода отладки, печатает переданное сообщения желтым цветом, 6 параметров (НЕ СЛОВ! слов может быть хоть миллион) DEBUG() { if [ "$DBG" = "on" ] then echo -e "E[33;40m$1 $2 $3 $4 $5 $6"; tput sgr0 fi } # Функция парсит строчку с параметрами разделов и выставляет значения переменных SetStruct() { str=`echo $1 | sed 's/#.*/ /g'` n=0 for arg in $str do let "n+=1" case "$n" in 1) CRYPT_UUID=$arg ;; 2) KEY_FILENAME=$arg ;; 3) CRYPT_NAME=$arg ;; 4) CRYPT_MOUNTPOINT=$arg return 0 ;; *) DEBUG "too many arguments: $arg" ;; esac done return 1 } # Функция парсит строчку с параметрами ключевого носителя и выставляет значения переменных SetKey() { nk=0 str=`echo $1 | sed 's/#.*/ /g'` for arg in $str do let "nk+=1" case "$nk" in 1) KEY_UUID=$arg ;; 2) KEY_TIMEOUT=$arg if (( KEY_TIMEOUT < 0 )) then echo "Invalid key timeout for $KEY_UUID" KEY_TIMEOUT=1 fi if (( KEY_TIMEOUT > 60 )) then echo "Invalid key timeout for $KEY_UUID" KEY_TIMEOUT=60 fi DEBUG "KEY_TIMEOUT=$KEY_TIMEOUT" ;; 3) KEY_MOUNTPOINT=$arg return 0 ;; *) DEBUG "too many arguments: $arg" ;; esac done return 1 } # функция монтирует ключевой носитель PrepareKeyDrv() { if [ ! -e "/dev/disk/by-uuid/$KEY_UUID" ] then #если ключевой носитель не найден echo -en "Waiting $KEY_TIMEOUT seconds for the key device r" #ожидаем пока загрузится модуль for (( n = ++KEY_TIMEOUT; n ; n-- )) do if [ ! -e "/dev/disk/by-uuid/$KEY_UUID" ] then (( KEY_TIMEOUT-- )) echo -en "Waiting $KEY_TIMEOUT seconds for the key device r" else echo echo "Key device was found!" DEBUG $KEY_UUID break fi if [ "$n" = "1" ] then echo echo "Not found a key device!" DEBUG "Now completing PrepareKeyDrv()" #не нашли, выходим return 1 fi sleep 1 # it is not a debug! Do not comment it! done fi #нашли, монтируем, определяя фс по длинне uuid DEBUG "Mounting the key device" if [ ! -d "$KEY_MOUNTPOINT" ]; then RM_KMP=1 mkdir $KEY_MOUNTPOINT fi uuid_l=`echo $KEY_UUID | wc -m` case "$uuid_l" in 10) mount /dev/disk/by-uuid/$KEY_UUID $KEY_MOUNTPOINT -t vfat -o ro DEBUG "fat detected" ;; 17) mount /dev/disk/by-uuid/$KEY_UUID $KEY_MOUNTPOINT -t ntfs-3g -o force,ro DEBUG "ntfs detected" ;; 37) mount /dev/disk/by-uuid/$KEY_UUID $KEY_MOUNTPOINT -o ro DEBUG `mount | grep $KEY_MOUNTPOINT ` ;; *) DEBUG "Can not identify type of the file system on the specified uuid:" DEBUG "$KEY_UUID" mount /dev/disk/by-uuid/$KEY_UUID $KEY_MOUNTPOINT -o ro ;; esac } # Функция размонтирует ключевые носители для извлечения UmountKey() { DEBUG "Unmounting the key device" umount $1 /dev/disk/by-uuid/$KEY_UUID if [ "$RM_KMP" = "1" ]; then rmdir $KEY_MOUNTPOINT fi } # Функция подготавливает разделы для подкачки PrepareSwap() { if [ "$KEY_FILENAME" = "random" ] then # Если используем шифрованные разделы подкачки if [ ! -e "/dev/disk/by-uuid/$CRYPT_UUID" ] then # Если раздела нет # ругаемся echo "Not found any partition for swap with UUID:" echo "$CRYPT_UUID" ### Если тебе в голову придёт изменить логику проверок ДОПИШИ СЮДА ВЫХОД ИЗ ФУНКЦИИ else # Раздел существует echo "Prepare to encripting swap" DEBUG "Disable all swaps" # Отрубаем всю подкачку (кстати, откуда она у нас ?) swapoff -a DEBUG "Regenerating new temporary key" # Создаём папку для временного ключа mkdir /tmp/key ### НЕ ВЗДУМАЙ МЕНЯТЬ ПУТЬ особенно если ты не позаботился о том чтобы ключ не записался на винт # Монтируем в эту папку кусочек оперативы (свап же мы отключили? Ключик на винте не окажется?) mount -t ramfs none /tmp/key -o maxsize=1 # генерим ключик dd if=/dev/urandom of=/tmp/key/swapkey$CRYPT_UUID bs=1 count=256 &> /dev/null DEBUG "Configuring encrypt on the swap partition" # создаём на партиции шифрованный раздел echo "YES"|cryptsetup luksFormat /dev/disk/by-uuid/$CRYPT_UUID /tmp/key/swapkey$CRYPT_UUID --uuid=$CRYPT_UUID # подключаем его DEBUG "Openinig swap partition for operations" cryptsetup luksOpen /dev/disk/by-uuid/$CRYPT_UUID $CRYPT_NAME --key-file /tmp/key/swapkey$CRYPT_UUID # ключик уничтожаем DEBUG "Erasing temporary key" rm -r -f /tmp/key/swapkey$CRYPT_UUID # Размонтируем рамдиск umount none # генерируеим новый UUID для свопа (не путать с uuid luks который имеет физический раздел) DEBUG "Regenerating swapfs uuid" FS_CRYPT_UUID=`uuidgen` #echo $FS_CRYPT_UUID > $KEYCRYPTAB_DIR/swaps/$CRYPT_UUID DEBUG "Creating swap format" if [ -e "$SWAPCLEAN_FILE" ] then # Если в прошлый раз кто-то замонтировал раздел не шифруя забиваем его мусором чтоб стереть остатки инфы ### неплохо бы однако проверять это не по наличию а по отсутсвию файла, это более секьюрно, и при установке скрипт сам позаботится о том чтобы всё сделать правильно. echo "Swap partition was mounted unsafe, cleaning..." echo -e 'It may take a long time. e[31;40mDo not halt the computer! e[0m' S=`fdisk -s /dev/mapper/$CRYPT_NAME` let "S *= 1024" dd if=/dev/urandom | pv -s $S | dd of=/dev/mapper/$CRYPT_NAME 2> /dev/null rm -r -f $SWAPCLEAN_FILE fi # создаём swapfs mkswap -f -U $FS_CRYPT_UUID /dev/mapper/$CRYPT_NAME &> /dev/null # врубаем подкачку DEBUG "Activating swap" swapon -U $FS_CRYPT_UUID fi else # Нешифрованный свап, обычное монтирование раздела со свапом токлько много мата и специальный файлик-флаг if [ ! -e "/dev/disk/by-uuid/$CRYPT_UUID" ] then echo "Not found any partition for swap with UUID:" echo "$CRYPT_UUID" else chkswapt=`cat /proc/swaps | grep "$CRYPT_NAME"` if [ ! "$chkswapt" = "" ] then echo "Oops! e[31;40mYou already have the swap! e[0m" cat /proc/swaps echo "Сheck your 'fstab', 'cryptab' and '$KEYCRYPTAB_FILE'" echo "files for duplicate entries for swap partition" echo "In future use only one of these files to manage swaps" return 0 else echo -e 'e[31;40mWARNING!!! e[0mThe system uses the unface way to manage swap!' echo 'You need to use value "random" of <key filename> on the' echo "$KEYCRYPTAB_FILE file for all swap partition!" fi swapoff -a mkswap -f -U $CRYPT_UUID /dev/disk/by-uuid/$CRYPT_UUID &> /dev/null swapon -U $CRYPT_UUID && echo "Remoove this file for disable cleaning swap on boot time" > $SWAPCLEAN_FILE # echo $CRYPT_UUID > $KEYCRYPTAB_DIR/swaps/$CRYPT_UUID fi fi } # Функция подключает и монтирует шифрованные разделы PrepareVolumes() { cat $KEYCRYPTAB_FILE | while read line; do #Читаем по строке из файла с параметрами монтирования if SetStruct "$line" then # Если строчка успешно распарсилась if [ ! -e "/dev/disk/by-uuid/$CRYPT_UUID" ] then # Если такого раздела нет # Ругаемся, переходим к следующему echo "Not found encrypted partition with UUID:" echo "$CRYPT_UUID" else # Если раздел существует if [[ "$CRYPT_MOUNTPOINT" = "swap" && "$1" = "swaps" ]] then # Если раздел для подкачки и процедура запущена с параметром "swaps" # Запускаем для монтирования свап раздела специальную процедуру PrepareSwap else # Если это обычный раздел if [[ "$CRYPT_MOUNTPOINT" != "swap" && "$1" != "swaps" ]] ### Уберёшь первое условие, сломаю руку! Ибо сюда может попасть свап раздел и в лучшем случае получишь срач на вывод ошибок от моунта, в худшекм останешься без свопа или что ещё хуже с нешифрованным свопом. then # И если процедура не запущена с параметром "swaps" # Подключаем шифрованный раздел echo "Opening encrypted volume" cryptsetup luksOpen /dev/disk/by-uuid/$CRYPT_UUID $CRYPT_NAME --key-file $KEY_MOUNTPOINT/$KEY_FILENAME if [ -e "/dev/mapper/$CRYPT_NAME" ] then # Если подключился # Создаём точку монтирования и монтируем echo "Mounting encrypted volume" if [ ! -d "$CRYPT_MOUNTPOINT" ]; then mkdir $CRYPT_MOUNTPOINT fi mount /dev/mapper/$CRYPT_NAME $CRYPT_MOUNTPOINT fi fi fi fi fi done } case "$1" in start) # Эта часть выполняется при запуске скрипта с параметром start # Например при запуске компьютера # if [ -d "$KEYCRYPTAB_DIR/swaps/" ] # then # rm -r -f $KEYCRYPTAB_DIR/swaps/*.* # else # mkdir --parents $KEYCRYPTAB_DIR/swaps/ # fi cat $KEYDRIVER_FILE | while read line; do #Читаем по строке из файла ключей if SetKey "$line" then # Если строчка успешно распарсилась if PrepareKeyDrv then # Если ключевой носитель удалось смонтировать # Монтируем шифрованные разделы PrepareVolumes # Размонтируем ключевой носитель UmountKey fi fi done #Монтируем разделы подкачки PrepareVolumes swaps DEBUG "Now comleting" ;; stop) # Эта часть выполняется при запуске скрипта с параметром stор # Например при отгрузке OS cat $KEYCRYPTAB_FILE | while read line; do #Читаем по строке из файла с параметрами монтирования if SetStruct "$line" then # Если строчка успешно распарсилась if [ "$CRYPT_MOUNTPOINT" = "swap" ] then # Если в строке описан свап файл DEBUG "Deactivating swap /dev/mapper/$CRYPT_NAME" # Отключаем подкачку на описанном разделе swapoff /dev/mapper/$CRYPT_NAME # swapoff -UUID=`cat $KEYCRYPTAB_DIR/swaps/$CRYPT_UUID` else # Иначе (в строке описан обычный раздел) # Узнаём смонтирован он или нет chkmnt=`mount | grep "$CRYPT_NAME"` if [ "$chkmnt" != "" ] then # Если смонтирован # размонтируем echo "Unmounting encrypted volume" DEBUG "/dev/mapper/$CRYPT_NAME" umount $UMOUNT_FLAG /dev/mapper/$CRYPT_NAME fi fi if [ -e "/dev/mapper/$CRYPT_NAME" ] then # Если описанный раздел подключен echo "Closing encrypted volume" DEBUG "/dev/mapper/$CRYPT_NAME" # Отключаем cryptsetup luksClose $CRYPT_NAME fi fi done ;; restart|reload) do_stop do_start ;; force-reload) UMOUNT_FLAG="-f" do_stop do_start ;; *) echo "Usage: $1 {start|stop|restart|reload|force-reload}" echo "Actions 'stop', 'restart', 'reload' and 'force-reload' will unmount" echo "all encrypted disk partitions, including partition containing swap" echo "To mount the additional partitions without unount already mounted," echo "run $1 script with the parameter 'start' again" exit 1 ;; esac
-
Файл keycryptab
#All swap partition is required for the mount point "swap" #key file name for swap can take only 2 values: #"none" (is strongly not recommended) and "random" example: #11111111-2222-3333-4444-555555555555 random swap1 swap #<uuid> <key filename> <luks name> <dotmount> 0cf1c420-09a0-4338-85b4-df6aed780425 random swap1 swap 4ebecf51-4a5a-4aaf-ba97-3523129e567c keyfile.key dtb /var/lib/pgsql 0feb764f-195e-487d-a0ed-1de525fb3282 bacup.key bkp /media/old #this file MUST contain final newline or final comment
-
Файл keycryptab
# Syntax: # <uuid> <timeout> <dotmount> # Example: # kkkkkkkk-kkkk-kkkk-kkkk-kkkkkkkkkkkk 20 /media/keys # <uuid> <timeout> <dotmount> 609e85b7-5fa8-4434-9210-b8df1d4c0a66 20 /media/keys bc5e202a-1523-46bc-95f4-3c89f10edd27 120 /media/keys #bacup user #this file MUST contain final newline or final comment
На текущий момент решение работает уже на нескольких серверах и было адаптировано под Debian и
#!/bin/bash
#Keycrypt 1.1 SmartTech 2012 модификация ArchLinux
# Этот скрипт находит сьёмные носители, содержащие файл ключа,
# подкключает и монтирует зашифрованные файловые системы и раздел подкачки
#Инклайды функций для корректного отображения состояния на загрузочном экране
. /etc/rc.conf
. /etc/rc.d/functions
KEYCRYPTAB_DIR=/etc/keycrypt
# Рабочая директория
KEYCRYPTAB_FILE=$KEYCRYPTAB_DIR/keycryptab
# Файл параметров разделов
# Этот файл содержит параметры монтирования всех разделов
# которые необходимо монтировать при помощи ключей
# В этом файле обязательно должен быть финальный перевод строки или комментарий в конце
# Синтаксис:
# <uuid раздела> <имя файла ключа> <имя радела для links> <точка монтирования>
# Для раздела подкачки в качестве точки монтирования нужно указать "swap".
# Имя файла ключа для раздела подкачки может содержать всего 2 значиния: "none" и "random"
# Use the option "none" is not recommended.
# Examples:
# 11111111-2222-3333-4444-555555555555 random swap1 swap
# #safety swap partition. It no need any keyfile, but must be encrypted
# 12345678-1234-4321-1234-567890123456 harry.key harry /home/harry
# #home directory for Harry, Harry have the "harry.key" in his flash drive
# 66666666-9999-8888-7777-000000000000 ntldr public "/var/ftp"
# #publuc directory, all staff have the "ntldr" file in flash drives
KEYDRIVER_FILE=$KEYCRYPTAB_DIR/keydrv
# Файл параметров ключевого носителя
# Этот файл содержит параметры ключевых носителей
# В этом файле обязательно должен быть финальный перевод строки или комментарий в конце
# Синтаксис:
# <uuid> <timeout> <dotmount>
# Examples:
# kkkkkkkk-kkkk-kkkk-kkkk-kkkkkkkkkkkk 20 /media/keys
SWAPCLEAN_FILE=$KEYCRYPTAB_DIR/swapclean.flg
# NOTE!!! If u are not using the encription swap
# u need to run "dd if=/dev/urandom of=/u/swap/partition".
# Otherwise encryption will not make sense.
# I was include this functional on ершы sсript, but
# it will take a very long time to load OS.
DBG="on"
# Разкоментировать строку выше для вывода отладки
#=====================Begin of script=====================
# Do not edit next if you are not sure what you are doing!
UMOUNT_FLAG=""
# менять на свой страх и риск
# Функция вывода отладки, печатает переданное сообщения желтым цветом, 6 параметров (НЕ СЛОВ! слов может быть хоть миллион)
DEBUG()
{
if [ "$DBG" = "on" ]
then
echo -e "E[33;40m$1 $2 $3 $4 $5 $6"; tput sgr0
fi
}
# Функция парсит строчку с параметрами разделов и выставляет значения переменных
SetStruct()
{
str=`echo $1 | sed 's/#.*/ /g'`
n=0
for arg in $str
do
let "n+=1"
case "$n" in
1)
CRYPT_UUID=$arg
;;
2)
KEY_FILENAME=$arg
;;
3)
CRYPT_NAME=$arg
;;
4)
CRYPT_MOUNTPOINT=$arg
return 0
;;
*)
DEBUG "too many arguments: $arg"
;;
esac
done
return 1
}
# Функция парсит строчку с параметрами ключевого носителя и выставляет значения переменных
SetKey()
{
nk=0
str=`echo $1 | sed 's/#.*/ /g'`
for arg in $str
do
let "nk+=1"
case "$nk" in
1)
KEY_UUID=$arg
;;
2)
KEY_TIMEOUT=$arg
if (( KEY_TIMEOUT < 0 ))
then
echo "Invalid key timeout for $KEY_UUID"
KEY_TIMEOUT=1
fi
if (( KEY_TIMEOUT > 60 ))
then
echo "Invalid key timeout for $KEY_UUID"
KEY_TIMEOUT=60
fi
DEBUG "KEY_TIMEOUT=$KEY_TIMEOUT"
;;
3)
KEY_MOUNTPOINT=$arg
return 0
;;
*)
DEBUG "too many arguments: $arg"
;;
esac
done
return 1
}
# функция монтирует ключевой носитель
PrepareKeyDrv()
{
if [ ! -e "/dev/disk/by-uuid/$KEY_UUID" ]
then
#если ключевой носитель не найден
echo -en "Waiting $KEY_TIMEOUT seconds for the key device r"
#ожидаем пока загрузится модуль
for (( n = ++KEY_TIMEOUT; n ; n-- ))
do
if [ ! -e "/dev/disk/by-uuid/$KEY_UUID" ]
then
(( KEY_TIMEOUT-- ))
echo -en "Waiting $KEY_TIMEOUT seconds for the key device r"
else
DEBUG "Key device was found!"
DEBUG $KEY_UUID
break
fi
if [ "$n" = "1" ]
then
DEBUG "Not found a key device!"
DEBUG "Now completing PrepareKeyDrv()"
#не нашли, выходим
return 1
fi
sleep 1 # it is not a debug! Do not comment it!
done
fi
#нашли, монтируем, определяя фс по длинне uuid
DEBUG "Mounting the key device"
if [ ! -d "$KEY_MOUNTPOINT" ]; then
RM_KMP=1
mkdir $KEY_MOUNTPOINT
fi
uuid_l=`echo $KEY_UUID | wc -m`
case "$uuid_l" in
10)
mount /dev/disk/by-uuid/$KEY_UUID $KEY_MOUNTPOINT -t vfat -o ro
DEBUG "fat detected"
;;
17)
mount /dev/disk/by-uuid/$KEY_UUID $KEY_MOUNTPOINT -t ntfs-3g -o force,ro
DEBUG "ntfs detected"
;;
37)
mount /dev/disk/by-uuid/$KEY_UUID $KEY_MOUNTPOINT -o ro
DEBUG `mount | grep $KEY_MOUNTPOINT `
;;
*)
DEBUG "Can not identify type of the file system on the specified uuid:"
DEBUG "$KEY_UUID"
mount /dev/disk/by-uuid/$KEY_UUID $KEY_MOUNTPOINT -o ro
;;
esac
}
# Функция размонтирует ключевые носители для извлечения
UmountKey()
{
DEBUG "Unmounting the key device"
umount $1 /dev/disk/by-uuid/$KEY_UUID
if [ "$RM_KMP" = "1" ]; then
rmdir $KEY_MOUNTPOINT
fi
}
# Функция подготавливает разделы для подкачки
PrepareSwap()
{
if [ "$KEY_FILENAME" = "random" ]
then
# Если используем шифрованные разделы подкачки
if [ ! -e "/dev/disk/by-uuid/$CRYPT_UUID" ]
then
# Если раздела нет
# ругаемся
DEBUG "Not found any partition for swap with UUID:"
DEBUG "$CRYPT_UUID"
### Если тебе в голову придёт изменить логику проверок ДОПИШИ СЮДА ВЫХОД ИЗ ФУНКЦИИ
else
# Раздел существует
DEBUG "Prepare to encripting swap"
DEBUG "Disable all swaps"
# Отрубаем всю подкачку (кстати, откуда она у нас ?)
swapoff -a
DEBUG "Regenerating new temporary key"
# Создаём папку для временного ключа
mkdir /tmp/key ### НЕ ВЗДУМАЙ МЕНЯТЬ ПУТЬ особенно если ты не позаботился о том чтобы ключ не записался на винт
# Монтируем в эту папку кусочек оперативы (свап же мы отключили? Ключик на винте не окажется?)
mount -t ramfs none /tmp/key -o maxsize=1
# генерим ключик
dd if=/dev/urandom of=/tmp/key/swapkey$CRYPT_UUID bs=1 count=256 &> /dev/null
DEBUG "Configuring encrypt on the swap partition"
# создаём на партиции шифрованный раздел
echo "YES"|cryptsetup luksFormat /dev/disk/by-uuid/$CRYPT_UUID /tmp/key/swapkey$CRYPT_UUID --uuid=$CRYPT_UUID
# подключаем его
DEBUG "Openinig swap partition for operations"
cryptsetup luksOpen /dev/disk/by-uuid/$CRYPT_UUID $CRYPT_NAME --key-file /tmp/key/swapkey$CRYPT_UUID
# ключик уничтожаем
DEBUG "Erasing temporary key"
rm -r -f /tmp/key/swapkey$CRYPT_UUID
# Размонтируем рамдиск
umount none
# генерируеим новый UUID для свопа (не путать с uuid luks который имеет физический раздел)
DEBUG "Regenerating swapfs uuid"
FS_CRYPT_UUID=`uuidgen`
#echo $FS_CRYPT_UUID > $KEYCRYPTAB_DIR/swaps/$CRYPT_UUID
DEBUG "Creating swap format"
if [ -e "$SWAPCLEAN_FILE" ]
then
# Если в прошлый раз кто-то замонтировал раздел не шифруя забиваем его мусором чтоб стереть остатки инфы
### неплохо бы однако проверять это не по наличию а по отсутсвию файла, это более секьюрно, и при установке скрипт сам позаботится о том чтобы всё сделать правильно.
echo "Swap partition was mounted unsafe, cleaning..."
echo -e 'It may take a long time. e[31;40mDo not halt the computer! e[0m'
S=`fdisk -s /dev/mapper/$CRYPT_NAME`
let "S *= 1024"
dd if=/dev/urandom | pv -s $S | dd of=/dev/mapper/$CRYPT_NAME 2> /dev/null
rm -r -f $SWAPCLEAN_FILE
fi
# создаём swapfs
mkswap -f -U $FS_CRYPT_UUID /dev/mapper/$CRYPT_NAME &> /dev/null
# врубаем подкачку
DEBUG "Activating swap"
swapon -U $FS_CRYPT_UUID
fi
else
# Нешифрованный свап, обычное монтирование раздела со свапом токлько много мата и специальный файлик-флаг
if [ ! -e "/dev/disk/by-uuid/$CRYPT_UUID" ]
then
DEBUG "Not found any partition for swap with UUID:"
DEBUG "$CRYPT_UUID"
else
chkswapt=`cat /proc/swaps | grep "$CRYPT_NAME"`
if [ ! "$chkswapt" = "" ]
then
echo "Oops! e[31;40mYou already have the swap! e[0m"
cat /proc/swaps
echo "Сheck your 'fstab', 'cryptab' and '$KEYCRYPTAB_FILE'"
echo "files for duplicate entries for swap partition"
echo "In future use only one of these files to manage swaps"
return 0
else
echo -e 'e[31;40mWARNING!!! e[0mThe system uses the unface way to manage swap!'
echo 'You need to use value "random" of <key filename> on the'
echo "$KEYCRYPTAB_FILE file for all swap partition!"
fi
swapoff -a
mkswap -f -U $CRYPT_UUID /dev/disk/by-uuid/$CRYPT_UUID &> /dev/null
swapon -U $CRYPT_UUID && echo "Remoove this file for disable cleaning swap on boot time" > $SWAPCLEAN_FILE
# echo $CRYPT_UUID > $KEYCRYPTAB_DIR/swaps/$CRYPT_UUID
fi
fi
}
# Функция подключает и монтирует шифрованные разделы
PrepareVolumes()
{
cat $KEYCRYPTAB_FILE | while read line; do
#Читаем по строке из файла с параметрами монтирования
if SetStruct "$line"
then
# Если строчка успешно распарсилась
if [ ! -e "/dev/disk/by-uuid/$CRYPT_UUID" ]
then
# Если такого раздела нет
# Ругаемся, переходим к следующему
DEBUG "Not found encrypted partition with UUID:"
DEBUG "$CRYPT_UUID"
else
# Если раздел существует
if [[ "$CRYPT_MOUNTPOINT" = "swap" && "$1" = "swaps" ]]
then
# Если раздел для подкачки и процедура запущена с параметром "swaps"
# Запускаем для монтирования свап раздела специальную процедуру
PrepareSwap
else
# Если это обычный раздел
if [[ "$CRYPT_MOUNTPOINT" != "swap" && "$1" != "swaps" ]] ### Уберёшь первое условие, сломаю руку! Ибо сюда может попасть свап раздел и в лучшем случае получишь срач на вывод ошибок от моунта, в худшекм останешься без свопа или что ещё хуже с нешифрованным свопом.
then
# И если процедура не запущена с параметром "swaps"
# Подключаем шифрованный раздел
DEBUG "Opening encrypted volume"
cryptsetup luksOpen /dev/disk/by-uuid/$CRYPT_UUID $CRYPT_NAME --key-file $KEY_MOUNTPOINT/$KEY_FILENAME
if [ -e "/dev/mapper/$CRYPT_NAME" ]
then
# Если подключился
# Создаём точку монтирования и монтируем
DEBUG "Mounting encrypted volume"
if [ ! -d "$CRYPT_MOUNTPOINT" ]; then
mkdir $CRYPT_MOUNTPOINT
fi
mount /dev/mapper/$CRYPT_NAME $CRYPT_MOUNTPOINT
fi
fi
fi
fi
fi
done
}
case "$1" in
start)
# Эта часть выполняется при запуске скрипта с параметром start
# Например при запуске компьютера
stat_busy "Preparing encrypted partitions"
# if [ -d "$KEYCRYPTAB_DIR/swaps/" ]
# then
# rm -r -f $KEYCRYPTAB_DIR/swaps/*.*
# else
# mkdir --parents $KEYCRYPTAB_DIR/swaps/
# fi
cat $KEYDRIVER_FILE | while read line; do
#Читаем по строке из файла ключей
if SetKey "$line"
then
# Если строчка успешно распарсилась
if PrepareKeyDrv
then
# Если ключевой носитель удалось смонтировать
# Монтируем шифрованные разделы
PrepareVolumes
# Размонтируем ключевой носитель
UmountKey
fi
fi
done
#Монтируем разделы подкачки
PrepareVolumes swaps
DEBUG "Now comleting"
if [ $? -gt 0 ]; then
stat_fail
else
stat_done
fi
add_daemon internet
;;
stop)
# Эта часть выполняется при запуске скрипта с параметром stор
# Например при отгрузке OS
cat $KEYCRYPTAB_FILE | while read line; do
#Читаем по строке из файла с параметрами монтирования
if SetStruct "$line"
then
# Если строчка успешно распарсилась
if [ "$CRYPT_MOUNTPOINT" = "swap" ]
then
# Если в строке описан свап файл
DEBUG "Deactivating swap /dev/mapper/$CRYPT_NAME"
# Отключаем подкачку на описанном разделе
swapoff /dev/mapper/$CRYPT_NAME
# swapoff -UUID=`cat $KEYCRYPTAB_DIR/swaps/$CRYPT_UUID`
else
# Иначе (в строке описан обычный раздел)
# Узнаём смонтирован он или нет
chkmnt=`mount | grep "$CRYPT_NAME"`
if [ "$chkmnt" != "" ]
then
# Если смонтирован
# размонтируем
DEBUG "Unmounting encrypted volume"
DEBUG "/dev/mapper/$CRYPT_NAME"
umount $UMOUNT_FLAG /dev/mapper/$CRYPT_NAME
fi
fi
if [ -e "/dev/mapper/$CRYPT_NAME" ]
then
# Если описанный раздел подключен
DEBUG "Closing encrypted volume"
DEBUG "/dev/mapper/$CRYPT_NAME"
# Отключаем
cryptsetup luksClose $CRYPT_NAME
fi
fi
done
rm_daemon internet
if [ $? -gt 0 ]; then
stat_fail
else
stat_done
fi
;;
restart|reload)
do_stop
do_start
;;
force-reload)
UMOUNT_FLAG="-f"
do_stop
do_start
;;
*)
echo "Usage: $1 {start|stop|restart|reload|force-reload}"
echo "Actions 'stop', 'restart', 'reload' and 'force-reload' will unmount"
echo "all encrypted disk partitions, including partition containing swap"
echo "To mount the additional partitions without unount already mounted,"
echo "run $1 script with the parameter 'start' again"
exit 1
;;
esac
(ещё до того как он обзавелся systemd).
Автор: kin63camapa