Perl для системного администрирования

       

Опасность на проводе


SNMP хорош для активного наблюдения (а также в некоторых предусматривающих реакцию ситуациях мониторинга, когда используют.'5'

SNMP-прерывания (traps)), но он не всегда помогает, если происходит что-то незапланированное, например авария в сети. В таких случаях, возможно, придется наблюдать за сетью другими способами, которые не охватываются доступными SNMP переменными.

Perl спасает положение

Вот правдивая история об этом, рассказывающая, как в таких случаях может помочь Perl. Однажды субботним вечером я, как обычно, зарегистрировался на одной из машин в моей сети, чтобы почитать почту, и, к своему удивлению, обнаружил, что наш почтовый и веб-серверы находятся практически «при смерти». Попытки прочитать или отправить почту или посмотреть на веб-сайт заканчивались медленными ответами, обрывами соединений и невозможностью установить соединение. Почтовая очередь начинала достигать критического размера.

Первым делом я проверил состояние серверов. Интерактивные ответы были нормальными, загрузка процессора - велика, но не смертельна. Признаком проблем было количество запущенных почтовых процессов. В соответствии с файлами журналов большее, чем обычно, количество процессов было вызвано тем, что многие транзакции не были завершены. Повисшими были процессы, запущенные для обработки входящих соединений, они и увеличивали загрузку. А эта загрузка уже мешала появлению новых исходящих соединений. Такое странное поведение сети заставило меня изучить таблицу текущих соединений сервера с помощью netstat.

Последний столбец вывода netstat говорил о том, что с внешним миром действительно было установлено много соединений. Большой неприятностью было состояние этих соединений. Вместо того чтобы выглядеть примерно так:

tcp 0 0 n-ailhub.3322 mail.nel.aone. nc. gui* р ESTABLISHED

tcp О О rnail'iub.3320 cdunet.edunet.dk.smtp CLOSE., WAIT

tcp 0 0 man 1-н,!). '723 k-aken. -wret, wne. sr',p ESTABLISHED

tcp 0 0 mail hub. 1709 pi over. net. D' idg. silt.:: CLOSE_WAIT

они больше напоминали следующее:






tcp 0 0 TiaiihuD. 3322 nail. mel. aone ne. sr.tp Si'N_PGiyD

tcp 0 0 r!'.ailnuD. 3320 ed ,riet. edunc-т.. gk. smp Sr'N_RCVD

tec 0 0 railnub 1723 гтаке^, nvnet .v,ne. sr^D SYN RCVD

tcp 0 0 pailf'oO. 1709 olover.ret or :dc;. s:"n; CLOSE wAI~

На первый взгляд, это было похоже на классическую атаку типа «отказ от обслуживания» (Denial of Service), называемую SYN Flood, или атакой SYH-ACK. Давайте отвлечемся на некоторое время и поговорим о  том, как работает протокол TCP/IP, чтобы понять, что собой представляют эти атаки.

Каждое TCP/IP-соединение начинается с «рукопожатия» между участниками. Это позволяет и инициатору, и получателю сообщить о своей готовности к беседе. Первый шаг предпринимает инициатор соединения. посылая получателю пакет SYN (от SYNchronize - синхронизировать). Если получатель готов к «общению», он посылает в ответ пакет 6Y', >, подтверждение (от ACKnowledgment) запроса, и записывает в таблице отложенных соединений, что должна начаться беседа. Инициатор отвечает на пакет SYM-ACK пакетом АСК, подтверждая, что пакет '-. <'1-М>: был получен. При получении пакета АСК получатель удаляет запись из таблицы, и начинается передача данных.

По крайней мере, именно так все и должно происходить. В случае с атакой SYN Flood, злоумышленник посылает на машину лавину пакетов;'. ',. часто подделывая при этом адрес источника. Ничего не подозревающая машина посылает по поддельным адресам пакеты SYN-АСК и добавляет запись в таблицу ожидающих соединений для каждого полученного пакета SYN. Эти записи остаются в таблице до тех пор, пока операционная система не признает их устаревшими по прошествии определенного вре -мени. Если было послано достаточно много пакетов, таблица ожидающих запросов переполнится, и все попытки установить вполне законное соединение завершатся неудачей. А это приведет к появлению описанных мною симптомов и подобному выводу netstat.

