Синопсис
Не секрет, что у многих из нас остался 1 Тб свободного места на MRU-Облаке со времён его бета-теста. Объём приличный по нынешним меркам — но что с ним делать? Фотографии и своё видео туда просто так заливать не очень хочется — взломы аккаунтов случаются нередко, да и в любом случае — облако это облако, и нельзя сбрасывать со счетов тот простой факт, что любое облачное хранилище принадлежит коммерческой компании, в интересах которой использовать его для собственной выгоды. А значит, нужен дополнительный слой защиты, например, EncFS. Полистаем хабр и гитхаб, вроде бы имеется решение о шифровании данных в своём облаке. Но есть неочевидное, но весьма важное неудобство, о котором в исходной статье не упоминается — для того, чтобы синхронизироваться, нужно локально хранить 600Гб шифрованных фотографий. Для скромных локальных хранилищ, в которых и нешифрованные 600Гб фотографий едва умещаются, это слишком много.
Идея
Поэтому был придуман честолюбивый план — написать FUSE-файловую систему для Облака, а уже поверх неё смонтировать EncFS. В некотором роде это будет похоже на то, как пользуясь облаком MEGA мы перетаскиваем файл в окошко браузера — применяется Е2Е-шифрование и файл отправляется в облако, ничего не занимая локально. Назвать было решено "MARC-FS" — MAail.Ru Cloud FileSystem. С её помощью мы уходим от указанной проблемы локального хранения зашифрованных данных — копируя данные в каталог EncFS мы будем точно так же автоматически шифровать их и отправлять в облако. На самом деле уровнем пониже мы будем вызывать довольно длинную цепочку преобразований вида cp -> kernel FUSE module -> userspace libfuse hook -> EncFS -> FUSE kernel -> libfuse hook ->MARC-FS -> kernel network stack, но упрощённая схема работы будет выглядеть примерно так:
+---------------+
| |
| Local data |
| |
| /media/DATA/ |
| |
+---------------+
|
|
|
|
+-------v-------+
| |
| EncFS |
| |
| ~/remote-enc |
| |
+---------------+
|
| FUSE
| encryption
|
+-------v-------+
| |
| MARC-FS |
| |
| ~/remote |
| |
+---------------+
|
| FUSE
| networking
|
+-------v-------+
| |
| |
| cloud.mail.ru |
| |
| |
+---------------+
Реализация
План амбициозный, но на деле пришлось столкнуться со многими ограничениями как со стороны FUSE и ядра, так и со стороны Облака. Самое, наверное, тревожащее — то, что в Облаке применяется дедупликация данных. То есть файлы, имеющие одинаковые хэши и размеры, хранятся в некоем общем пуле, а пользователям после закачки просто добавляется "хардлинк" на них. Понятное дело, ваши зашифрованные файлы будут уникальны, т.к. зашиты вашим ключом, в связи с чем есть опасение как бы Mail.ru не обратило внимание на такие личные облака и не предъявило рекламацию по этому поводу. Будем надеяться, пользователей OS X/Linux, посмевших при этом ещё и бета-тестировать Mail.ru, было не очень много.
Некоторые другие интересные детали:
- Облако не поддерживает закачку с
Transfer-Encoding: chunked
, а в интерфейсе FUSE нет возможности узнать заранее размер файла перед тем, как он будет записан. Приходится сперва загружать файл в память, и только потом, зная размер, отправлять по сети. - EncFS требует random read/write для своей работы, поскольку разбивает файлы на куски, которые позже шифрует. Для сетевых файловых систем (NFS, например) это страх и ужас, потому что в протоколах нет инструкций типа "записать 4096 байт вот по такому смещению от начала файла". Поэтому для корректной работы EncFS приходится держать опять-таки файл в памяти на время работы с ним (Несколько позднее я добавил возможность указать кэш-каталог, в котором будут храниться промежуточные данные, подробнее см. README.md в репозитории — примеч. автора).
- Связь с облаком устанавливается по https, а это, внезапно, добавляет довольно большой оверхед по памяти для инициализации SSL-движка. Причём не совсем ясно, как бороться с этим, и другие сетевые файловые системы с таким также сталкиваются.
- Хоть размер хранилища и терабайт, у Облака имеется ограничение на размер загружаемого одним запросом файла — в 2 Гб, и только платные услуги дают возможность заливать больше. Для библиотеки фотографий или музыки это подходит, но вот свою коллекцию HD-фильмов залить уже не получится, поэтому позже я добавил прозрачный сплит файлов — если заливать или скачивать файлы более 2Гб, ФС разобьёт или склеит их сама.
- В MacOS используется мультиплексирование файловых дескрипторов, в этом её отличие от Linux, поэтому пришлось линковать проект не с системно присутствующей
libfuse
, а сosxfuse
, доустановленной с помощью Homebrew Cask. В ней прописаны соответствующие обходные пути. Ещё, из возможных проблем могу отметить то, что монтирование одной FUSE-файловой системы в другую на MacOS по умолчанию отключено из-за проблем с рекурсиями, но это обходится с помощью опций монтирования вручную.
В итоге (а может, тем не менее) получился жизнеспособный вариант, с помощью которого удалось залить всю коллекцию музыки и фото и аниме в облако в виде шифрованных файлов. Результат работы над MARC-FS представлен здесь, там же можно найти инструкции по настройке связки с EncFS. Работа ведётся и фронт улучшений широкий, пулл-реквесты и предложения, само собой, приветствуются.
Пример шифрованного каталога:
Локально дешифрованный:
На облаке:
Пара заметок на полях
Не смог найти никакого описания API Облака, пришлось выдёргивать из смежных статей и проектов. Но надо отдать должное Mail.Ru — даже при работе в 25 потоков и сотнях запросов getattr/read/write в секунду только изредка проскакивает 500 Internal Server error в ответ. Я не так часто его вижу, но если эту статью читает кто-то из команды Облака и нужны детали — я могу вытащить несколько таких репортов.
libfuse при написании файловой системы, да и вообще сама архитектура FUSE по своему концепту напомнила о userspace client-server drivers, например в GNU/Hurd или в Plan 9 From Bell Labs. Приятно видеть, что интересные идеи, которые были заброшены с потерей актуальности этих систем, получили новую жизнь.
Многопоточность для ФС пришлось придумывать с нуля и на коленке, так что это был интересный вызов. Нет чёткой уверенности, что всё получилось в лучшем виде (и без багов), но это работает — и работает довольно быстро. Разве что есть сомнения по поводу того, как само Облако отнесётся к одновременной заливке и скачиванию файлов.
Следует предупредить, что я не тестировал подробно MARC-FS на Mac OS X. Мы с другом добились стабильной сборки и пару раз покопировали музыку в терминале, но не более. У меня нет Apple-железа под рукой и я не могу эффективно протестировать работу ФС, так что если кто-то сможет поддержать работу ФС на этой платформе, буду рад.
Дисклеймеры
Я публикую код и эту статью в надежде на то, что они могут кому-то пригодиться, но не предоставляю гарантии товарного вида или пригодности для использования. Внутреннее API Облака может измениться в любую секунду, и возможность оказаться с разбитым корытом довольно высока. Используйте на свой страх и риск.
Я знаю, что в лицензионном соглашении Mail.Ru есть строчки про запрет автоматических средств взаимодействия, но никто из legal_dep не смог понять моего вопроса в письмах и объективно прояснить ситуацию. В конце-концов, мы все используем почтовые клиенты, которые точно так же программно взаимодействуют с Mail.ru. Меня два раза редиректили на Д. А., который честно отвечал, что юридические консультации — это не его стезя.
Ссылки
P.S. Прежде чем меня предупредят о возможных последствиях написания файловых систем — я уже в курсе.
Автор: Kanedias