Перейти к содержимому
RootPoligon

Итоговое расследование: выгрузка не работает

Чтение и практика ~60 мин

Вера не может сделать выгрузку. У коллеги всё открывается. Возьмите заявку целиком: от наблюдений и гипотез до результата трёх клиентов и записки сменщику. Нужны главы 1–6; на основной вариант заложите час, на второй — ещё 40 минут.

Подготовка: отдельный пакет итоговой лаборатории

Скачать лабораторию глав 1–7 · Памятка · SHA256

Старые архивы не содержат главу 7. Остановите свой прежний сценарий через Ctrl+C, распакуйте новый архив в отдельную папку и откройте network-diagnostics в двух терминалах. Нужны Linux/Ubuntu 24.04, Python 3.12, curl, ss, nc и OpenSSL 3. Встроенного терминала нет.

Первый терминал: python3 lab/netlab.py start ch7. Он напечатает пути к vera.conf, gleb.conf, legacy-sync.conf и учебной CA. Второй терминал — для запросов. Запишите реальную папку профилей, например P=/tmp/netlab-clients-…. Общий URL: https://notes.lab.test:18443/api/notes. При втором варианте остановите первый запуск и начните ch7b; пути профилей будут новые.

Все слушатели только на loopback. Параллельно два сценария не запускайте; при занятом порте не завершайте чужие процессы. Системные DNS, hosts, часы и доверие не меняются. Учебной CA доверяйте только через отдельный --cacert или временный профиль лаборатории, не устанавливайте её в систему. Ключи и профили удаляются при остановке.

Что нового

Новых протоколов здесь нет. Все сегменты вам знакомы: имя (глава 2), клиент и его условия (глава 6), адрес и порт (глава 3), посредник (глава 4), HTTPS (глава 5). Новое — задача целиком: жалоба сформулирована пользователем, а не диагностом, причина в ней не названа, и готовой цепочки нет. Ошибка, которую вы увидите первой, может оказаться не единственной: после починки первого симптома остаётся ещё то, чего пользователь хотел на самом деле.

Что вы делаете сами, без подсказки главы:

  1. Превращаете жалобу в проверяемое условие: что именно должен увидеть пострадавший клиент (код, версия, содержимое, проверка сертификата включена).
  2. Различаете три вопроса, которые легко принять за один: отвечает ли сайт, получает ли пользователь нужный результат, не сломан ли кто-то ещё.
  3. До любых правок записываете гипотезы и предсказание, выбираете различающее наблюдение, а следующий шаг обосновываете предыдущим результатом, а не номером по списку.
  4. Для решения указываете область действия, цену, откат и условие закрытия — и отделяете временную меру от исправления причины.
  5. Заканчиваете запиской сменщику.

Что настоящее, что модель.

ЧтоСтатус
curl, проверка сертификата, коды выхода, -v, чтение файла настроекнастоящее (curl 8.5.0, OpenSSL 3.x на этой стороне)
Сертификаты и учебная CAнастоящие X.509, выпущены openssl во временной папке; в системное хранилище не добавляются, часы не меняются
Профили vera.conf, gleb.conf, legacy-sync.confнастоящие файлы настроек curl (папка netlab-clients-…, права 0700)
HTTPS-вход lab-edge, маршруты по заголовку, два экземпляра «Заметок», учебный DNSмодель на Python; маршрутизация по заголовку X-Api-Version придумана для учебной задачи и не копирует настройки конкретного сервера
Браузеры, VPN, настоящий nginx / Squid / облачный балансировщик, отдельные сетине проверялись и не моделируются

Инструменты (порядок и состав выбираете сами; готовой цепочки нет):