Но в выводе команды netstat была одна аномалия, которая ставила под сомнение мой диагноз - разнообразие узлов в таблице. Возможно, что кто-то обладает программой с отличными способностями к подделкам, но обычно соединения устанавливаются с меньшего числа фальшивых узлов. Кроме того, многие из этих узлов казались настоящими. Ничего не прояснили и даже ухудшили ситуацию некоторые выполненные мною проверки. Попытки выполнить команды ping или traceroute для случайно выбранных узлов из списка, предоставленного командой net stat, иногда завершались успешно, а иногда нет. Мне не хватало данных. Надо было лучше разобраться с соединениями по этим удаленным узлам. Тут мне на помощь пришел Perl.



Поскольку я писал программу, находясь « под дулом пистолета », для ра -боты с самой сложной частью проблемы у меня получился очень маленький сценарий, полагающийся на вывод двух внешних программ, анализирующих сеть и проделывающих наиболее сложную часть работы. Я покажу вам эту версию, используя ее в качестве трамплина для дальнейшего улучшения.

На этот раз задача свелась к одному вопросу: могу ли я добраться до уз лов, пытающихся связаться со мной? Для поиска узлов, пытающихся связаться с моей машиной, я воспользовался программой clog Брайена Митчела (Brian Mitchell), которую можно найти на ftp://coast.cx.pn> due.edu/pub/mirrors/ftp.saturn.net/clog. Для прослушивания сети в поисках запросов TCP-соединений, т. е. пакетов SYN, clog использует библиотеку ИЬрсарот Lawrence Berkeley National Laboratory's Network Re

search Group. Эту же библиотеку использует и эффективная программа наблюдения за сетью tcpdump. Библиотека libpcap с ftp://ftp.ee.lbl.gov/lib pcap.tar.Z работает и для машине Linux. Перенесенная версия libpcap для NT/2000 доступна на http://netgroup-serv.polito.it/windump/ или http:// www.ntop.org/libpcap.html, но хотелось бы также увидеть версию и для MacOS.

clog

сообщает о пакетах SYN таким образом:

