ТЗ: Secure Distributed File Processing System

Версия: 0.3 (MVP) Компоненты: Rust Client, Go Server, Go Worker, C++ Core, PostgreSQL, Docker/rootless Podman


1. Назначение

Клиент загружает файл, Server создаёт задачу (Job), Worker забирает её, запускает существующий C++ Core (обфускация) в изолированном контейнере и возвращает результат. Клиент скачивает результат.

1.1. Цели

1.2. Не входит в MVP


2. Архитектура

Rust Client --HTTPS (Bearer token)--> Go Server --> PostgreSQL
                                         |--> Private Storage (локальная ФС)
                                         ^
                                         | Internal HTTPS, mTLS (Worker сам опрашивает Server)
                                      Go Worker --> Docker/Podman --> C++ Core

2.1. Ответственность компонентов

Client: аутентифицируется, загружает файл, запрашивает статус, скачивает результат. Прямого доступа к PostgreSQL, Storage и Worker нет.

Server: публичный и внутренний API, аутентификация и авторизация, проверка лимитов, создание и хранение Job, единственный владелец статусов, доступ к файлам, метаданные в PostgreSQL. Docker и C++ Core не запускает.

Worker: сам инициирует соединение с Server (pull), получает назначенную задачу, скачивает вход, создаёт отдельную рабочую директорию, запускает Core в sandbox, проверяет результат, передаёт его Server. Доступа к PostgreSQL нет. Не доверяет пользовательским именам файлов и путям.

C++ Core: только обработка файла. Не знает о пользователях, задачах и HTTP, не получает произвольных команд и параметров.

PostgreSQL: пользователи, токены, Job и метаданные; на MVP также очередь задач.

Storage: исходные файлы и результаты на локальной ФС. Публичного прямого доступа нет, доступ только через Server и авторизованные внутренние операции Worker.

2.2. Принципы взаимодействия


3. Технологический стек

Компонент Технология
Client Rust
Server, Worker Go
Core C++
Database PostgreSQL
API REST over HTTPS
Контейнеризация Docker или rootless Podman
Storage MVP Локальная файловая система

4. Основной сценарий

  1. Client: POST /api/v1/jobs (multipart, потоково).
  2. Server проверяет токен, размер, лимит активных задач пользователя, доступное место.
  3. Server потоково сохраняет файл под случайным внутренним именем, создаёт Job pending, возвращает job_id и статус.
  4. Worker: POST /internal/v1/jobs/claim. Server атомарно назначает задачу, ставит processing, выдаёт lease и fencing_token.
  5. Worker скачивает вход, создаёт рабочую директорию, запускает Core в sandbox, шлёт heartbeat.
  6. Worker проверяет exit code и выходной файл, отправляет результат (PUT .../result) или ошибку (POST .../fail).
  7. Server сохраняет результат, считает SHA-256, фиксирует completed.
  8. Client запрашивает статус; после completed скачивает результат.

5. Жизненный цикл Job

Статусы: pending, processing, completed, failed.

pending -> processing -> completed
              |  ^
              |  +-- retry (attempt < max_attempts) -> pending
              +----> failed

5.1. Правила

5.2. Инварианты

5.3. Lease, retry, fencing

Поля Job: worker_id, lease_expires_at, heartbeat_at, attempt, fencing_token, max_attempts (по умолчанию 3), error_code, error_retryable.


6. API

Публичные endpoints: /api/v1, внутренние: /internal/v1. Единый формат ошибок { "error": { "code": "...", "message": "..." } }, без внутренних путей, stack trace и сведений об инфраструктуре.

6.1. Публичный API (Client)

Метод Endpoint Назначение
POST /api/v1/auth/token Получить токен (логин + пароль)
GET /api/v1/auth/me Данные текущего пользователя
POST /api/v1/jobs Создать задачу и загрузить файл
GET /api/v1/jobs/{id} Статус и сведения о задаче
GET /api/v1/jobs/{id}/result Скачать результат
GET /health Проверка живости

6.2. Внутренний API (Worker)

Метод Endpoint Назначение
POST /internal/v1/jobs/claim Получить следующую задачу (204, если пусто)
GET /internal/v1/jobs/{id}/input Скачать исходный файл
POST /internal/v1/jobs/{id}/heartbeat Продлить lease
PUT /internal/v1/jobs/{id}/result Передать результат
POST /internal/v1/jobs/{id}/fail Сообщить об ошибке (code, retryable)

Запросы Worker содержат fencing_token. Server проверяет worker_id, статус, lease и токен.

6.3. HTTP-требования


7. Требования безопасности

7.1. Аутентификация и авторизация пользователей

7.2. Аутентификация Worker (mTLS)

7.3. Защита от устаревших Worker и повторной отправки

7.4. Защита загрузки файлов

7.5. Изоляция C++ Core

Входной файл недоверенный. Core может содержать уязвимости (buffer/integer overflow, use-after-free, OOB read/write, алгоритмический DoS, возможное выполнение кода). Поэтому Core запускается только в изолированной среде.