python3 lab/netlab.py client --profile ФАЙЛ URL              # запрос от имени клиента: настоящий curl с настройками из ФАЙЛА
python3 lab/netlab.py client --profile ФАЙЛ --verbose URL    # то же с -v: рукопожатие, проверка сертификата, заголовки
python3 lab/netlab.py client --profile ФАЙЛ --data '{"title": "…"}' URL   # запись (POST)
python3 lab/netlab.py dig notes.lab.test                     # что отвечает учебный DNS
python3 lab/netlab.py certinfo URL                           # что отдаёт сервер: субъект, имена, издатель, срок; вердикта нет
openssl x509 -in ФАЙЛ -noout -subject -issuer -dates         # что внутри файла сертификата (в том числе файла доверия)
cat ФАЙЛ; diff ФАЙЛ1 ФАЙЛ2                                    # профили — обычные текстовые файлы
ss -ltnp                                                      # кто слушает какой порт
python3 lab/netlab.py config
python3 lab/netlab.py logs --source edge
python3 lab/netlab.py logs --source app

client запускает настоящий curl -q -sS -i … -K ФАЙЛ: -q — файл ~/.curlrc не читается; переменные окружения *_proxy убираются; внешняя сеть не нужна. Учебный DNS спрашивает оболочка client, сам curl DNS не опрашивает. Записей в профиль, которые вы добавите сами, вы же и откатываете.

Ситуация

Четверг, 22 октября, 09:20. В канал поддержки пишет Вера из отдела отчётности:

Выгрузка «Заметок» для отчёта за октябрь перестала работать: скрипт экспорта падает при обращении к службе, в сообщении что-то про сертификат. Мне нужно, чтобы запрос списка заметок возвращал версию 3.1 и в списке была заметка №4 «Отчёт за октябрь» — она нужна для выгрузки. Проверка сертификата должна остаться включённой. Глеб (второй пользователь той же службы) должен получать то же самое. Старый скрипт синхронизации (legacy-sync) трогать нельзя: он должен продолжать получать то, что получал (версию 3.0 и заметку №4 «Договор №42»). Записи старой версии не потерять.

Дежурный Пётр:

Сайт жив: /health отвечает 200, у меня на ноутбуке всё открывается. Пусть Вера просто отключит проверку сертификата в скрипте, и закрываем.

Условия закрытия, как их сформулировали пользователи (проверяете вы сами, по запросу пострадавшего клиента):

  1. Вера, проверка сертификата включена: 200, X-App-Version: 3.1, в списке «Отчёт за октябрь».
  2. Глеб: то же самое.
  3. legacy-sync: по-прежнему 200, версия 3.0, заметка «Договор №42».
  4. Прежний экземпляр жив, его записи на месте; новая запись Веры видна Глебу.

Воспроизведите симптом: выполните GET /api/notes от имени Веры, затем от имени Глеба и legacy-sync — и сравните.

Мини-задача

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

До любых правок (наблюдать можно, менять нельзя) запишите:

  1. Не меньше двух гипотез о причине (одна из них — слова Петра: «её клиент, пусть отключит проверку»); для каждой — что подтвердило бы её и что бы исключило.
  2. Предсказание: что вы увидите, если верна главная гипотеза.
  3. Различающее наблюдение: не «ещё один запрос», а такое, исход которого различит гипотезы. Выполните его и запишите, что оно исключило.
  4. Следующее наблюдение и почему именно оно: ссылкой на предыдущий результат («получили X, значит теперь нужно проверить Y»), а не по номеру шага.

Дальше действуйте по своим наблюдениям. Когда причина (или причины) названа, сформулируйте проверяемое условие закрытия для каждой — и только затем выбирайте решение (см. «Развилку»).

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

Разбор мини-задачи

Разбор: откройте после своей попытки

Гипотезы (пример; ваши могут быть другими, если различимы).

ГипотезаЕсли вернаЧто исключает
Сертификат сервера неправильный (имя, срок)certinfo покажет несовпадение имени или истёкший срок; сломан не только один клиентcertinfo: имя в SAN верное, срок действует; Глеб с тем же сервером получает 200
У Веры не тот файл доверия (CA)curl -v у Веры: проверка цепочки не прошла, у Глеба — прошла; файл доверия (cacert) в профилях разный; остальные строки сравните целиком, не только этуесли бы файл доверия был верным, сбой остался бы и у неё, и у Глеба
Сервер или посредник недоступенdig, ss, журнал входа покажут отказ соединениясервер отвечает всем остальным; код выхода 60 (проверка), а не 7 (соединение)
«Её клиент, пусть отключит проверку» (слова Петра)после отключения проверки Вера получит нужный результатотключение проверки даёт 200, но версию 3.0 и «Договор №42»: результата нет, а проверка потеряна

