Daily MaverickWHAT’S COOKING: Roast butternut soup with roasted garlic, spices and orange zestThe Jerusalem PostIsrael 'in the dark' over flydubai terror hijacker investigation as Mossad probes link to IranPunchAt 70, Alake has made Ekiti proud — OyebanjiBollywood HungamaEggoz onboards Boman Irani as brand ambassadorInquirerGroup hits ‘veiled threats’ to journalists in Sara Duterte’s trialZDF heuteAktuelle Pressemitteilungen des ZDFUOLTécnico de jiu-jítsu Bruno Formiga é acusado de assédio sexual e estupro por alunas no RJColliderThis Horror Legend's Biggest Bomb Got Better With Its Unrated VersionObservador DesportoHomem aterra avioneta em Serpa e sai de táxiFootball ItaliaLazio linked with move for ex Man Utd and Crystal Palace winger ZahaABC NewsSeeing threats to religious liberty, Alito calls same-sex marriage a 'decisive' turnCNN بالعربيةبرفقة والديها وسروال جينز.. هل تتعمد عارضة أزياء هندية لفت الأنظار في باريس؟
The Daily Newsstand · Free, Always
Tuesday, October 6, 2026

Как я делаю защищенное файловое хранилище — 2: Ограничения и возможности

Translate

Армянский язык очень своеобразный. Прожив несколько лет в Армении, я могу безошибочно отличить его на слух от любого другого незнакомого языка. Кстати, вы знали, что в фильме «Борат» друг Бората говорит не на казахском, а на армянском?

Сам я, кроме как поздороваться, так ничего и не выучил. Но через несколько недель неожиданно заметил одну любопытную особенность.

Когда вокруг все говорят на незнакомом языке, мозг через какое-то время перестает цепляться за отдельные слова. Речь окружающих превращается в обычный городской шум. Из-за этого внимание полностью переключается на собственные мысли. Возникает странное ощущение, будто идешь по пустому городу. Даже когда вокруг толпа.

Еще появилась привычка всюду носить с собой рюкзак. Когда-то спокойно обходился без него, но теперь в нем почти всегда лежал ноутбук. Достаточно было зайти в любое кафе — и новое рабочее место было готово.

Вот так, перемещаясь время от времени по городу и размышляя по дороге, я и продолжил работать над своим проектом.

2. Чего же мы хотим?

Для начала пришлось посмотреть в лицо самому главному ограничению: у меня был только я и был я не дома. Возможности собрать собственное устройство при помощи паяльника и мотка синей изоленты у меня не было. Впрочем, паяльника и изоленты у меня тоже не было.

Оставался только один вариант: написать для файлового хранилища приложение, которое будет работать на том железе, которое мне попадется. Все необходимое для этого у меня как раз было.

Несмотря на то, что я не мог использовать аппаратные механизмы защиты, меня это не огорчило. Скорее наоборот — сделало задачу интереснее. Нужно было добиться максимальной защиты, используя только программные средства.

Что ж, если приложение должно работать на любом железе, то оно должно как можно меньше зависеть от этого самого железа. А значит все необходимое для работы приложение должно привозить с собой. Получается, я выбираю Docker.

Надо сказать, контейнеризация сильно упрощала мне жизнь. Базовая структура будущего приложения тут же сложилась сама собой:

             .
Docker       .       Docker
container    .       volumes
┌──────┐     .     ┌───────────┐
│      │     .     │ Encrypted │
│      │     .     │ files     │
│ App  │     .     └───────────┘
│ core │     .     ┌───────────┐
│      │     .     │ Encrypted │
│      │     .     │ key       │
└──────┘     .     └───────────┘
             . 

Само приложение должно работать внутри контейнера и не хранить в нем ничего ценного. Так контейнер можно остановить, удалить и создать заново без каких-либо последствий.

А все ценное выносится наружу в два подключаемых тома. В одном должны находиться зашифрованные данные, во втором — зашифрованный ключ и прочие служебные штуки.

Кроме того, контейнеризация позволяла разделить весь проект на независимые сервисы. Ядро приложения, клиентская часть, тесты и прочее — все могло жить в собственных контейнерах, со своими зависимостями:

               .                  .
┌──────────┐   .   ┌──────────┐   .   ┌──────────┐
│ Core     │   .   │ Client   │   .   │ Extra    │
│ app      │   .   │ app      │   .   │ servcies │
└──────────┘   .   └──────────┘   .   └──────────┘
               .                  . 

Ядро приложения должно выставлять наружу только API. Тогда можно подключать к нему любые клиенты. Их можно свободно менять или комбинировать, не трогая основную логику хранилища. Или работать с приложением напрямую через API вообще без клиентов (например, встроив его в какую-нибудь другую систему).

В качестве бонуса API решал еще одну важную задачу: при атаке по локальной сети поверхность атаки сужалась до одного интерфейса. Никаких SMB, NFS или других характерных файловых протоколов.

На этом с инфраструктурой можно было на время закончить. Контейнеры, тома — все это были лишь инструменты. Теперь предстояло ответить на следующий вопрос: каким должно быть файловое хранилище с точки зрения пользователя?

3. Нужные вещи