Минимальные настройки контейнера:

Запуск фиксированного бинарника через exec.Command; sh -c с пользовательскими данными запрещён. Пользователь не влияет на команду, путь и аргументы.

Доступ к Docker socket на хосте равен root-доступу. Поэтому: Worker на отдельной машине/VM, предпочтительно rootless Podman, либо узкий supervisor с заранее заданными операциями запуска. При повышенных требованиях рассмотреть gVisor или Firecracker (после MVP).

7.6. Контракт запуска Core (зафиксировать до реализации Worker)

7.7. PostgreSQL

7.8. Storage

7.9. Бизнес-логика и гонки

7.10. Что пока не требуется


8. Атомарное завершение

При PUT /result Server:

  1. Проверяет worker_id (из сертификата), статус processing, lease, fencing_token.
  2. Пишет результат во временный файл, проверяет размер, считает SHA-256 потоково.
  3. Делает rename в финальное место.
  4. В одной транзакции с проверкой статуса и токена ставит completed, сохраняет sha256 и размер.

Если шаг 4 не удался, Job остаётся processing, а файл без записи в БД считается осиротевшим. Фоновая очистка удаляет такие файлы.


9. Надёжность, хранение, нефункциональные требования


10. Структура

10.1. Server

server/
├── cmd/server/main.go
├── internal/
│   ├── jobs/
│   │   ├── domain/        (job.go, status.go, errors.go)
│   │   ├── ports.go
│   │   ├── service.go
│   │   └── adapters/
│   │       ├── http/      (client/, worker/)
│   │       └── postgres/
│   ├── auth/
│   ├── users/
│   ├── storage/
│   └── platform/          (config, logging, tls)
├── migrations/
└── go.mod

Правила: Domain не зависит от HTTP, БД, ФС, Docker. Интерфейсы объявляются у потребителя. Client- и Worker-handlers используют один jobs.Service, но дают разные операции. Очередь это механизм назначения, а не отдельный бизнес-модуль. HTTP DTO не в Domain. Пакеты common/utils/helpers не создавать. Каталоги создавать по мере необходимости.

10.2. Worker

worker/
├── cmd/worker/main.go
└── internal/
    ├── jobs/        (клиент внутреннего API, цикл claim/heartbeat)
    ├── executor/    (запуск и контроль обработки)
    ├── container/   (запуск sandbox)
    └── platform/

Worker не содержит логики пользователей и не управляет статусами в БД.


11. Тесты

Domain: допустимые и недопустимые переходы, инварианты, повторное завершение, retry-логика.

Server (безопасность и корректность):

Worker и sandbox:

Integration: upload -> claim -> обработка в sandbox -> result -> completed -> download; а также отказ Worker во время обработки и восстановление после истечения lease.


12. Критерии готовности MVP

  1. Пользователь аутентифицируется и загружает файл в пределах лимита.
  2. Server создаёт Job pending; Worker (по mTLS) забирает её, скачивает вход, обрабатывает Core в sandbox, отправляет результат.
  3. completed ставится только после успешного сохранения результата.
  4. Пользователь видит статус и скачивает результат только своей завершённой задачи; чужие данные недоступны.
  5. Worker не может завершить чужую, не назначенную или переназначенную задачу.
  6. Некорректный вход, сбой Core и таймаут обрабатываются контролируемо.
  7. Зависшая задача обнаруживается и восстанавливается.
  8. Входные файлы не обрабатываются вне sandbox; лимиты sandbox подтверждены негативными тестами.
  9. Токены хранятся только в виде HMAC-хеша, revoke работает; секреты вне репозитория; логи без секретов.

13. Порядок реализации

  1. Контракт Core (7.6), конфиг, TLS/mTLS-инфраструктура (CA, сертификаты Server и Worker).
  2. Domain Job (статусы, переходы, ошибки).
  3. Persistence: миграции, хранилище Job, атомарный claim, lease, fencing; таблицы users и tokens.
  4. Storage: потоковое сохранение и выдача, безопасные пути, лимиты.
  5. HTTP API: публичный и внутренний listener, auth и mTLS middleware, валидация, лимиты body, единый формат ошибок.
  6. Worker: claim, скачивание, workspace, sandbox, heartbeat, отправка результата и ошибки.
  7. Интеграция Core: фиксированный запуск, проверка exit code, выходного файла, таймаута и лимитов.
  8. Security и reliability review: авторизация и владение, IDOR, Path Traversal/symlink, изоляция контейнера, таймауты и лимиты, гонки завершения и переназначения, восстановление зависших задач, логи без секретов, backup/restore БД.

14. Документация

docs/
├── ARCHITECTURE.md   компоненты, границы, взаимодействие
├── API.md            публичный и внутренний API, ошибки
└── THREAT_MODEL.md   активы, границы доверия, угрозы и меры защиты