Итоговое расследование: выгрузка не работает
Чтение и практика ~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). Новое — задача целиком: жалоба сформулирована пользователем, а не диагностом, причина в ней не названа, и готовой цепочки нет. Ошибка, которую вы увидите первой, может оказаться не единственной: после починки первого симптома остаётся ещё то, чего пользователь хотел на самом деле.
Что вы делаете сами, без подсказки главы:
- Превращаете жалобу в проверяемое условие: что именно должен увидеть пострадавший клиент (код, версия, содержимое, проверка сертификата включена).
- Различаете три вопроса, которые легко принять за один: отвечает ли сайт, получает ли пользователь нужный результат, не сломан ли кто-то ещё.
- До любых правок записываете гипотезы и предсказание, выбираете различающее наблюдение, а следующий шаг обосновываете предыдущим результатом, а не номером по списку.
- Для решения указываете область действия, цену, откат и условие закрытия — и отделяете временную меру от исправления причины.
- Заканчиваете запиской сменщику.
Что настоящее, что модель.
| Что | Статус |
|---|---|
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, у меня на ноутбуке всё открывается. Пусть Вера просто отключит проверку сертификата в скрипте, и закрываем.
Условия закрытия, как их сформулировали пользователи (проверяете вы сами, по запросу пострадавшего клиента):
- Вера, проверка сертификата включена:
200,X-App-Version: 3.1, в списке «Отчёт за октябрь». - Глеб: то же самое.
legacy-sync: по-прежнему200, версия3.0, заметка «Договор №42».- Прежний экземпляр жив, его записи на месте; новая запись Веры видна Глебу.
Воспроизведите симптом: выполните GET /api/notes от имени Веры, затем от имени Глеба и legacy-sync — и сравните.
Мини-задача
Задача: выяснить, почему выгрузка Веры не работает, вернуть ей нужный результат так, чтобы выполнились все четыре условия закрытия, и оставить записку сменщику. Готовой цепочки команд нет.
До любых правок (наблюдать можно, менять нельзя) запишите:
- Не меньше двух гипотез о причине (одна из них — слова Петра: «её клиент, пусть отключит проверку»); для каждой — что подтвердило бы её и что бы исключило.
- Предсказание: что вы увидите, если верна главная гипотеза.
- Различающее наблюдение: не «ещё один запрос», а такое, исход которого различит гипотезы. Выполните его и запишите, что оно исключило.
- Следующее наблюдение и почему именно оно: ссылкой на предыдущий результат («получили 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", которой нет у Глеба. Вторая строка не мешает проверке
сертификата, поэтому первая ошибка скрывает вторую: до данных дело не доходит.
Две причины.
- Файл доверия в профиле Веры — не та CA (устаревший файл «прошлого года»), поэтому curl не находит издателя сертификата: код 60.
- Заголовок
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 строк), по фактам:
- Причина (или причины), с уточнением, что установлено запуском, а что предполагается.
- Доказательства: какие наблюдения (цитаты) это показали.
- Что сделано и что не тронуто (и почему).
- Остаточный риск: что ещё может выглядеть так же и не проверено. Маршрут «версия 2 → прежний экземпляр» остаётся по договорённости с владельцем
legacy-sync: назовите, кто им владеет и чьи ещё запросы с этим заголовком не проверялись. - Кто и что проверяет дальше: например, кто решает о переносе данных, кто подтверждает, что профили Веры и Глеба теперь одинаковы.
Подсказки
Подсказки необязательны, штрафа за них нет: возьмите столько, сколько нужно.
Подсказка 1 — направление
Вере нужен не «ответ», а определённый результат. Что именно из её слов можно проверить запросом? И первое сообщение об ошибке — единственное ли, что стоит между ней и этим результатом?
Подсказка 2 — нужное наблюдение
Сравните, как curl завершает проверку сертификата у Веры и у Глеба (--verbose), затем сравните их профили целиком, а не только строку про доверие.
После исправления первого симптома запросите ещё раз и посмотрите не на код ответа, а на версию и список заметок.
Подсказка 3 — разбор
Сервер и сертификат исправны: Глеб проверяет их без ошибок. У Веры устаревший файл доверия (код 60), а за ним скрыт заголовок версии API, который ведёт её запросы
на прежний экземпляр. Починка только доверия даёт 200 и версию 3.0 — не то, что нужно. Исправление причины — в профиле Веры (В); общий маршрут и останов прежнего экземпляра
ломают legacy-sync. Отключение проверки (А) — не решение. Копия профиля Глеба (Е) условия выполняет, но грубее точечной правки (В).
Второй вариант
Пятница, 23 октября, 09:00. Тот же сервис, другое окружение. Вера пишет почти то же самое: «скрипт падает, в сообщении что-то про сертификат; мне нужна версия 3.1 и «Отчёт за октябрь»».
Пётр: «Вчера лечилось заменой файла доверия и удалением заголовка версии из профиля — сделайте то же самое». Перезапустите лабораторию со сценарием ch7b
(python3 lab/netlab.py start ch7b; профили новые, пути новые) и действуйте так же, как в основной части.
Условия закрытия те же, плюс три:
- Покажите запуском, почему вчерашний ремонт здесь не работает: примените его буквально и запишите результат — не «потому что другой профиль».
- Назовите наблюдение, которое отличает этот случай от вчерашнего, и найдите, где теперь причина — по журналам и
certinfo, а не по догадке. - После исправления первой ошибки проверьте ещё раз и Веру, и Глеба, и
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 — первая ошибка может скрывать последствия второй правки. Б не снимает жалобу.
Источники
- curl,
--cacertи коды завершения: 60 — проверка сертификата сервера не прошла;--insecureотключает проверку. - Everything curl, «Server certificate verification»: как curl проверяет цепочку и имя.
- RFC 5280, «Certification Path Validation» и срок действия сертификата.
- RFC 9110, «Header Fields»: заголовок
X-Api-Version— условное имя учебной службы, не стандартный заголовок; маршрутизация по нему — решение владельца входа.
Ещё одна допустимая альтернатива второго варианта
После замены сертификата можно согласованно задать X-Api-Version: 3 обоим клиентам,
Вере и Глебу, оставив профиль legacy-sync прежним. Для трёх названных клиентов все
условия выполняются. Это более узкая правка: маршрут по умолчанию остаётся старым
для неизвестных потребителей. Назовите этот риск и передайте его владельцу входа.
Браузерный кейс принимает такой результат, если проверены все условия; скрытого запрета нет.
Прочитали и разобрались? Отметьте урок — прогресс сохранится в этом браузере.