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

Отслеживание операций с файлами и сетью


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

Мы сосредоточимся на довольно узком круге вопросов и будем обращать внимание только на операции с файлами и сетью, которые выполняют другие пользователи. Кроме того, мы будем обращать внимание только на те операции, владельца которых можно отследить (другими словами, на конкретные процессы, запущенные конкретными пользователями). Что ж, учитывая это, двинемся дальше.

Отслеживание операций в Windows NT/2000

Попытка найти файлы, открытые другими пользователями, вернее всего сработает, если применять программу, работающую в командной строке, - nthandle Марка Руссиновича (Mark Russinovich), ее можно найти на

http://www.sysinternals.com. Она позволяет показать все открытые дескрипторы на определенной системе. Вот как выглядит ее вывод:

System pid: 2

10: File C:\WINNT\SYSTEM32\CONFIG\SECURITY

84: File C:\WINNT\SYSTEM32\CONFIG\SAM.LOG

cc: File C:\WINNT\SYSTEM32\CONFIG\SYSrEM

dO: File C:\WINNT\SYSTEM32\CONFIG\SECURITY.LOG

d4: File C:\WINNT\SYSTEM32\CONFIG\DEFAULT

e8: File C:\WINNT\SYSTEM32\CONFIG\SYSTEM.ALT

fc: File C:\WINNT\SYSTEM32\CONFIG4SOFTWARE.LOG



118: File C:\WINNT\SYSTEM32\CONFIG\SAM

128: File C:\pagefile.sys

134: File C:\WINNT\SYSTEM32\CONFIG\DEFAULT. LOG

154: File С:\WNNT\3YSTEM32\CON'FIG;'SOFTWARE

1bO: File \3evice\NafiedPipe\

294: File C:\WINNT\PROFILES\Adnirustrator\ntLSer.aa-.; OG

2a4: File C:\WINNT\PROFILES\AdminisTrator\NTUSEH.DAT

SMSS.EXE pid: 27 (NT AUTHORITY:SYSTEM)

4: Section С:\WINNT\SYSTEM32\5MSS.EXE

c: File С'\WINNT

28: File C:\WINNT\SYSTEM

Можно также запросить информацию по конкретным файлам и каталогам:

> nthandle c:\temp

Handle V1.11

Copyright (С) 1997 Mark Russinovich




http://www.sysinternals.com

WINWORD.EXE pid: 652

C:\TEMP\~DFF2B3.tmp WINWORD.EXE pid: 652

C:\TEMP\~DFA773.tmp WINWORD.EXE pid: 652

C:\TEMP\~DF9l3E.tmp

Программа nthandle позволяет получить эту информацию по конкретному процессу при помощи ключа -р.

Использовать ее из Perl очень просто, поэтому не будем приводить примеры. Вместо этого посмотрим на подобную, но более интересную операцию - аудит.

NT/2000 позволяет эффективно отслеживать изменения в файлах, каталогах или иерархии каталогов. Вы могли бы учитывать постоянное повторение операции stat() над нужным объектом, но это потребовало бы слишком больших затрат процессорного времени. В NT/2000 отслеживание изменений можно поручить операционной системе.

Относительно безболезненно эту работу выполняют два модуля: Win32: :ChangeNotify Кристофера Мадсена (Christopher J. Madsen) и Win32: :AdvNotify Амина Мюлей Рамдана (Amine Moulay Ramdane). В примерах этого раздела будет использоваться второй, т. к. он более гибкий.

Работа с модулем Win32: : AdvNotify- это многошаговый процесс.

На следующем шаге нужно создать следящий поток (monitoring thread) для интересующего нас каталога. Win32: :AdvNotify позволяет следить сразу за набором каталогов, для этого необходимо лишь создать несколько потоков. Мы же будем следить только за одним каталогом:

Sthread = $aob]->StartThread(Directory => 'C:\terr.c'.

Filter => All, WatchSubtree -> 0) or die "Невозможно начать поток\п":

Первый параметр этого метода говорит сам за себя; рассмотрим остальные.

Установив Filter в одно из приведенных значений (табл. 4.1) или в их

комбинацию (SETTING 1 | SETTING2 | SETTINGS. ..), можно следить за различными типами изменений.

Таблица 4.1. Параметры Filter в Win32::AdvNotifу




Параметр



Отмечает

FILE_NAME

Создание, удаление, переименование файла(ов)

DIR_NAME

Создание или удаление каталога(ов)


