Облако.Mail.Ru + EncFS без локального хранения файлов

в 7:52, , рубрики: c++, encfs, fuse, linux, open source, криптография, Облако Mail.ru, Разработка под Linux

image

Синопсис

Не секрет, что у многих из нас остался 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. Работа ведётся и фронт улучшений широкий, пулл-реквесты и предложения, само собой, приветствуются.

Пример шифрованного каталога:

image

Локально дешифрованный:

image

На облаке:

image

Пара заметок на полях

Не смог найти никакого описания 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js