Files
contracts-flask/docs/upload-buffer-incident-2026-10-30.md
T
RepinoidandCopilot bb5dd6538e
Deploy contracts-flask / validate (push) Canceled after 0s
Fix Contracts upload buffer endpoint
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-30 12:08:55 +03:00

12 KiB
Raw Blame History

Инцидент: ошибки загрузки файлов через VM-буфер — 2026-10-30

Краткий вывод

Контрактор отправлял выбранные файлы на https://contracts.kube5s.ru/contracts-upload/. На момент проверки 2026-10-30 имя contracts.kube5s.ru возвращало NXDOMAIN у публичных DNS-резолверов 1.1.1.1 и 8.8.8.8. Поэтому браузер не мог разрешить адрес назначения и показывал общую ошибку «Сетевая ошибка при отправке файла в буфер». Запрос не доходил до Nginx и WebDAV.

Форматы и содержимое выбранных документов не были причиной этого сбоя. Реальные файлы из пользовательской папки не отправлялись; проверки upload-инфраструктуры ограничились DNS, чтением конфигураций и безопасными HTTP OPTIONS-запросами.

Подтверждённые факты

Настройка Контрактора

  • Развёрнутая страница Контрактора показывала версию 2.0.23.
  • Загруженный браузером site/static/app.js содержит VM_UPLOAD_URL = 'https://contracts.kube5s.ru/contracts-upload/'.
  • site/config.py задаёт тот же домен для VM_UPLOAD_URL и VM_UPLOAD_PREFIX. Backend регистрирует upload_refs с этим VM_UPLOAD_PREFIX через site/routes/__init__.py.
  • deploy/nginx-contracts.conf содержит WebDAV location /contracts-upload/ с каталогом /var/www/contracts-upload/ и CORS origin https://contractor.pythonk8s.dev.nubes.ru. Этот location находится в virtual host contracts.kube5s.ru.

DNS и доступные буферы

  • Запросы к 1.1.1.1 и 8.8.8.8 для contracts.kube5s.ru A вернули NXDOMAIN.
  • После первоначальной диагностики владелец DNS добавил contracts-upload.sinefo.ru A -> 5.172.178.213. Повторная проверка обоих публичных резолверов подтвердила эту запись.
  • При первой проверке до настройки Nginx новый hostname отдавал чужой TLS- сертификат. После установки HTTP challenge vhost сертификат Let's Encrypt выпущен для contracts-upload.sinefo.ru; срок действия до 2027-01-28.
  • Nginx virtual host для нового hostname теперь установлен на VM213, а nginx -t и reload прошли успешно.
  • upload.sinefo.ru разрешался в 5.172.178.213, но https://upload.sinefo.ru/contracts-upload/... отвечал 404.
  • На upload.sinefo.ru/drhider-upload/ WebDAV-маршрут существует, но Access-Control-Allow-Origin возвращается только для DrHider origin https://drhider.pythonk8s.dev.nubes.ru, не для Контрактора.
  • На obfuscator-upload.sinefo.ru/obfuscator-upload/ маршрут существует, но Access-Control-Allow-Origin возвращается только для Obfuscator origin https://obfuscator.pythonk8s.services.ngcloud.ru, не для Контрактора.
  • Запросы OPTIONS для origin Контрактора к обоим существующим маршрутам возвращали 204, но без Access-Control-Allow-Origin. Такой ответ не разрешает браузерный запрос; кроме того, путь /contracts-upload/ на этих хостах не настроен.
  • На VM213 (5.172.178.213) при проверке существовали каталог /var/www/contracts-upload/ и старый Nginx location /contracts-upload/ внутри virtual host contracts.kube5s.ru. Наличие каталога само по себе не делает его доступным по другому hostname.

Почему прямое исправление URL недостаточно

Замена только домена в Контракторе не решит проблему:

  1. /contracts-upload/ на upload.sinefo.ru сейчас не настроен и отвечает 404.
  2. Существующий /drhider-upload/ предназначен для DrHider и не разрешает origin Контрактора в CORS.
  3. Буфер Obfuscator имеет отдельные hostname и путь, отдельную конфигурацию и allowlist origin. Он не является готовым Contracts endpoint.
  4. URL на фронтенде и доверенный префикс backend должны совпадать. Иначе браузер может загрузить объект, но upload_refs отклонит URL при SSRF-проверке либо backend будет пытаться забрать объект с другого адреса.
  5. Backend VM_UPLOAD_PREFIX может быть переопределён переменной окружения и должен совпадать с URL буфера. URL браузерной загрузки задаётся константой VM_UPLOAD_URL в site/static/app.js; переменная окружения с таким именем сама по себе статический JavaScript не меняет.