ATTRIBUTES

Изменение атрибутов любого каталога


SIZE

Изменение размера любого файла


LAST_ WRITE

Изменение даты модификации файла(ов)


CREATION

Изменение даты создания файла(ов)


SECURITY

Изменение информации безопасности (ACL и пр.) файла(ов)
<


Значение АИ из приведенного примера- это всего лишь постоянная, объединяющая все варианты выбора. Если не указать параметр Filter при вызове метода, то по умолчанию будет использоваться АИ. Параметр WatchSubtree определяет, необходимо ли следить только за указанным каталогом или за каталогом и всеми его подкаталогами.

StartThreadO создает поток, но проверка начинается только после того, как поступает распоряжение об этом:

$thread->EnableWatch() or die

"Невозможно начать наблюдение\п";

Существует также функция OisableWatch(), которую необходимо использовать в программе для завершения проверки.

Мы следим за нужным объектом, но как узнать, изменилось ли что-нибудь? Надо придумать что-то, что позволило бы потоку сообщить нам об изменениях, за которыми мы наблюдаем. Здесь тот же подход, что и в главе 9 «Журналы» при обсуждении сетевых сокетов. Обычно следует вызывать функции, которые заблокированы до тех пор, пока ничего не происходит:

while($thread->Wait(INFINITE)){

print "Что-то изменилось1\п":

last if ($changes++ == 5):

}

Этот цикл while() вызовет метод Wait() для нашего потока. До тех пор пока потоку нечего сообщить, вызов будет заблокирован. Обычно Wair() принимает в качестве параметра число миллисекунд, равное времени ожидания. Мы же передаем специальное значение, которое соответствует «бесконечному ожиданию». Когда Wait() возвращает значение, следует вывести сообщение и ждать дальше, если только уже не были замечены пять других изменений. Теперь можно закончить:

$thread->Terminnte() undef $aobj;

Наша программа пока еще не очень полезна. Нам известно, что что-то изменилось, но мы не знаем ни что изменилось, ни как это произошло. Чтобы исправить эту ситуацию, изменим тело цикла wniie() и добавим определение формата:

while($thread->Wait(INFINITE)){

while ($thread->Read(\@status)){ foreach Sevent (@status){

Sfilename = $event->{FileName); $time = $event->{DateTime};

Saction = $ActionName{$event->{Action}}; write; } } }



format STDOUT =

@««««««««« @««««««««« @«««««««««

Sfilename,$time,Saction

format STDOUT_TOP =

File Name Date Action

Основное изменение- это добавление метода Read(). Он получает информацию об изменении и заполняет элементы списка ©status ссылками на хэш. Каждая ссылка указывает на анонимный хэш, который выглядит примерно так:

{'FileName' => "GLF2425.TMP',

'DateTime' => '11/08/1999 06:23:25р',

'Directory' => 'C:\temp', 'Action' => 3 }

Для каждого изменения могут произойти несколько событий, отсюда и необходимость вызывать Read() в цикле while(). Если соответствующим образом разыменовать содержимое этих хэшей и применить к ним форматирование, то получатся примерно такие данные:

File Name Date Acti01

"DF400E.tmp 11/08/1999 07:29:56p

FILE_.ACTION_PEMO\,'F.D

"DF6C5C. fnp 11/08/1999 07'29'56p

F1LF_AC1ION_ADDED

~OF6E66.tmp 11/08/1999 07:29:56р

FILE_ACTION_ADOED

~DF6E5C.tmp 11/08/1999 07:29:56р

FILE_ACriON_REMOVEO

К сожалению, отслеживание операций с сетью в NT/2000 впечатляет намного меньше. В идеале, как администратор вы хотели бы знать, какой процесс (а, следовательно, какой пользователь) открыл сетевой порт. Печально, но я не знаю ни одного модуля и ни одного инструмента, которые могли бы предоставить такую информацию. Существует один коммерческий инструмент, работающий в командной строке, под названием TCPVstat, который может показать связь процессов с использованием сети. TCPVstat можно найти в пакете TCPView Professional Edition, который доступен на http://www.winternals.com.

Если использовать только некоммерческие инструменты, то придется иметь дело лишь со списком сетевых портов, открытых в настоящее время. Для этого следует применять другой модуль Рамдана -Win32: :1рНе1р. Вот как выглядит код, печатающий нужную информацию:

use Win32: -.IpHelp;

# замечание: в данном случае регистр "IpHelp"

