Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
# Инцидент: ошибки загрузки файлов через 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 Контрактора и обработка документов ещё не проверены.
|
||||
Reference in New Issue
Block a user