Отличие от Obfuscator

Актуальный Obfuscator использует согласованную отдельную конфигурацию:

  • hostname obfuscator-upload.sinefo.ru;
  • путь /obfuscator-upload/;
  • отдельный каталог /var/www/obfuscator-upload/;
  • CORS allowlist для https://obfuscator.pythonk8s.services.ngcloud.ru;
  • один и тот же URL-префикс на фронтенде и backend.

Это описано в OBFUSCATOR/README.md и настроено в obfuscator-upload.conf. Контрактор должен получить аналогично согласованный endpoint, а не использовать Obfuscator/DrHider путь без изменения и проверки его конфигурации.

План исправления

Первоначальный план обновлён после предоставления DNS-записи contracts-upload.sinefo.ru. Код и source Nginx изменены локально; VM213 и развёрнутое приложение ещё не обновлялись.

  1. Hostname и DNS — выполнено. Выбран contracts-upload.sinefo.ru; публичные DNS 1.1.1.1 и 8.8.8.8 возвращают 5.172.178.213.
  2. Настроить VM213 — выполнено. Проверен отдельный каталог /var/www/contracts-upload/ и права пользователя Nginx. Для первоначального выпуска сертификата установить deploy/nginx-contracts-upload-acme.conf как /etc/nginx/sites-enabled/contracts-upload.conf, выполнить nginx -t и только при успехе reload. Первая попытка Certbot завершилась временным CAA DNS timeout; повторная попытка выпустила сертификат. После этого временный конфиг заменён полным deploy/nginx-contracts-upload.conf, снова выполнить nginx -t и reload. Полный vhost задаёт отдельный WebDAV путь, PUT/GET/DELETE, OPTIONS, ограничение тела 50 MiB и CORS allowlist через map для точного origin https://contractor.pythonk8s.dev.nubes.ru. Предел 50m соответствует backend-лимиту 50 MiB; не копировать 1024 MiB из Obfuscator, поскольку Contracts не принимает файлы такого размера. Установить deploy/contracts-upload-cleanup.cron как /etc/cron.d/contracts-upload-cleanup, чтобы удалять оставшиеся объекты старше 30 минут. Cron установлен в /etc/cron.d/contracts-upload-cleanup. Не менять маршруты DrHider или Obfuscator и не объединять их каталоги с Contracts.
  3. Код приложения — выполнено локально, deploy ожидает выполнения. Frontend VM_UPLOAD_URL и backend VM_UPLOAD_PREFIX default приведены к https://contracts-upload.sinefo.ru/contracts-upload/; обновлены frontend/backend defaults, пример конфига и default URL нагрузочного теста. Версия приложения поднята до 2.0.24. Проверить переменные окружения развёртывания: VM_UPLOAD_PREFIX не должен переопределять новый endpoint старым значением.
  4. Синхронизировать конфигурацию VM — выполнено. Источник Nginx вынесен в отдельные bootstrap- и TLS-конфиги; старый upload location удалён из deploy/nginx-contracts.conf. Конфигурация TLS-vhost и cron установлена на VM213; Nginx config test/reload успешны. Деплой приложения Контрактора — отдельная операция, ещё не выполнена.
  5. Проверить endpoint до обработки документов — выполнено.
    • DNS разрешается в 5.172.178.213; TLS сертификат валиден.
    • OPTIONS с origin Контрактора отвечает 204 и содержит точный Access-Control-Allow-Origin, разрешённые методы и заголовки. С посторонним origin Access-Control-Allow-Origin не отправляется.
    • Синтетический объект прошёл PUT (201) → GET (байты совпали) → DELETE (204). Объект удалён.
  6. Проверить путь через приложение безопасно. В тестовой сессии/батче загрузить синтетический небольшой файл, убедиться в успешном POST /api/upload_refs и очистить созданную тестовую запись/объект. Не использовать пользовательские документы для проверки, пока синтетический тест не пройден.
  7. Только после успешной проверки проверить обычную загрузку и последующие этапы обработки выбранных документов.

Статус

  • Причина исходной сетевой ошибки подтверждена: используемый hostname contracts.kube5s.ru вернул NXDOMAIN.
  • DNS, TLS, Nginx vhost, CORS и прямой синтетический WebDAV цикл проверены успешно.
  • Причина установлена до этапов HTTP/WebDAV, CORS и обработки документов.
  • URL/defaults, версия приложения и source Nginx обновлены локально.
  • Конфигурация буфера и cron уже установлены на VM213.
  • Изменения приложения ещё не запушены и не redeployed; end-to-end загрузка через UI Контрактора и обработка документов ещё не проверены.