имеет значение' my $iobj = new Win32::IpHelp;

# заполняем список хэшем хэшей $iobj->GetTcpTable(\(a>table,1);

foreach Sentry (@table){



print $entry->{LocalIP}-><Value( . ":" .

$entry->{LocalPort}->{ValLie}. " -> ";

print $entry->{fiemoteIP}->{Value} . ":" .

Sentry->{RemotePort}->{Value}."\n";

}

Посмотрим, как можно сделать то же самое в Unix.



Отслеживание операций в Unix



Для отслеживания операций с файлами и сетью в Unix можно использовать один и тот же подход. Это один из тех редких случаев, когда вызов внешней программы намного предпочтительней. Вик Абель (Vic Abell) преподнес чудесный подарок системным администраторам, написав программу Isof (LiSt Open Files), которую можно найти на ftp:// vic.cc.purdue.edu/pub/tools/unix/lsof. Isof

позволяет отобразить подробную информацию об открытых в настоящий момент файлах и сетевых соединениях на Unix-машине. По-настоящему удивительной эту

программу делает ее переносимость. Последняя версия программы (на момент написания этой книги) работает по крайней мере на 18 видах Unix и поддерживает различные версии этих операционных систем.

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

COMMAND PID USER FO TYPE DEVICE SIZE/OFF NODE NAME

netscape 21065 dno cwd VOIR 172,289"; 8192 12129 /-OTie

netscape 21065 dnb txt VREG 172,1246 1438236Д 656749

/net/ arch-solans (fileserver-./vol/systems/arch-solaris)

netscape 21065 dnb txt VREG 32,6 54656 35172

/usr (,/dev/ dsk/cOtOdOs6)

netscape 21065 dnb txt VREG 32;6 146740 6321

/ubr/lro/ libelf.so.1

netscape 21065 dnb txt VREG 32,6 69292 102611

/usr (/dev/ dsk/cOtOdOs6)

netscape 21065 dnb txt VREG 32,6 21376 79751

/usr/iib/ locale/en_US/en_US.so.1

netscape 21065 dnb txt VREG 32,6 19304 5804

/usrЛib/ libmp.so.2

netscape 21065 dnb txt VREG 32,6 98284 22860

usr/onenwi:' lib/libICE.so.6

netscape 21065 dnb txt VREG 32,6 46576 22891

/usr/opftrwiv lib/libSM.so.6

netscape 21065 drib txt VREG 32.6 1014020 5810



/!jsr/::.t; libc.so.1

netscape 21065 dnb txt VREG 32.6 105788 5849

/usr/ln; libm.so.1

netscape 21065 dnb txt VREG 32,6 721924 5806

netscape 21065 ar.b txt VREG 32 6 156196 5774

netscape 21065 ri^t 0. VCHP 2-1.3 Ot73 5853 > .

pseudo/bls@C:3-> ttcoirpat ->lcter"b>:neii->pis netscape 21065 dnb 3u VCHR 13,12 oto 5821

/devices/ pseudo/mm@0:zero

netscape 21065 dnb 7u FIFO Ox6034d264 C;1 47151 PIPE-> Ox6034d1eO

netscape 21065 dnb 8u met Ox6084cb68 Oxfb210er, TCP host. cos.

ne^.. edu:46575->host2.ccs,neu. edu:6000 (ESTABLISHED)

netscape 21065 dnb 29u met 0x60642848 Ot215868 TCP nost, ccs. re.. edu:46758->

www.mind-bright.se:80 (CLOSE_ WAIT)

Из этого примера можно понять, насколько мощна эта команда. Мы можем увидеть текущий рабочий каталог (VDIR), обычные файлы (VREG), символьные устройства (VCHR), каналы (FIFO) и сетевые соединения (inet), открытые этим процессом.

Самый простой способ применить программу Isof из Perl - вызвать ее в специальном режиме «field» (-F). В этом режиме вывод программы делится на специальным образом отмеченные и разделенные поля, вместо использования колонок в стиле ps. Это позволяет надежно проанализировать и распознать вывод.

У этого способа вывода результатов есть одна особенность. Вывод организован в виде «наборов процессов» (process sets) и «наборов файлов» (file sets), как их называет автор. Набор процессов - это набор полей, относящихся к одному процессу; набор файлов - это подобный же набор для файла. Все приобретет больший смысл, если включить режим разбивки на поля с параметром 0. В этом случае поля будут разделены символом NUL (ASCII 0), а наборы - символом NL (ASCII 12). Вот как будет выглядеть предыдущий вариант вывода команды, если использовать режим разбивки на поля (NUL представлен в виде символов ~@):

p21065~@cnetscape~@u6700"@Ldnb~@

fcwd"@a ~@1 "@tVDIR"@DOx2bOOb4b"@s8192"@i12129"@n/home/dnb"@

ftxt"@a ~@1 >tVREG"@DOx2b004de"@s14382364"@i656749"@n/net/arch-solaris (fileserver:/vol/systems/arch-solaris)"@



ftxt~@a "@1 ~@tVREG"(5)DOx800006~@s54656~@i35172~@n/usr (/dev/dsk/cOtOdOs6)"@ ftxt"@a "@1 "@tVREG"@OOx800006"@s146740"@i6321"@n/usr/lib/libelf.so.1"@ ftxt"@a "@1 "@tVREG"@DOx800006"@s40184"@i6089"ian/usr (/dev/dsk/cOtOdOs6)"@ ftxt"(5>a "@1 "@tVREG"@DOx800006"@s69292"@i102611"@n/usr (/dev/dsk/cOtOdOs6)"@

ftxt"@a ~@1 •@tVREG"@DOx800006"@s21376"@i79751"@n/usr/lib/locale/en_US/ en_US.so.1"@

ftxt"@a ~@1 "@tVREG"№Ox800006"@s19304"@i5804"@n/usr/lib/libmp. so. 2"@

ftxt"@a "@1 ~@tVREG"@DOx800006"@s98284"@i22860"@n/L>sr/openwin/lib/ libICE.so.6"@ ftxt~@a ~@1 "@tVREG"@DOx800006"@s46576"@i22891"@n/usr/openwin/lib/ libSM.so.6"<°>

ftxt"@a ~@1 "@tVREG"@DOx800006"@s1014020"@i5810"@n/usr/lib/libc.so.1"@ ftxt~@a ~@1 ~@tVREG~№Ox800006~@s105788~@i5849~<a>n/usr/lib/libm. so. 1"@ ftxt"@a "@1 "§tVREG"@DOx800006'@s721924"@i5806"@n/usr/lib/libnsl.so.Г@

ftxt"@a "@1 "@tVREG"@DOx800006"§s166196"@i5774"@n/usr/lib/ld.so.Г@ fO"@au~@l "@tVCHR"@DOx600003"iao73~(ai5863"(an/devices/pseudo/ pts@0:3->ttcompat->ldterm->ptem->pts"@

f3"@au"@l "@tVCHR"@DOx34000c"@oO"§i5821"@n/devices/pseudo/mm(s)0:zero"(8i f7"@au"@l "@tFIFO"@dOx6034d264"@o1"@i47151"@nPIPE->Ox6034d1eO"@

f8"@au"@l "@tinet"@dOx6084cb68"@o270380692"(9>PTCP''@nhost.ccs.neu.edu:46575-> host2.cos.neu.edu: 6000"@TST=ESTABLISHED~@

f29"@au"@l "@tinet"@dOx60642848"@o215868~@PTCP"@nhost.ccs.neu.edu:46758-> www.mindbright.se: 80"(a>TST=CLOSE_WAIT~@

Давайте разберемся с этими данными. Первая строка - это набор процессов (это можно понять по первому символу р):

p21065~(acnetscape~@u6700~@Ldnb~(a



Каждое поле начинается с буквы, определяющей его содержимое (о -идентификатор процесса (pid), с - команда, и - идентификатор пользователя (uid) и L - имя пользователя (login)), и заканчивается символом разделителя. Все поля в строке создают набор процесса. Все последующие строки вплоть до очередного набора процесса описывают открытые файлы/сетевые соединения процесса, описываемого своим набором.

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

use Text::Wrap;

Slsofexec = "/usr/local/Din/lsof"; 8 путь к Isof

8 режим (F)ield, разделитель NUL (0), показывать (L)ogin,

8 тип файла (t)ype и имя файла (n)ame

$lsofflag = "-FLOtn";

open(LSOF,"$lsofexec $lsofflag[") or

die "Невозможно запустить $lsofexec:$!\n";

while(<LSOF>){

8 работаем с набором процесса if (substr($_,0,1) eq "p"){

($pid,$login) = split(/\0/);

$pid = substr($pid,1,length(Spid)); }

# работаем с набором файла.

# Замечание: мы интересуемся только обычными файлами

if (substr($_,0,5) eq "tVREG"){

($type,$pathname) = split(/\0/);

# процесс может дважды открыть один и тот же файл,

# поэтому мы должны убедиться, что запишем его

# только один раз

next if ($seen{$pathname} eq $pid); $seen{$pathname} = $pid;

Spathname = substr($pathname,1,length($pathname));

push(@{$paths{$pathname}},$pid);

}

close(LSOF);

for (sort keys %paths){

print "$_:\n";

print wrap("\t","\t",join(" ",@{$paths{$_}})),"\n";

}

В этом случае Isof будет показывать только некоторые из полей. Можно обойти в цикле весь вывод, собирая имена файлов и идентификаторы процессов в хэш списков. Когда будут обработаны все выведенные данные, следует ввести имена файлов в виде отформатированного списка процессов (спасибо Дэвиду Шарноффу (David Muir Sharnof f) за модуль Text: :Wrap):

/usr (/dev/dsk/cOtOdOs6):

115 117 128 145 150 152 167 171 184 191 200 222 232 238



247 251 276 285 286 292 293 296 297 298 4244 4709 4991

4993 14697 20946 21065 24530 25080 27266 27603

/usr/bin/tcsh:

4246 4249 5159 14699 20949

/usr/bin/zsh:

24532 25082 27292 27564

/usr/dt/lib/libXm.so.S: 21065 21080

/usr/lib/ld.so.1:

115 117 128 145 150 152 167 171 184 191 200 222 232 238

247 251 267 276 285 286 292 293 296 297 298 4244 4246 4249 4709 4991

4993 5159 14697 14699 20946 20949 21065

21080 24530 24532 25080 25082 25947 27266 27273 27291

27292 27306 27307 27308 27563 27564 27603

/usr/lib/libc.so.1:

267 4244 4246 4249 4991 4993 5159 14697 14699 20949

21065 21080 24530 24532 25080 25082 25947 27273 27291

27292 27306 27307 27308 27563 27564 27603

Чтобы показать последний код, относящийся к отслеживанию операций с файлами и сетью в Unix, вернемся к поиску запущенных IRC-po-ботов из приведенного ранее примера. Существует более надежный способ найти такие процессы-демоны, чем изучение таблицы процессов. Пользователь может скрыть имя робота, переименовав исполняемый файл, но для того чтобы спрятать сетевое соединение, ему придется очень хорошо потрудиться. В большинстве случаев это соединение с сервером на портах 6660-7000. Программа Isof позволяет без труда отыскивать такие процессы:

Slsofexec = "/usr/local/bin/lsof" Slsofflag = "-FLOc -iTCP: 6660-7000";

ft это срез хэша. используемый для предварительной загрузки

и таблицы хэшей, существование этих ключей мы будем проверять

и позже. Обычно это записывается так:

ft %approvedclients = ("ircll" => undef, "xirc" => undef, ...);

ft (но такой вариант - отличная идея, воплощенная Марком-

ft Джейсоном Доминусом(МагК-иа50п Dominus))

@>approvedclients{"ircH" , "xirc" , "pirc"} = ();

open(LSOF, "$lsofexec $lsofflag|") or die "Невозможно запустить $lsofexec:$! \n" ;

while(<LSOF>){

($pid,$command,$login) = /p(\d+)\000

c(.+)\000 L(\w+)\000/x;

warn "$login использует неразрешенный клиент с именем



$conimand (pid $pid)l\n" unless (exisTs $approvedclients{$command});

close(LSOF);

Это самая простая проверка из всех возможных. Она позволяет отловить тех, кто догадается переименовать eggdrop б pine или -tcsh, и тем более тех, кто даже не попытается спрятать своего робота. Тем не менее, она подвержена тому же недостатку, что и предыдущий вариант тестирования. Если пользователь достаточно умен, он может переименовать робота во что-то из списка «разрешенных клиентов». Чтобы продолжить игру в кошки-мышки, можно предпринять еще как минимум два шага:

  • Запустить Isof для проверки того, что файл, открытый для данной исполняемой программы, известен как тот, который мог быть открыт этой программой, а не произвольный файл из файловой системы.


  • Применить методы управления процессами для проверки того, что пользователь запускает программу из существующего интерпретатора. Если это единственный процесс, запущенный пользователем (т. е. если пользователь оставил его, а сам завершил работу), он, вероятно, является демоном, а значит и роботом.


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






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