- PVSM.RU - https://www.pvsm.ru -
Многие системные администраторы не раз «изобретали велосипед», для выполнения некоторых, казалось бы, вполне тривиальных задач. Подробную историю, навеянную мне логами приехавшего на профилактику сервера и восстановленную по служебной документации, об изобретении одного из таких велосипедов, я и хочу поведать. Осторожно много букв и(з) консоли!
Примерно 2 года назад мне для работы был предоставлен сервер в стоечном исполнении, содержащий операционную систему GNU/linux (читай достаточно древнюю ubuntu), и 2 USB флеш-накопителя производства фирмы Transcend обьёмом 16 гигабайт (уж не знаю, зачем такой объём) для изготовления ключевых носителей.
Была поставлена задача: зашифровать разделы с базами данных находящиеся на сервере для предотвращения несанкционированного доступа.
Для реализации задачи была выбрана технология шифрования разделов LUKS [1].
Также понадобились следующие инструменты:
Первоначально планировалось воспользоваться встроенным в дистрибутив алгоритмом монтирования и подключения шифрованных файловых систем, однако то ли недо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-скрипт который выполнял всё вышеуказанные функции, а именно
Также в процессе написания и отладки скрипта удалось реализовать следующие возможности:
В процессе написания скрипта было установлено, что в репозиториях ubuntu содержится устаревшая версия пакета cryptsetup, (не умела генерить раздел с заданным UUID) поэтому актуальная версия программы была собрана из исходных кодов и установлена вместо стандартного пакета ubuntu.
Bash-скрипт был установлен в /etc/init.d/encryptdb. После установки скрипта производились различные отладочные действия, не изменяющие общего состояния системы. После отладки скрипт, при помощи команды update-rc.d encryptdb defaults, был активирован для запуска в автоматическом режиме. Вместе со скриптом были установлены файлы настроек /etc/keycrypt/keycryptab и /etc/keycrypt/keydrv.
Приложения:
#!/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
#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
# 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
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/linux/29534
Ссылки в тексте:
[1] LUKS: http://ru.wikipedia.org/wiki/LUKS
[2] Источник: http://habrahabr.ru/post/172923/
Нажмите здесь для печати.