Содержание статьи
Никто из нас не застрахован от ошибок. Иногда синдром кривых рук приводит к весьма печальным последствиям. Иногда очень сложно удержаться и не провести «антинаучные» эксперименты с системой или запустить скрипт/приложение, скачанное из непроверенного источника. И здесь на помощь приходят различные средства для запуска приложений в изолированной среде и расширенные возможности файловой системы.
Введение
*nix-системы всегда были относительно устойчивы к некорректно написанным приложениям (в том случае, конечно, если они запускались не из-под суперпользователя). Однако иногда возникает желание поэкспериментировать с системой — порезвиться с конфигами, отдельные из которых могут быть жизненно важными, запустить подозрительный скрипт, поставить программу, полученную из недоверенного источника… А то и просто одолевает паранойя, и хочется возвести как можно больше барьеров для защиты от потенциальной малвари. В статье будут описаны некоторые средства, позволяющие избежать последствий невынужденных ошибок с помощью отката на заблаговременно созданную точку возврата (снапшоты Btrfs), запустить подозрительную программу в ограниченном окружении и потешить твою паранойю (Arkose и chroot).
Chroot
Chroot известен давным-давно. У него имеется огромное преимущество перед другими средствами — он работает везде, даже на очень старых дистрибутивах. Все эти новомодные песочницы представляют собой не что иное, как его дальнейшее развитие. Но есть и минусы. Например, нет возможности ограничить работу с сетью, root может при некотором усилии выйти из него, и самое главное — его достаточно сложно настраивать. Несмотря на это, для некоторых целей, к примеру установки пакетов из исходников, он подходит идеально.
Существует как минимум три способа создания chroot-окружения:
- Все необходимые для работы запускаемой программы приложения и библиотеки ты определяешь сам. Это наиболее гибкий способ, но и наиболее замороченный.
- Chroot-окружение формируется динамически. Одно время существовал проект Isolate, который это и делал, но нынче по неизвестным причинам он канул в Лету.
- Развертывание базовой системы в указанном каталоге и чрутинг на него — его я и опишу.
Grub и Btrfs
Скорее всего, при загрузке с раздела Btrfs Grub будет ругаться, что-де разреженные файлы недопустимы, и просить нажать любую клавишу. Чтобы это сообщение не выскакивало, открой в любимом текстовом редакторе файл /etc/grub.d/00.header и закомментируй там следующую строчку:
if [ -n "\${have_grubenv}" ]; then if [ -z "\${boot_once}" ]; then save_env recordfail; fi; fi
Собственно, переменная recordfail необходима, чтобы предотвратить циклическую перезагрузку, для чего она при старте взводится, а затем, в случае успешной загрузки, устанавливается в 0. Хоть комментировать код, отвечающий за эту процедуру, и нежелательно, но думаю, что на настольной системе вполне можно обойтись и без него.
Для начала установим пакет debootstrap, который используется как раз с этой целью.
$ sudo apt-get install debootstrap
Затем создадим каталог, в котором будет находиться chroot, и развернем базовую систему quantal в нем. В общем-то, его можно создать где угодно, но традиционное место его размещения — /var/chroot. Поскольку большинство следующих команд требуют права root, имеет смысл переключиться на аккаунт суперпользователя:
$ sudo su -
# mkdir /var/chroot && cd /var/chroot
# debootstrap quantal ./quantal-chr1 http://mirror.yandex.ru/ubuntu
Разберем последнюю команду. Она разворачивает релиз убунты Quantal в отдельный каталог quantal-chr1 (мало ли, вдруг понадобится еще один chroot) с ближайшего зеркала. После завершения развертывания необходимо отобразить файловые системы procfs, sysfs и (если это необходимо) каталог /dev на данное поддерево. В случае если chroot будет использоваться для текстовых приложений только до перезагрузки, должно хватить следующих команд:
# mount --bind /proc /var/chroot/quantal-chr1/proc
# mount --bind /sys /var/chroot/quantal-chr1/sys
# mount --bind /dev /var/chroot/quantal-chr1/dev
Если же требуется, чтобы данное поддерево работало и после перезагрузки, добавь соответствующие строки в /etc/fstab. Ну а для работы некоторых графических приложений также следует отобразить каталоги /tmp и /var/run/dbus. После этого уже можно вводить следующую команду, которая, собственно, и делает chroot:
# chroot /var/chroot/quantal-chr1/
И ты уже заперт в нем. Для того чтобы не спутать chroot с реальной системой, рекомендую изменить приглашение оболочки. Для примера давай установим и запустим в chroot Skype. Для этого потребуется установить на хостовой системе пакет schroot, который позволяет упростить запуск программ в chroot-окружении:
# apt-get install schroot
Затем добавим запись в файл /etc/schroot/schroot.conf. В моем случае я добавил следующую:
/etc/schroot/schroot.conf
[quantal-skype]
description=Quantal Skype
directory=/var/chroot/quantal-chr1
priority=3
users=rom
groups=rom
root-groups=root,rom
Пробрасываем /dev, /proc, /sys, /tmp и /var/run/dbus — как это сделать, смотри выше. Добавим в chroot пользователя и группу skype — при этом желательно, чтобы uid и gid совпадали с uid/gid основного пользователя реальной системы (в моем случае — rom), для чего набираем следующие команды:
# schroot -c quantal-skype -u root
# addgroup --gid 1000 skype
# adduser --disabled-password --force --uid 1000 --gid 1000 skype
После этого ставим свежескачанный Skype — опять же в chroot — и удовлетворяем его зависимости:
# dpkg --force-all -i skype-ubuntu-precise_4.1.0.20-1_i386.deb
# apt-get -f install
# exit
В основной системе разрешаем соединения с X-сервером с localhost и заходим в chroot как обычный пользователь:
$ xhost +localhost
$ cd / && schroot -c quantal-skype -u rom /bin/bash
Устанавливаем переменную DISPLAY (которую надо посмотреть в основной системе) и запускаем Skype:
$ export DISPLAY=":0.0"
$ skype --dbpath=/home/skype/.Skype &
Скайп успешно установлен и запущен в chroot-окружении.
Можно было бы написать скрипт для облегчения запуска, но это ты можешь сделать и сам.
Использование Arkose
Arkose действует аналогично песочницам в Windows, таким, например, как Sandboxie. Практически это удобная обертка для контейнеров LXC. Но, как известно, удобство и гибкость порой несовместимы — тонкая настройка создаваемых контейнеров затруднительна. Из плюсов отмечу интуитивно понятный интерфейс (это если использовать GUI — впрочем, запуск из командной строки тоже очень прост), из минусов же — по умолчанию требует довольно много свободного места на жестком диске и имеются некоторые возможные пути обхода; но, если использовать Arkose как дополнительную обертку для потенциальных путей внедрения малвари (браузер) или даже просто для экспериментов с каким-нибудь интересным приложением, это никак не повредит.
Seccomp и seccomp-bpf
Seccomp — малоизвестный механизм, внедренный еще в ядро 2.6.12, который позволяет процессу совершить односторонний переход в «безопасное» состояние, где ему будет доступно только четыре системных вызова — exit(), sigreturn(), read() и write(), причем последние два доступны только для уже открытых файлов. Если же процесс попытается вызвать любой другой сисколл, он будет немедленно убит.
Очевидно, что это решение не очень гибкое. В связи с этим в ядре 3.5 появился seccomp-bpf, который позволяет с помощью правил BPF тонко настраивать, какие именно системные вызовы (и их аргументы) разрешены, а какие — нет. Seccomp-bpf применяется в Google Chrome, Chrome OS, а также бэкпортирован в Ubuntu 12.04.
Перед тем как использовать Arkose, его надо установить. Процедура стандартна:
$ sudo apt-get install arkose-gui
Будет установлен как графический интерфейс (arkose-gui), так и утилита командной строки (arkose). Графический интерфейс настолько прост, что описывать его я смысла не вижу, лучше сразу перейдем к практике.
Опции командной строки рассмотрю:
- -n {none,direct,filtered} — отображение сети на песочницу. Опции none и direct не требуют пояснения, filtered создает для каждой песочницы свой интерфейс. На практике же лучше использовать либо none, либо direct, поскольку filtered настраивать достаточно долго.
- -d {none,system,session,both} — доступ к шинам D-Bus из песочницы.
- -s размер — устанавливает размер хранилища в мегабайтах. По умолчанию 2000 Мб для ext4 или половина памяти для tmpfs. После завершения работы запускаемой в песочнице программы хранилище уничтожается.
- -t [ext4,tmpfs] — тип файловой системы хранилища. По дефолту используется ext4.
- --root каталог — указывает каталог, который отображается на песочницу в качестве корня.
- --root-type {cow,bind} — как именно отображать корень. Если использовать cow, то любые изменения после закрытия песочницы пропадут, а если bind — сохранятся.
- --base-path — указывает место хранения песочницы. По умолчанию это ~/.arkose.
- --bind каталог и --cow каталог — отображает каталог либо в режиме cow, либо напрямую. Естественно, использование той или иной опции зависит от типа отображения корня — использовать опцию --cow на каталоге, который и так уже copy-on-write, не имеет смысла.
- -h — использовать реальный домашний каталог. Действует аналогично --bind $HOME.
- -p — разрешает использовать PulseAudio.
Для примера запустим Firefox:
$ sudo arkose -n direct -p firefox
Данная команда запустит Firefox с доступом к сети и к PulseAudio. Поскольку для каждого вновь создаваемого контейнера по умолчанию создается свой домашний каталог, то и профиль огнелиса будет новый, без установленных дополнений, если таковые у тебя имеются.
«Но постой! Зачем же sudo?» — может возникнуть резонный вопрос. Дело в том, что некоторые подготовительные операции доступны только из-под root. Однако спешу тебя успокоить — запускаемая программа будет работать с правами текущего пользователя.
Кратко о BTRFS
Бывает, что после установки обновлений система рушится. Здесь пригодились бы средства, аналогичные компоненту «Восстановление системы» Windows. С гордостью заявляю — их есть у нас! И одно из этих средств — Btrfs. Из достоинств новой файловой системы от Oracle стоит отметить следующие:
- Копирование при записи (Copy-on-Write). Эта технология служит для создания снапшотов — мгновенных снимков состояния системы. При создании снапшота драйвер ФС копирует в него метаданные и начинает мониторить фактическую запись. Если она обнаруживается, в снапшот помещаются оригинальные блоки данных, а на их место записываются новые.
- Динамическое выделение инодfов. В отличие от ФС старого поколения, в Btrfs нет ограничения на количество файлов.
- Сжатие файлов.
- Возможность размещения ФС на нескольких физических носителях. Фактически это тот же самый RAID, только более высокоуровневый. На момент написания статьи поддерживались RAID 0, RAID 1 и RAID 10, поддержка же RAID 5 находилась на ранней стадии разработки.
Создание и удаление снапшотов
Для совершения операций над ФС нового поколения, таких, например, как создание снапшотов, дефрагментация тома и многих других, служит команда btrfs. Синтаксис у нее, в общем случае, следующий:
btrfs <команда> <аргументы>
Какие же именно операции можно производить над Btrfs? Ниже будут приведены команды, которые мне показались интересными.
- btrfs subvol create [<путь>/]<имя> — создает подтом (см. врезку). Если путь не указан, создает его в текущей директории.
- btrfs subvol delete <имя> — соответственно, удаляет подтом.
- btrfs subvol find-new <путь> <поколение> — список последних модифицированных файлов в указанном пути, начиная с указанного поколения. К сожалению, пока что нет возможности простым способом узнать текущее поколение того или иного файла, поэтому применение этой команды может сопровождаться танцами с бубном.
- btrfs subvol snapshot [-r] <подтом> <путь к снапшоту> — гвоздь программы. Создает снапшот указанного подтома с указанным путем к нему же. Опция -r делает невозможной запись в снапшоты.
- btrfs subvol list <путь> — показывает список подтомов и снапшотов по указанному пути.
- btrfs filesys df — использование места для указанной точки монтирования.
- btrfs filesys resize [+/-]<новый размер>[g/k/m] <путь> — да-да, в Btrfs есть возможность изменять размер на «живой» системе, причем не только увеличивать, но и уменьшать! С аргументами, думаю, все более-менее ясно, но, помимо указания размера, можно использовать аргумент max, который расширяет ФС до максимально возможного размера.
Остальные команды хоть и интересны, но к теме статьи относятся лишь постольку-поскольку, и их мы рассматривать не будем. Итак, чтобы создать снапшот подтома c текущей датой, например корневого каталога, набираем следующую команду:
$ sudo btrfs subvol snap -r / /snapshot-2013-01-16
Снапшот создан. Опцию -r я рекомендую использовать для пущей безопасности — теперь файлы из него можно удалить, только удалив снапшот целиком:
$ sudo btrfs subvol del /snapshot-2013-01-16
Подтома Btrfs
Подтом Btrfs может выступать в двух ипостасях: как директория и как объект VFS — то, что может быть примонтировано. Например, при установке Ubuntu создается два подтома — @ и @home. Первый содержит системные файлы, второй — данные пользователя. Это похоже на разбиение диска на разделы, только если раньше один раздел мог содержать, как правило, лишь один объект VFS, то теперь на одном разделе могут быть сразу несколько объектов, при этом они могут быть вложенными.
Автоматизация
Создавать снапшоты ручками я не вижу большого смысла — можно банально забыть это сделать. Напрашиваются три сценария автоматизации:
- написать скрипт и поместить его в rc.local;
- написать скрипт и поместить его в cron;
- использовать команду btrfs autosnap.
К сожалению, в Ubuntu 12.10 последний метод по каким-то причинам недоступен, так что выбора, как такового, практически нет. Лично я предпочел написать скрипт для крона, но сначала давай создадим подтом, в котором и будут храниться наши снапшоты. Для чего? Хотя бы для того, чтобы не засорять корневую папку.
# mkdir /mnt/sda11
# mount /dev/sda11 /mnt/sda11
# btrfs subvol create /mnt/sda11/@snapshots
# umount /mnt/sda11
Рассмотрим, что делают эти команды. Поскольку фактический корень ФС в данный момент недоступен (вместо него в убунте в качестве корня используется подтом @), мы вынуждены подмонтировать его ручками. В моем случае он находится на /dev/sda11. Третьей командой мы создаем подтом @snapshots — таким образом, если мы не подмонтируем его или реальный корень, его содержимое будет недоступно. А теперь собственно скрипт:
autosnap.sh
#!/bin/bash
set -e
VOLUME=/dev/sda11
TMP_PATH=/tmp/snapshots
MOUNT_OPTS="subvol=@snapshots"
# Текущие дата и время — необходимы для формирования имен папок со снапшотами
NOW="$(date +%Y%m%d%H%M)"
NOW_SEC="$(date +%s)"
if [ $# -ne 1 ]; then
# Если скрипт запущен без аргументов, ставим по умолчанию один день назад
OLDER_SEC="$(date --date "1 day ago" +%s)"
else
# Если же у нас указан аргумент, считаем, что это дата в любом формате, который понимает команда date, со всеми вытекающими
OLDER_SEC="$(date --date "$1" +%s)"
fi
# Вычитаем из текущей даты требуемую и преобразуем ее в минуты
OLDER=$(($NOW_SEC-$OLDER_SEC))
OLDER_MIN=$(($OLDER/60))
[ ! -d "${TMP_PATH}/" ] && mkdir "${TMP_PATH}/"
[ -z "`grep "${TMP_PATH}" /proc/mounts`" ] && mount "${VOLUME}" "${TMP_PATH}/" -o "${MOUNT_OPTS}" && { # Монтируем
mkdir "${TMP_PATH}/${NOW}/"
# Создаем снапшоты
btrfs subvol snap / "${TMP_PATH}/${NOW}/rootsnap" > /dev/null 2>&1
btrfs subvol snap /home "${TMP_PATH}/${NOW}/homesnap" > /dev/null 2>&1
} && {
# Ищем папки со снапшотами старше указанной даты
for f in `find "${TMP_PATH}" -mindepth 1 -maxdepth 1 -type d -cmin +"$OLDER_MIN" -print0 |xargs -0`;
do
btrfs subvol del "${f}/rootsnap" > /dev/null 2>&1 &&
btrfs subvol del "${f}/homesnap" > /dev/null 2>&1 &&
# и удаляем снапшоты и папки, их содержащие
rmdir "$f"
done
}
umount -l "${TMP_PATH}" && rmdir "${TMP_PATH}"
Скрипт этот можно разместить, где удобно (лично я предпочитаю подобные вещи размещать в /usr/local/bin, но это дело вкуса), и запускать его либо из крона, либо из rc.local. По умолчанию скрипт ротирует снапшоты старше одного дня, но ты можешь задать любое желаемое количество в формате команды date — главное, не забудь заключить в кавычки.
Использование ISO-образа
Для того чтобы при повреждении каких-либо жизненно важных файлов не дергать каждый раз болванку с записанной убунтой, есть возможность в меню Grub добавить пункт загрузки с ISO-образа, что я и предлагаю сделать. Для этого понадобится не-Btrfs-раздел (поскольку по неизвестным причинам стандартная initramfs убунтовской исошки не желает видеть образ, если он находится на разделе с описываемой ФС) и прямые руки. Добавляем в файл /etc/grub.d/40_custom следующие строчки:
menuentry "Ubuntu 12.10 i386 iso" {
insmod part_msdos
insmod fat
# Устанавливаем корень, откуда берем ISO
set root='hd0,msdos7'
# Путь к образу относительно указанного выше корня
set isofile=/ubuntu-12.10-desktop-i386.iso
# Монтируем в качестве loopback-девайса прямо в Grub
loopback loop $isofile
linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile noeject noprompt --
initrd (loop)/casper/initrd.lz
}
и запускаем команду обновления основного конфига Grub:
$ sudo update-grub
Теперь даже в случае серьезного повреждения системы — если, конечно, не будет затронут загрузчик и его файлы — ты всегда сможешь загрузиться с ISO-образа и изменить поврежденные файлы или откатиться на предыдущее состояние системы.
INFO
Если ты работаешь в chroot-окружении под root, то есть возможность оттуда вырваться. Один из способов — использование системного вызова mknod() с последующим монтированием реального корня. Установка патчсета grsecurity позволяет решить эту проблему.
Команды Btrfs имеют стандартный и сокращенный вид. Например, команду «btrfs subvolume snapshot» можно записать как «btrfs su sn».
Итак, предположим, ты уронил систему и тебе необходимо ее восстановить из снапшота Btrfs. Для этого загрузись с данного ISO-образа, примонтируй тот раздел, систему на котором ты уронил, — именно раздел, не подтом! — и введи следующие команды (естественно, с поправкой на твои снапшоты и разделы):
# cd /mnt/sda11
# mv @ @_badroot
# mv @snapshots/201302011434/rootsnap @
То же самое, при необходимости, делаем с @home и перезагружаемся. Если все прошло нормально, то можешь удалить @_badroot:
$ sudo btrfs subvol del @_badroot
WWW
- wiki по Btrfs.
- www.palecrow.com — пример запуска демона в chroot-окружении. Несколько устарело, однако общие принципы остались те же.
Заключение
В *nix-системах есть множество способов обезопасить себя от неудачных экспериментов или смягчить их последствия. Я рассмотрел некоторые из них. Однако стоит заметить, что все эти способы предназначены в основном для экспериментаторов, любящих поковыряться в системе. Для отлова малвари они не подходят — их достаточно легко обнаружить, хотя некоторый уровень безопасности они, конечно же, обеспечивают.