Различающее наблюдение: сравнить, что curl успел узнать до отказа, с тем, что видят остальные. client --verbose у Веры: unable to get local issuer certificate, код 60; у Глеба — SSL certificate verify ok. Один и тот же сервер, один и тот же сертификат → ошибка не на стороне сервера. diff профилей Веры и Глеба (кроме комментария и user-agent) даёт два отличия: строка cacert — и вторая, не связанная с первой: у Веры есть строка header = "X-Api-Version: 2", которой нет у Глеба. Вторая строка не мешает проверке сертификата, поэтому первая ошибка скрывает вторую: до данных дело не доходит.

Две причины.

  1. Файл доверия в профиле Веры — не та CA (устаревший файл «прошлого года»), поэтому curl не находит издателя сертификата: код 60.
  2. Заголовок X-Api-Version: 2 отправляет входу «маршрутизируй как клиента версии 2», и вход направляет запрос на прежний экземпляр. Это не ошибка само по себе — именно так сделан legacy-sync и по замыслу так и остаётся.

Почему «сайт жив» и «у меня работает» не закрывают жалобу. /health — это 200 для всех; запрос автора с верным файлом доверия возвращает 3.1; Глеб получает 3.1. Но Вера получает код 60, а после замены файла доверия — 200 и версию 3.0 с чужой заметкой. «Ответ получен» и «ответ правильный» — разные вопросы.

Что не сработает. Отключить проверку (insecure): код 200, но версия 3.0, а защита выключена. Менять общий маршрут «версия 2» на новый экземпляр: Вере поможет, а legacy-sync перестанет получать свои данные. Остановить прежний экземпляр: legacy-sync получит 502, записи станут недоступны. Заменить профиль Веры копией профиля Глеба: все условия закрытия выполнятся, но заменяется весь файл — собственные настройки Веры (в лаборатории это только user-agent) пропадают.

Развилка: 10:10, что делать

Входом и службой владеете вы; профиль Веры — её файл, она разрешила вам его править; профиль legacy-sync — файл другой команды, его не трогаем. Решения (порядок случайный):

  • А. Отключить проверку сертификата в профиле Веры (insecure) — как предложил Пётр.
  • Б. Заменить в профиле Веры файл доверия на учебную CA лаборатории.
  • В. Б и убрать из профиля Веры заголовок версии API.
  • Г. Б и сменить общий маршрут версии 2 на новый экземпляр: python3 lab/netlab.py set edge_v2=127.0.0.1:19000.
  • Д. Остановить прежний экземпляр: python3 lab/netlab.py set old_app=off.
  • Е. Заменить профиль Веры копией профиля Глеба (весь файл).
  • Ж. Ответить Вере: «у меня работает, /health — 200» — и закрыть заявку.

Для выбранного решения запишите: область действия (кого оно задевает), цену, откат и условие закрытия (что именно вы увидите у Веры, у Глеба и у legacy-sync).

Последствия
  • А. Вредное и не решает задачу: 200, но версия 3.0 и «Договор №42»; проверка сертификата выключена. Область: один клиент. Откат: убрать строку.
  • Б. Частичное: ошибка 60 уходит, 200 приходит, но версия 3.0 — жалоба не снята (заголовок версии 2 остался). Закрывать нельзя.
  • В. Исправление причины на стороне клиента: Вера получает 3.1 с «Отчётом за октябрь» при включённой проверке; Глеб и legacy-sync не затронуты (проверьте обоих). Область: один файл одного клиента. Цена: если заголовок версии 2 был нужен самой Вере для чего-то ещё, нужна договорённость. Откат: вернуть прежние строки. Условие закрытия — приёмка ниже.
  • Г. Вредное: Вере (после Б) поможет, но legacy-sync теперь получает 3.1 без своих данных. Область: все клиенты с заголовком версии 2. Откат: set edge_v2=127.0.0.1:19001.
  • Д. Вредное: legacy-sync получает 502, его записи (в хранилище остались) недоступны никому. Откат: set old_app=on.
  • Е. Допустимое функциональное решение: все четыре условия закрытия выполняются (Вера 3.1 с включённой проверкой, Глеб, legacy-sync, запись 201 видна Глебу, прежний экземпляр жив). Цена: заменяется весь профиль — пропадают собственные настройки Веры (здесь только user-agent, в жизни там могут быть прокси, таймауты, пути к ключам), копия не следует за будущими правками профиля Глеба, нужно согласие Веры. Откат: вернуть сохранённый до замены файл. Хуже В: точечная правка видна и обратима, а копия не показывает, какая строка была причиной. Условия закрытия не требуют сохранить user-agent: это строка о клиентском ПО, а не способ входа пользователя.
  • Ж. Бесполезное: запрос с вашей машины идёт с правильным файлом доверия и не проходит через условия Веры.