Маг 02 11:21|192.168.1.51|1074|192,168.1,104(113 Маг 02 11:21|192.168.1.51|1094|192.168.1.104|23

Из примера видно, что получено два запроса на соединение от машины 192.168.1.51 к 192.168.1.104. Первый- это попытка соединиться с портом 113 (ident), а второй - с портом 23 (telnet).

Программа clog помогла мне выяснить, какие узлы пытались установить соединение со мной. Но мне надо было знать, могу ли я до них добраться. Эта задача выпала на долю программы fping Роланда Дж. Шемерса III (Roland J. Schemers III). Программа fping, которую можно найти на http://www.stanford.edu/~schemers/docs/fping/fping.html, - это быстрая и шикарная версия программы ping для тестирования работоспособности сети в Unix и его вариантах. Воспользовавшись этими двумя внешними программами, получаем маленькую программу на Perl:



Sclogex = "/usr/local/bin/clog"; # местоположение/ключи для

clog Sfpingex = "/us'r/local/bin/fping -r1"; # местоположение/ключи для fping

Slocalnet = "192.168.1"; ft префикс локальной сети

open CLOG, "$clogex|" or die "Невозможно запустить ciog:$!\n"; while(<CLOG>){

($date,$onghost.$ongport, Sdesthost,Sdestport) = split(/\|/);

next if (Songhost =" /~$localnet/);

next if (exists $cache{$onghost});

print 'Sfpingex Sorighost';

$cache{$orighost}=l; }

Эта программа запускает команду clog и считывает ее вывод до бесконечности. Поскольку наша внутренняя сеть вне подозрений, каждый узел сравнивается с префиксом локальной сети и весь трафик из локальной сети игнорируется.

На этот раз, как и в последнем примере, мы используем некоторое кэширование. Мы - добропорядочные жители сети и не собираемся закидывать внешние машины множеством пакетов ping, так что мы следим за тем, к каким узлам мы уже обращались с запросами. Флаг ~rl у fping служит для ограничения количества попыток обращения к узлу программой fping (по умолчанию предпринимается три попытки).

Эту программу необходимо выполнять с повышенными привилегиями, т. к. и программе clog, и программе fping нужен привилегированный доступ к сетевому интерфейсу компьютера. Вывод этой программы выглядит так:

199.174,175.99 is unreacrable

128,148.157 143 is unreachablo

204.241.60.5 is alive

199.2.26.116 is unreachable

199.172.62.5 is unreacnable

130.111.39.100 is alive

207.70.7.25 is unreachabie

198.214.63.11 is alive

129.186.1.10 is alive

Очевидно, что здесь творится нечто подозрительное. С чего вдруг половина узлов доступна, а половина - нет? Перед тем как ответить на этот вопрос, давайте посмотрим, что можно сделать для улучшения этой программы. Первый шаг - избавиться от зависимости от внешних программ. Умение прослушивать сеть и посылать пакеты ping из Perl открывает целый диапазон возможностей. Сначала позаботимся о том, чтобы удалить простую зависимость.



Модуль Net: : Ping Рассела Мосмана (Russell Mosemann), который можно найти в дистрибутиве Perl, помогает проверить работоспособность сети. Net: : Ping позволяет посылать пакеты ping трех типов и проверять возвращаемые ответы: ICMP, TCP и UDP. ICMP-пакеты (Internet Control Message Protocol)- это «классика ping», и их посылает подавляющее большинство программ, производных от ping. У пакетов этого типа есть два недостатка:

1. Как и в случае с программой, вызывающей clog/fping, все сценарии Net: :Ping, использующие ICMP, необходимо выполнять с повышенными привилегиями.

2. Perl на MacOS в настоящее время не поддерживает ICMP. Возможно, в будущем это будет исправлено, а пока не следует забывать об этом ограничении переносимости.

Два других варианта пакетов Net: :Ping -это пакеты TCP и UDP. В обоих случаях пакеты посылаются на порт службы echo удаленной машины. Эти две возможности обеспечивают переносимость, но вам они могут показаться менее надежными, чем ICMP. ICMP встроен во все стандартные стеки TCP/IP, но не на всех машинах может быть запущена служба echo. В результате, если ICMP не отфильтровывается намеренно, вы получите ответы на ICMP-пакеты с большей вероятностью, чем на пакеты других типов.

В Net: :Ping применяется стандартная модель объектно-ориентированного программирования, поэтому первым делом нужно создать новый экземпляр объекта

ping: use Net::Ping;

$p = new Net::Ping("icmp"):

Этот объект очень просто использовать:

if ($c->pir,g("hcst")){

prmt "oirg succeeded '' " ': else{

pr:it "p:ng faiied\n": }

Теперь вернемся к сложной части нашего первоначального сценария, прослушиванию сети с помощью clog. К сожалению, с этого момента нам придется отпустить пользователей MacOS. Программа на Perl, которую мы собираемся рассматривать, привязана к библиотеке libpcap, о которой мы говорили раньше, поэтому применение программы где-либо, кроме Unix и его вариаций, затруднено или невозможно.

Первый шаг, который необходимо выполнить, - собрать библиотеку libpcap. Я советую скомпилировать и tcpdump. Как и в случае с утилитами из командной строки для UCD-SNMP, tcpdump можно использовать для выяснения возможностей libpcap перед тем, как писать на Perl, а также для перепроверки кода, написанного для этой программы.



Имея libpcap, легко скомпилировать модуль Net: :Pcap, первоначально написанный Питером Листером (Peter Lister), а затем полностью переписанный Тимом Поттером (Tim Potter). Этот модуль открывает вам доступ ко всем возможностям

libpcap. Давайте посмотрим, как можно использовать его для поиска пакетов SYN, как это делает clog.

Программа начинается с запроса о доступном/допускающем прослушивание сетевом интерфейсе и его настройках:

use Net::Pcap;

# поиск сетевого устройства, допускающего прослуиивание

$dev = Net: :Рсар: : icoKupdev(\$en ) ;

die "Невозможно найти подходящее устройство: $err\n" unless Sdev;

tf выясняем номер и маску сет;.> эгого \'Стоойстра die "Невозможно выяснить информацию об устройстве:

В большинстве функций libpcap действуют соглашения о кодах возврата, принятые в С, и возвращают 0 в случае успеха и -1 в случае неудачи, поэтому в программах, использующих Net: : Рсар, часто применяется идиома с:е :*' . . В страницах руководства по рсар(З) можно выяснить смысл аргументов, передаваемых каждой функции.

Получив информацию о сетевом интерфейсе, можно сообщить libpcap о намерении прослушивать сеть (вместо того, чтобы читать пакеты из

сохраненного ранее файла пакетов). Net :Рса;> 'km," _r.vo возвращает дескриптор, используемый для ссылки на этот сеанс:

 открываем интерфейс для "живого" захвата

 die "HeB03vof.no пол>чить дескригтоо $е:- -Г ariesb Sceseriar:

Программа libpcap позволяет перехватывать как весь сетевой трафик, так и ограниченное подмножество пакетов, выбираемое в соответствии с заданным критерием фильтрации. Ее фильтрующий механизм очень эффективен, поэтому зачастую лучше напрямую вызвать его, чем потом в программе отсеивать ненужные пакеты. В данном случае нас интересуют только пакеты SYN.

Что собой представляет пакет SYN? Чтобы понять это, нужно иметь представление о том, как собираются TCP-пакеты. Посмотрите на рисунок из RFC973, где приведен TCP-пакет и его заголовок.

Пакет SYN - это тот пакет, в заголовке которого установлен только флаг SYN . Для того чтобы lihpcap «знала», что надо перехватывать только такие пакеты, следует опреде лить, какой именно байт в пакете она должна искать. Каждый штрих н;1 верху соответствует одному биту, так что нетрудно подсчитать байты Тот же пакет.



Нам необходимо проверить, равен ли 13-й байт двоичному числу 00000010 (десятичное число 2). В качестве фильтра нам нужна строка. Если бы мы хотели найти пакеты, у которых установлен по крайней мере флаг SYN, то могли бы использовать строку tcp[13] & 2 ! = 0. Этот фильтр затем компилируется в программу фильтрации и устанавливается:

Sprog = "tcp[13]- = 2";

# компилируем и устанавливаем "программу фильтрации" die "Невозможно скомпилировать $prog\n"

if (Net::Pcap::compile($descript ,\$compprog,Sprog,0SneLmasK)) ; die "Невозможно установить фильтр\п"

if (Net::Pcap::setfilter($descript,Scompprog));

Еще чуть-чуть и можно запускать libpcap. Но перед этим нужно определить, что делать с найденными пакетами. Для каждого пакета, соответствующего фильтру, она может по нашему выбору запустить подпрограмму обратного вызова. Этой подпрограмме передаются три аргумента:

1. Пользовательский строковый идентификатор, который может устанавливаться в начале перехвата и позволяет процедуре различать несколько сеансов перехвата пакетов.

2. Ссылка на хэш, описывающая заголовок пакета (отметки о времени и пр.).

3. Копия всего пакета.

Начнем мы с очень маленькой процедуры, выводящей длину полученного пакета:

sub printpacketiength {

print length($_[2]),"\n": }

Имея нужную подпрограмму, мы ищем пакеты SYIJ:

die "Невозможно перехватить пакеты: ". Not : : Рсар: :ge:err(Sdebcr ipt) \:~"

if (Net, :Pcap: : loop($descr ip:. -i. &p- -.tpao^i-fjir. ' )).

die "Невозможно закрыта уст роиство\п" if (Net: :Pcap; :close($descnpt)):

Второй аргумент -1 метода Net: : Рсар: : 1оор( )определяет количество пакетов, которые мы хотим перехватить до выхода. В данном случае мы будем перехватывать пакеты до бесконечности.

Приведенная выше программа перехватывает пакеты SYN и выводит их длину, но это не совсем то, чего мы хотели добиться в начале этого раздела. Нам нужна программа, которая будет искать пакеты SYN из других сетей и попытается «прощупать» (ping) источник. У нас есть практически все; пока не хватает только способа, позволяющего определить источник полученного SYN-пакета.



Как и в примере из главы 5, работающем с DNS, нам придется разбить пакет на части. Обычно такая процедура требует обращения к спецификации (RFC) и создания нужных шаблонов unpack(). Тим Поттер (Tim Potter) проделал сложную работу и написал несколько модулей Net Packet: NetPacket::Ethernet, NetPacket::IP, NetPacket::TCP, NetPacket::ICMP и т. д. Каждый из них поддерживает два метода: strip() и decode().

Метод strip() просто возвращает данные из пакета, выкидывая все, что касается уровня сети. Запомните, что TCP/IP-пакет в сети Ethernet - это, на самом деле, обычный пакет TCP, «обернутый» в пакет IP, а тот, в свою очередь, обернут в пакет Ethernet. Так что если $pkt хранит TCP/IP-пакет, то NetPacket:: Ethernet: :strip($pkt) вернет IP-пакет (удалив уровень Ethernet). Если бы нам нужна была TCP-часть от $pkt, можно было бы использовать NetPacket: :IP: : st r ip( Net Packet: : Ethernet: : stnp($packet)) для удаления и 1Р-, и Ethernet-уровня.

decode() продвигается глубже еще на один шаг. Он разбивает пакет на его составляющие и возвращает экземпляр объекта, содержащего все эти части. Например:

NetPacket::TCP->decode(

NetPacket: :IP: : strip( Net Packet: ; Ethernet: :st>-ip($packet)))

вернет экземпляр объекта со следующими полями:




Поле



Описание

src_port

TCP-порт источника

dest_port

TCP-порт приемника

Seqnum

Порядковый номер

Acknum

Номер подтверждения

Hien

Длина заголовка

Reserved

6-битное «зарезервированное» пространство в TCP-заголовке

Flags

Флаги URG, АСК, PSH, RST, SYN и FIN


Winsize

Размер TCP-окна

Cksum

Контрольная сумма

Urg

Указатель на экстренные данные


Options

Любые TCP-параметры в двоичном виде


Data

Данные для пакета
Это уже должно быть знакомо читателю (рис. 10.2). Чтобы выяснить порт приемника для пакета, можно сделать следующее:

$pt = NetPacket::TCP->decode( NetPacket: :IP: :stnp(

NetPacket::Ethernet::strip($packet )))->{dest_port};

Теперь соберем все вместе и кое-что изменим. Поттер создал оболочку для инициализации и циклов Net: : Рсар и выпустил ее как модуль Net: : PcapUtils. Модуль обрабатывает некоторые из выполняемых нами шагов, делая наши программы короче. Продемонстрируем все это л действии, учитывая все, что мы узнали в последнем разделе:



use Net::PcapUtils;

use NetPaeket::Ethernet; use NetPacket::IP;

use Net::Ping;

# локальная сеть $localnet = "192.168.1";

# фильтр для поиска SYN- пакетов не из локальной сети $prog = "tcp[13] = 2 and src net not Slocalnet";

$| =1: tt снимаем буферизацию с STDIO

sub grab_ip_and_pmg{

my ($arg,$hdr,$pkt) = @_ ;

ft получаем IP-адрес источника $src in = lietPacket: : IP->(Jecoae(

print "$src_]p is ". (($p-'>ping($src_ip)} 9

"alive" : "unreachable"). "\,i" unless $cache{$src_ip}++; }

Теперь, когда мы достигли своей цели и написали целиком на Perl (хоть и с помощью некоторых модулей, являющихся Perl-оболочками для программ на С) программу, позвольте мне рассказать, чем закончилась эта история.

В воскресенье утром центральная группа поддержки из другого отдела нашла ошибку в настройках маршрутизатора. Студент в одном из общежитий установил на свою машину Linux и неверно настроил демон маршрутизации сети. Эта машина посылала широковещательные сообщения в университетскую сеть о том, что она является маршрутом по умолчанию. Неправильно настроенный маршрутизатор, обслуживающий наш отдел, услышав это сообщение, безропотно изменил таблицу маршрутизации и добавил второй маршрут во внешний мир. Пакеты будут приходить из внешнего мира, и этот маршрутизатор, честно выполняя свои обязанности, поровну разделит ответы между двумя маршрутами. Такое распределение, когда «один пакет посылается настоящему маршрутизатору, другой посылается на машину студента, один - настоящему маршрутизатору, другой - на машину студента», создало асимметричную ситуацию маршрутизации. Когда фиктивный маршрут был удален и были задействованы фильтры, предотвращающие появление такой ситуации в дальнейшем, наша жизнь вернулась к нормальному ритму. Я не буду говорить, что стало со студентом, вызвавшим эту проблему.

В этом разделе вы познакомились с применением модулей Net: :Р. Net: : PcapUtils и семейства модулей NetPacket: : * для диагностики. Не останавливайтесь на этом! Эти модули позволяют написать множество программ, способных помочь разобраться с проблемами в сети или активно наблюдать за сетью в поисках опасности.






Содержание раздела