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

158 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Инцидент: ошибки загрузки файлов через 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`](../site/static/app.js) содержит
`VM_UPLOAD_URL = 'https://contracts.kube5s.ru/contracts-upload/'`.
- [`site/config.py`](../site/config.py) задаёт тот же домен для `VM_UPLOAD_URL`
и `VM_UPLOAD_PREFIX`. Backend регистрирует `upload_refs` с этим
`VM_UPLOAD_PREFIX` через [`site/routes/__init__.py`](../site/routes/__init__.py).
- [`deploy/nginx-contracts.conf`](../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`](../../../drhider/OBFUSCATOR/README.md) и
настроено в
[`obfuscator-upload.conf`](../../../drhider/OBFUSCATOR/deploy/vm213/nginx/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`](../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`](../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`](../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`](../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 Контрактора и обработка документов ещё не проверены.