Про данные. У прежнего экземпляра есть заметка №4 «Договор №42», которой нет в новом. Перенос записей между экземплярами — решение владельца службы; в главе вы его не выполняете, но не гасите прежний экземпляр и в записке сменщику назовите, кто принимает решение о переносе.

Приёмка

  • Гипотезы, предсказание и различающее наблюдение записаны до первой правки; для каждого следующего шага названо, на каком результате он основан.
  • Названа каждая причина (их больше одной) и для каждой — что её доказывает (цитата вывода, не догадка).
  • Для выбранного решения указана область, цена, откат и условие закрытия; временная мера названа временной.
  • Запрос от пострадавшего клиента при включённой проверке сертификата: client --profile vera.conf URL — 200, X-App-Version: 3.1, в списке «Отчёт за октябрь». Профиль не содержит insecure.
  • Второй клиент: Глеб — 200, 3.1, «Отчёт за октябрь».
  • Клиент, которого нельзя ломать: legacy-sync — 200, 3.0, «Договор №42».
  • Данные: запись от Веры (--data) — 201 и видна Глебу; прежний экземпляр жив, в нём «Договор №42». Перенос записей — передан владельцу в записке, а не молча выполнен и не молча отброшен.
  • Не принято: запрос с вашей машины; 200 без сравнения версии и содержимого; отключённая проверка сертификата; останов прежнего экземпляра; общий маршрут, под который попал legacy-sync.
  • Условия проверяются по отдельности: проверка TLS, GET трёх клиентов, запись и чтение, сохранность данных. Один GET 200 зачёта не даёт.
  • Записка сменщику (см. ниже).

Записка сменщику

Короткая (7–12 строк), по фактам:

  1. Причина (или причины), с уточнением, что установлено запуском, а что предполагается.
  2. Доказательства: какие наблюдения (цитаты) это показали.
  3. Что сделано и что не тронуто (и почему).
  4. Остаточный риск: что ещё может выглядеть так же и не проверено. Маршрут «версия 2 → прежний экземпляр» остаётся по договорённости с владельцем legacy-sync: назовите, кто им владеет и чьи ещё запросы с этим заголовком не проверялись.
  5. Кто и что проверяет дальше: например, кто решает о переносе данных, кто подтверждает, что профили Веры и Глеба теперь одинаковы.

Подсказки

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

Подсказка 1 — направление

Вере нужен не «ответ», а определённый результат. Что именно из её слов можно проверить запросом? И первое сообщение об ошибке — единственное ли, что стоит между ней и этим результатом?

Подсказка 2 — нужное наблюдение

Сравните, как curl завершает проверку сертификата у Веры и у Глеба (--verbose), затем сравните их профили целиком, а не только строку про доверие. После исправления первого симптома запросите ещё раз и посмотрите не на код ответа, а на версию и список заметок.

Подсказка 3 — разбор

Сервер и сертификат исправны: Глеб проверяет их без ошибок. У Веры устаревший файл доверия (код 60), а за ним скрыт заголовок версии API, который ведёт её запросы на прежний экземпляр. Починка только доверия даёт 200 и версию 3.0 — не то, что нужно. Исправление причины — в профиле Веры (В); общий маршрут и останов прежнего экземпляра ломают legacy-sync. Отключение проверки (А) — не решение. Копия профиля Глеба (Е) условия выполняет, но грубее точечной правки (В).