Идея постепенно превращалась в список вполне конкретных требований:

  1. Простая установка. Приложение должно работать на самом обычном железе — компьютере, ноутбуке, мини-ПК. Оно должно легко устанавливаться и не требовать специфической настройки хостовой операционной системы.

  2. Автономность. Приложение не должно зависеть от внешних сервисов. Никаких облаков, сторонней инфраструктуры или обязательного подключения к интернету. Оно не должно ничего никуда отправлять и ничего ниоткуда получать.

  3. Шифрование. Содержимое хранилища должно быть надёжно зашифровано. Ключ шифрования должен храниться отдельно и при необходимости легко извлекаться. Без него хранилище должно оставаться полностью недоступным.

  4. Простота использования. Пользователь не должен думать о криптографии. Он должен просто работать с файлами. Шифрование должно оставаться внутренней задачей приложения.

  5. Надежность. Несмотря на то, что главная задача приложения — защищать данные, они не должны становиться хрупкими. Пользователь должен иметь возможность создавать резервные копии зашифрованного хранилища, восстанавливать его из этих копий и безболезненно переносить данные между разными версиями приложения.

  6. Многопользовательский режим и разделение прав доступа. Изначально этого требования не было. Но я предположил, что такая возможность будет крайне полезна. Тогда следовало разделить владельца, который открывает хранилище ключом, и пользователей, которые могут работать с уже открытым хранилищем.

  7. Dead Man’s Switch. Я все еще не знал, как именно должен работать этот механизм, но было понятно, что без него вся концепция теряет смысл. В списке требований он занял отдельное место.

Тут я всерьез задумался: а нужно ли вообще кому-то такое приложение? Прежде всего мне самому. Я решил провести простой мысленный эксперимент.

Возьмем к примеру мой личный фотоархив. Не скажу, что там есть что-то сенсационное или компрометирующее. Это обычные фотографии из моей жизни. Но если бы передо мной встал выбор — безвозвратно потерять весь архив или допустить, чтобы он достался бы кому-то постороннему, — я бы, не задумываясь, выбрал первое.

И дело вовсе не в содержимом фотографий. Дело в том, что они только мои.

Можно было двигаться дальше.

4. Так что же это?

Так постепенно складывался образ будущего приложения. Я уже представлял, как оно должно работать. И как оно работать не должно.

Приложение должно одинаково хорошо подходить для хранения любых данных, доступ к которым должен оставаться исключительно под контролем владельца: личные и семейные фотографии, сканы юридических и финансовых документов, резервные копии и архивы, исследовательские данные и прочее. Контекст использования получался довольно широкий.

Благодаря API должна быть возможность использовать приложение как файловое хранилище для других систем в полностью автономной инфраструктуре без зависимости от внешних сервисов.

При этом оставался целый класс задач, для которых такое приложение изначально не предназначалось. Прежде всего это системы, требующие горизонтального масштабирования, распределённого хранения данных или рассчитанные на высокие нагрузки. Эти задачи лежали за пределами задуманной архитектуры и это вполне укладывалось в предполагаемую область применения.

Получался файловый сейф — без внешних зависимостей, без социальных функций, без совместного редактирования документов и без попыток решить все задачи сразу. Просто место, где файлы оставались бы в безопасности.

Можно было переходить к выбору технологий.

5. Язык

Выбор языка программирования был одним из самых важных решений. От него зависели очень многие архитектурные особенности.

Сначала нужно было ответить всего на один вопрос: компилируемый или интерпретируемый? Производительность против простоты. Скорость выполнения против скорости разработки.

Тут я сообразил, что выбираю не совсем по тем критериям потому что не учитываю контекст задачи. Открытость против закрытости. Вот что действительно имело значение.

Как можно говорить о безопасности, если нельзя проверить, что в данный момент делает запущенное приложение?

Я мысленно вычеркнул все, что компилировалось. Выбор сузился вдвое и я стал перебирать оставшееся:

PHP отпал сразу. Пусть он прекрасно чувствует себя в вебе, но идея строить на нем свое приложение казалась мне сомнительной.

JS мне просто не нравился. Заставлять себя писать на языке, который мне не нравится, я не собирался.

Ruby был приятен, но его экосистема была довольно узкой, а долгосрочные перспективы — туманными.

Остался старый добрый Python. Годы развития, огромная экосистема, отличная документация, огромное сообщество. Но он был невероятно скучным: ни революций, ни постоянной смены парадигм, ни гонки за трендами. То, что надо!

Конечно, у такого выбора были и недостатки: повышенное потребление памяти, более низкая производительность. Но приложение должно было надежно хранить файлы, а не бить рекорды в бенчмарках. Так что эти недостатки меня не смущали.

6. Фреймворк

С языком все было понятно. Теперь можно было выбирать фреймворк. Я перебрал практически все, что на тот момент казалось разумным.

Django отпал первым. Он предлагал гораздо больше, чем мне было нужно, и выглядел слишком тяжелым для небольшого автономного приложения. Кроме того, мне не нравилось, что он слишком много делает за меня.

Flask был полной противоположностью. Он был простым, понятным и удобным. Но асинхронность была скорее дополнением, чем естественной частью фреймворка. Мне же хотелось, чтобы она лежала в его основе.

aiohttp был быстрым и асинхронным, но оставался скорее библиотекой, чем полноценным фреймворком. За эту свободу приходилось платить: слишком многое пришлось бы реализовывать самостоятельно.

FastAPI с его автоматической валидацией данных, встроенным OpenAPI, асинхронностью из коробки. Отличная документация, огромное сообщество. Он требовал меньше всего кода для реализации моего приложения. В нем было все, что мне нужно и не было ничего лишнего. На нем я и остановился.

Стек складывался на удивление спокойно. Без ненужных компромиссов между модой и необходимостью: мне нужен был надежный инструмент, а не повод что-то попробовать.

7. Конец второй части

Дальше предстояло решить вопрос, как именно будут храниться пользовательские файлы и данные самого приложения.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.