Привет, Хабр! Меня зовут Николай и уже 17 лет я работаю сетевым инженером. Обычный вопрос для моей профессии: «Доходит ли трафик куда нужно?» И десятки лет на него отвечают одним словом — tcpdump. А теперь зададим другой вопрос: «Как поймать трафик, если доступных путей много?» Неужели вариант один: открывать десятки консолей?
В СберМаркете я работаю с начала 2022 года и занимаюсь автоматизацией рабочих процессов. Одной из моих задач и стало найти решение на вопросы, рассмотренные выше.
В этой статье я хочу поделиться разработанным нами быстрым решением для получения дампа трафика с множества серверов в одном окошке. Мы уже выкатили его в опенсорс. В статье же подробно расскажу, как и зачем этим решением пользоваться.
Постановка проблемы
Балансировка, избыточность, резервирование, асимметрия — давно привычные и нужные понятия в сетях передачи данных. Облачные технологии их реализацию выводят на новый уровень — теперь решение задачи по развертыванию сотен облачных маршрутизаторов можно отточить так, что она займет всего несколько минут. И не надо железки по датацентрам таскать, облачные провайдеры уже сделали это за нас :) Но в ходе эксплуатации такого множества точек маршрутизации всё ещё возникают задачи диагностики сетевых проблем, требующие снимать дамп трафика.
По известным причинам некоторое время назад, дабы просто не потерять сетевую инфраструктуру, мы стали пристально смотреть на Open Source и приняли решение в качестве машин, передающих и маршрутизирующих трафик, взамен известных брендов использовать сервера с Linux. Теперь в местах, где раньше дамп трафика был невозможен, он стал доступен из коробки старым добрым tcpdump.
Балансировка и резервирование, разумеется, только увеличились. Мест, откуда теперь можно снимать дамп, стало очень много. Сам собой всплыл вопрос, как бы это делать наиболее удобно — без необходимости открывать десятки консолей, писать дампы, стягивать их куда-то в одно место, а потом сводить в какую-то общую картину. И мы начали небольшое исследование в этом направлении.
«Может использовать готовое решение?»
Поиски в этом направлении довольно быстро нас приводят к rpcapd и некоторому множеству самоделок с околонулевой популярностью.
Мы быстро сделали вывод, что надеяться на код каких-то энтузиастов, во-первых, опасно с точки зрения ИБ, во-вторых, имеет кучку вопросов без внятных ответов. Как часто код обновляется и обновляется ли вообще? Достаточный ли для нас уровень удобства использования? Сможем ли мы сами что-то туда добавить и насколько затратно это будет? И так далее. Общий ответ на эти вопросы просится сам собой: если мы готовы использовать самоделку, то делать её надо самим.
Приключение с Rpcapd
Рассмотрим самое популярное решение нашей задачи — rpcapd. Начнем по классике — с мануала, который логично располагается на tcpdump.org.
В разделе INSTALLING RPCAPD ON UNIX-LIKE SYSTEMS на момент написания статьи видим лишь TBD (подлежит уточнению). Стало быть с установкой на Linux эта история обещает приключения.
Гуглинг на тему сборки под Linux (rpcapd linux install) видимо уже много лет ведет на репозиторий в гитхабе с модифицированным rpcapd, а также libpcap (зачем, спрашивается).
Там мы видим:
Последний раз код обновлялся 8-12 лет назад.
Описание в репозитории сообщает, что это вообще-то история под Windows, поэтому для Linux надо «патчить» и rpcapd и libpcap.
В описании читаем: «It is still quite messy and may not compile or work», т. е. сам создатель предупреждает, что код беспорядочный и может не собраться. Что ж… это не внушает доверия.
Забегая вперед — я его всё же собрал, но, как оказалось, зря.
Клиентская часть
Для бо́льшего удобства использования, кажется, кроме Wireshark тут и рассматривать больше нечего. Имея на своей рабочей станции MacOS или Linux сразу сталкиваемся с тем, что Wireshark не умеет в rpcapd... Почему? Ответ на этот вопрос разработчики Wireshark и libpcap нам дают вот тут — Unix-системы обеспокоены безопасностью:
By default, libpcap on UN*Xes is built without rpcap support, as it increases the "attack surface" of libpcap. The current version should be robust against a malicious server, but we (the libpcap developers) aren't at the point where we'd want to enable it by default yet.
Вот и ответ почему установка rpcapd в Linux — это нестандартная история. На клиентской стороне она выглядит так же, ибо rpcap это не функция wireshark, это фишка libpcap.
Фиксируем: в libpcap почти во всех(а может и во всех) unix-like дистрибутивах выключен rpcap по причинам связанным с безопасностью. Однако, если очень хочется, то можно пересобрать libpcap и на клиентской машине. В том же ответе нам сообщают как.
Итог по Rpcapd
С ним дело обстоит так:
на серверах необходимо пересобирать libpcap;
для linux-серверов rpcapd и libpcap необходимо собирать из исходников энтузиастов (возможно тут ошибаюсь, но других вариантов не нашел);
если у вас клиентская машина unix-like, также необходимо пересобрать libpcap;
на серверах необходимо содержать (запуск, настройка, авторизация, сетевой доступ до его портов) ещё один демон — rpcapd.
Хотелось иметь решение в формате «запустил и работает», а необходимость перекомпилить код такого надежного мамонта как libpcap на каждом сервере и на каждой рабочей станции с unix-like совсем не выглядит таким решением.
Решение: делаем сами
Посмотрев на картину выше, решили что надо создавать что-то самостоятельно. Клиент-серверный подход кажется неразумным, да и зачем нам придумывать какой-то «свой» rpcapd, если он уже есть и он нам не подходит.
Отправную точку для решения задачи без серверной части, но с сохранением простоты использования подсказал коллега из отдела (спасибо Рома!) Есть давно известный «хак» для отправки дампа с удаленного сервера прямо в Wireshark:
На удаленном сервере запускается tcpdump и поток дампа в формате pcap (да, tcpdump всё ещё толком не умеет pcapng).
Мы получаем на stdout своего стандартного ssh клиента и отправляем его в Wireshark.
ssh root@${h} -i ~/.ssh/eve -C 'tcpdump -U -i '${i}' -w -' |
/Applications/Wireshark.app/Contents/MacOS/Wireshark -i - -k 2>/dev/null &
Идея: такую же логику реализовать с помощью скрипта сразу для нужного количества серверов. Именно такую реализацию я и принялся воплощать в жизнь, решая по пути проблемы, которые она неминуемо с собой несёт.
Ограничения формата Pcap
Tcpdump, к сожалению, всё ещё отдает pcap. В этом формате, изучая заголовок, видим, что в нем присутствует поле LinkType. Если коротко, оно определяет, каким образом далее в файле следует читать блоки с пакетами трафика. То есть, если вы собирали трафик на интерфейсе «eth0», то это будет один LinkType, «any» — другой, и так далее, их много. Более того в зависимости от версии дистрибутива, с которого вы собираете дамп, LinkType для того же «any» может меняться.
Такое ограничение формата делает невозможным в одном pcap собрать пакеты с разных интерфейсов разных серверов. Если так сделать, то любой визуализатор формата pcap отобразит только те пакеты, формат которых соответствует LinkType в заголовке. Остальные будут представлять из себя кашу (проверено!)
И тут на помощь приходит формат pcapNG. Разбирать его не будем, на Хабре уже есть статьи о нем, одна из которых мне собственно и помогла в написании скрипта.
Переход на PcapNG
Первым делом стоит задача конвертации pcap (который отдает tcpdump) в pcapng. Следуя по пути наименьшего сопротивления, ищем готовое решение — оно находится тут же, рядом с Wireshark. Это dumpcap, который поставляется с Wireshark. Для конвертации pcap в pcapng берём поток с stdout нашего ssh и отправляем его в stdin dumpcap. Подбираем ему нужные флаги и наша команда принимает вид:
ssh root@${h} -i ~/.ssh/eve -C 'tcpdump -U -i '${i}' -w -' | dumpcap -i - -w - -q
|/Applications/Wireshark.app/Contents/MacOS/Wireshark -i - -k 2>/dev/null &
Один вопрос решён: теперь в Wireshark видим данные в формате pcapng.
Некоторые флаги тут ключевые, они позволяют нам потоково передавать данные из одной команды в другую, что в итоге даёт нам возможность наблюдать появление пакетов в Wireshark практически в реальном времени.
Собираем в PcapNG
Заголовок PcapNG состоит из блоков, некоторые из них можно применять несколько раз и неважно, в каком месте файла они располагаются. Для нашей задачи достаточно использовать SHB, IDB и EPB.
SHB — этот блок является общим заголовком дампа. И да, их тоже может быть несколько, но следующий SHB в дампе означает, что далее в файле идет следующий дамп, не связанный с теми данными что были в файле до него. Данный заголовок для нашей задачи нужен только один. С нескольких серверов дамп каждого, пропущенный через dumpcap, разумеется, даст нам и несколько заголовков SHB. Сохраним первый попавшийся, а остальные просто отбросим. Наша задача поставлена так, что трафик с нескольких серверов как-то логически связан, а поэтому считаем что дамп у нас один.
IDB — блок описания интерфейса, с которого снят дамп. Ключевой момент для решения нашей задачи. Для pcapng именно в этом блоке указан LinkType. IDB блоков в файле будет несколько, каждый со своим LinkType, что позволяет нам блоки с пойманными пакетами связать с соответствующим блоком IDB и, соответственно, читать их с правильным LinkType. Таким образом Wireshark сможет адекватно отобразить пакеты с разных серверов и разных интерфейсов.
Данный блок дает нам интересный бонус. В нем возможно наличие поля Options, в которое можно положить любые произвольные данные. Я воспользовался полем Options чтобы сохранить в нём имя хоста и название интерфейса с которого снят дамп. Далее в WireShark можно включить отображение этого поля. Для каждого пакета мы увидим хост и интерфейс, на котором пакет был пойман.
В поле Interface name видим название хоста, на котором пойман пакет, и через подчеркивание название интерфейса.
EPB — блок с данными пойманного пакета. Сложность для нашей задачи состоит в том, что он связан с IDB. Каждый EPB содержит поле Interface ID, указывающее на блок IDB. Без правильного указания на нужный IDB Wireshark не сможет корректно отобразить EPB (то есть данные пакета, ради чего всё и затевалось). Interface ID — это просто цифра, номер блока IDB по порядку в файле pcapng, начиная с нуля. Т.к. дамп с каждого сервера неизвестно когда может начать прилетать, то все блоки IDB необходимо считать по порядку начиная с нуля, запоминать с какого именно dumpcap он нам прилетел, хранить эти номера и далее переписывать их в блоках EPB, прилетевших с того же dumpcap.
В итоге принципиально схема работы скрипта выглядит так:
Итоги
Выше примеры решений представлял на bash, но рабочий вариант был написан на Golang. Код решено было разместить в Open Source репозитории СберМаркета. Можно брать и пользоваться, собирается для Windows, Linux и Macos. Форки приветствуются.
Честно говоря, ещё есть много идей, каким функционалом для удобства можно было бы дополнить, но задача решена до того уровня, что мы им пользуемся в работе, поэтому думаю, что и вам это решение может пригодиться. А выдать идеальный код и поддерживать его — к сожалению, это слишком затратно для обычного инструмента одного отдела. Да и написание полноценных приложений и их поддержка это всё же работа не сетевиков, а разработчиков. Поэтому если кто-то возьмёт доделывать/переделывать или просто, прочитав статью, напишет что-то своё более юзабельное — считаю цели эта статья достигла.
Идеальным же решением поставленных вопросов считаю появление такого функционала прямо в самом Wireshark. У него для этого всё есть. Да и давно уже пора уходить от концепции «дампим трафик с сервера» в концепцию «дампим трафик с сети».
Всем спасибо! Критику приветствую :)
Tech-команда СберМаркета ведет соцсети с новостями и анонсами. Если хочешь узнать, что под капотом высоконагруженного e-commerce, следи за нами в Telegram и на YouTube. А также слушай подкаст «Для tech и этих» от наших it-менеджеров.
gudvinr
Сбер
github
Oh, irony
Shaman_RSHU
Как так-то github? У них же есть свой православный https://gitverse.ru/, который, как написано в его документах, как раз и предназначен для размещения opensource репозиториев :)
UPD: Не увидел Oh, irony