Второй вариант

Пятница, 23 октября, 09:00. Тот же сервис, другое окружение. Вера пишет почти то же самое: «скрипт падает, в сообщении что-то про сертификат; мне нужна версия 3.1 и «Отчёт за октябрь»». Пётр: «Вчера лечилось заменой файла доверия и удалением заголовка версии из профиля — сделайте то же самое». Перезапустите лабораторию со сценарием ch7b (python3 lab/netlab.py start ch7b; профили новые, пути новые) и действуйте так же, как в основной части.

Условия закрытия те же, плюс три:

  1. Покажите запуском, почему вчерашний ремонт здесь не работает: примените его буквально и запишите результат — не «потому что другой профиль».
  2. Назовите наблюдение, которое отличает этот случай от вчерашнего, и найдите, где теперь причина — по журналам и certinfo, а не по догадке.
  3. После исправления первой ошибки проверьте ещё раз и Веру, и Глеба, и legacy-sync — а не только того, на кого пришла жалоба.

Решения (порядок случайный):

  • А. Повторить вчерашний ремонт в профиле Веры (файл доверия и заголовок).
  • Б. Перевыпустить сертификат (python3 lab/netlab.py set cert_valid=fresh) и закрыть заявку: «теперь всё открывается».
  • В. Перевыпустить сертификат и временно добавить в профиль Веры заголовок X-Api-Version: 3.
  • Г. Перевыпустить сертификат и сменить маршрут по умолчанию входа: python3 lab/netlab.py set edge_default=127.0.0.1:19000.
  • Д. Перевыпустить сертификат и сменить общий маршрут версии 2: python3 lab/netlab.py set edge_v2=127.0.0.1:19000.
  • Е. Остановить прежний экземпляр: python3 lab/netlab.py set old_app=off — сам по себе, без других правок (проверьте результат в том состоянии, в котором вы сейчас находитесь, и после замены сертификата).

Для выбранного решения — область, цена, откат и условие закрытия; кого оно задевает (к входу ходят и другие клиенты); записка сменщику.

Подсказка 1 — направление

Вчерашний ремонт менял профиль Веры. Что сейчас в профиле Веры по сравнению с профилем Глеба — и чей это тогда симптом?

Подсказка 2 — нужное наблюдение

Выполните запрос от всех трёх клиентов с --verbose: у кого и как заканчивается проверка сертификата? Что показывает certinfo про срок? Затем замените сертификат на вашей стороне и снова запросите — теперь смотрите на версию.

Подсказка 3 — разбор

Здесь у всех троих одна и та же ошибка проверки: срок действия сертификата сервера истёк; профиль Веры — рабочий, вчерашний ремонт его не меняет. После замены сертификата все получают 200, но маршрут по умолчанию входа указывает на прежний экземпляр: версия 3.0 у Веры и у Глеба. Заголовок версии 3 у Веры (В) — временная мера для одного клиента. Исправление причины — маршрут по умолчанию (Г): Вера и Глеб получают 3.1, legacy-sync по заголовку остаётся на 3.0. Д ломает legacy-sync. Е в исходном состоянии ничего не меняет в наблюдениях (у всех по-прежнему код 60: просроченный сертификат скрывает остановку), а после замены сертификата все три получают 502 — первая ошибка может скрывать последствия второй правки. Б не снимает жалобу.

Источники

Ещё одна допустимая альтернатива второго варианта

После замены сертификата можно согласованно задать X-Api-Version: 3 обоим клиентам, Вере и Глебу, оставив профиль legacy-sync прежним. Для трёх названных клиентов все условия выполняются. Это более узкая правка: маршрут по умолчанию остаётся старым для неизвестных потребителей. Назовите этот риск и передайте его владельцу входа. Браузерный кейс принимает такой результат, если проверены все условия; скрытого запрета нет.

Прочитали и разобрались? Отметьте урок — прогресс сохранится в этом браузере.