№7(20) июль 2004 подписной индекс 81655 www.samag.ru
Почтовая система на базе MTA exim Обзор системы поддержки списков рассылок GNU Mailman Как различать и считать маркированный трафик? FreeBSD tips: ARP в «заморозке» Строим шлюз с Luinux CrossOver и лицензионный вопрос Упрощаем себе жизнь с Webmin PostgreSQL: первые шаги
№7(20) июль 2004
HA-кластер LifeKeeper компании SteelEye Русификация FreeDOS Metro Ethernet
оглавление Строим шлюз с Luinux
IMHO
Сергей Яремчук grinder@ua.fm
Есть такая профессия Алексей Коршунов akeeper@akeeper.ru
4 «Стальной глаз на страже жизни». HA-кластер LifeKeeper компании SteelEye Антон Борисов a.borisov@tesv.tmb.ru
АДМИНИСТРИРОВАНИЕ Почтовая система на базе MTA exim Всеволод Стахов cebka@jet.msk.su
6
14
20
Крис Касперски kk@sendmail.ru
62
СЕТИ 26 Metro Ethernet Денис Еланский grosm@samag.ru
84
34 ОБРАЗОВАНИЕ
38
Второе начало термодинамики – гарант успеха систем с открытым исходным кодом Алексей Мичурин alexey@office-a.mtu-net.ru
№7(20), июль 2004
59
Техника внедрения кода в РЕ-файлы и методы его удаления
FreeBSD tips: ARP в «заморозке» Сергей Супрунов amsand@rambler.ru
52
18 ПРОГРАММИРОВАНИЕ
Упрощаем себе жизнь с Webmin Сергей Яремчук grinder@ua.fm
Иван Коробко ikorobko@prosv.ru
Вадим Дружин vdruzhin@mail.ru
PostgreSQL: первые шаги Сергей Супрунов amsand@rambler.ru
50
Русификация FreeDOS
Как различать и считать маркированный трафик? Павел Закляков amdk7@mail.ru
Александр Шибенко pulse@hotmail.ru
Управление файловыми серверами
«Кто стучится в дверь ко мне…» Обзор системы поддержки списков рассылок GNU Mailman Андрей Маркелов andrew@markelov.net
43
Вежливый отказ
Настройка почтовой системы на базе Postfix + Dovecot + PostgreSQL + Amavisd-new + SpamAssassin + ClamAV Рустам Атнагулов mosqit@sc.rb.ru
40
91 1
конкурс
С 24 июня по 3 августа 2004 года на страницах популярного каталога программного обеспечения SOFT@Mail.Ru (http://soft.mail.ru) проводится конкурс «Сисадмин – тоже человек», посвященный Международному дню системного администратора (30 июля). Конкурс организован компанией SoftLine и интернет-холдингом Mail.Ru. Конкурс посвящен вам, уважаемые сисадмины, и вашему нелегкому труду, и не только нелегкому, но еще и нервному, а иногда и просто вредному. Для здоровья, конечно, а не для пользователей. Вознаграждать этот труд обычно некому: для пользователя сисадмин – работник «невидимого фронта», чем незаметней, тем лучше, да и сами сисадмины по природе обычно скромные и сдержанные. Работа обязывает. Сисадмин в России часто отвечает за работу, не имеющую прямого отношения к его профессиональным обязанностям. Более половины сисадминов официально не называются системными администраторами. Поэтому к участию в этом летнем творческом конкурсе приглашаются также веб-мастера, администраторы баз данных и служб электронной почты, системные программисты, администраторы телефонных сетей и систем голосовой почты и других служб. Конкурс «Сисадмин – тоже человек» будет способствовать мягкой психологической реабилитации работников системного администрирования: вам, уважаемые сисадмины, предлагается поделиться своими размышлениями о работе, пользователях, компьютерах и жизни в целом. Рассказав о трудностях (а может, и радостях) сисадминской жизни, изложив всю правду о «юзерах» и почитав сообщения ваших коллег, вы снимете напряжение, расслабитесь, выскажетесь и … улыбнетесь. Ваши оригинальные рассказы, жалобы, истории вдохновят пользователей на создание открыток в честь дня сисадмина. Создание открыток для сисадминов составит вторую часть конкурса под кодовым названием «Утешить сисадмина». Открытки разместятся на сайте Открытки@Mail.Ru (http://cards.mail.ru). С этого сайта все желающие смогут послать поздравления своим знакомым сисадминам. Каждая открытка будет создаваться автором в честь одного из сисадминов, оставивших свое сообщение в книге жалоб. Победитель среди сисадминов будет определяться по числу размещенных в его честь открыток. Чем раньше
№7(20), июль 2004
вы разместите свое сообщение, тем больше пользователей его прочитает и тем больше у вас шансов победить. Итоги конкурса будут подведены 3 августа. Системного администратора, чье сообщение будет пользоваться наибольшим вниманием пользователей и авторов открыток, ждет главный приз от генерального спонсора конкурса «Сисадмин – тоже человек» – Учебного Центра SoftLine (http://softline.ru/edu). Это программа подготовки Microsoft Certified Systems Administrator (MCSA 2003). Программа представляет собой трек продолжительностью 17 дней, состоящий из набора полезных курсов: ! управление и поддержка среды Windows Server 2003; ! установка, настройка и администрирование Windows XP Professional; ! внедрение сетевой инфраструктуры Windows Server 2003: сетевые узы; ! внедрение, управление и сопровождение сетевой инфраструктуры Windows Server 2003: сетевые службы; ! установка и управление Microsoft Internet Security and Acceleration Server 2000. Победитель может заменить входящие в трек MCSA 2003 курсы на аналогичные по стоимости и длительности курсы Microsoft. Сисадминов-победителей ждут фирменные призы от Mail.Ru, а также бесплатная подписка на второе полугодие 2004 года на журнал «Системный администратор» от редакции журнала. Авторы лучших открыток получат ценные призы от интернет-магазина Allsoft.ru (http://www.allsoft.ru) компании SoftLine, главный из которых – графический планшет Wacom. А 30 июля – в Международный день сисадмина – всех участников конкурса ждет приятный сюрприз. Официальная страничка конкурса в Интернете – http:// soft.mail.ru/competitions/sysadmin. По адресу info@soft.mail.ru вы можете задать любые вопросы, связанные с проведением конкурса, а также прислать ваши предложения и пожелания. С уважением организаторы конкурса – компании SoftLine и Mail.Ru.
3
IMHO
ЕСТЬ ТАКАЯ ПРОФЕССИЯ АЛЕКСЕЙ КОРШУНОВ В последнее время профессия системного администратора стала крайне популярной, особенно среди молодых людей. Практически в любой компании есть специалисты такого профиля. Кто эти люди, и какое у них как специалистов будущее? Так как официально профессии «системный администратор» не существует, количество её трактовок неимоверно велико, каких только не приходилось мне слышать. От ужасно вычурных до откровенно «забавных», но в одном сходятся все – это человек, настраивающий и следящий за работой «компьютерного хозяйства» компании. Будь то мощные сервера и сотни пользовательских станций или десяток пользовательских компьютеров и одиннадцатый в «качестве сервера». Из-за неразберихи с определением, а значит, и обязанностями системных администраторов, большинство бизнесменов и начальников слабо представляют, чем человек с такой специальностью должен заниматься. Очень часто приходится слышать, как сисадмина называют посвойски «компьютерщик». Я неспроста привел этот термин. Дело в том, что довольно давно, когда он появился, у людей в голове сложилась картина этакого «маньяка клавиатуры», который, грубо говоря, просто хорошо разбирается в компьютерах. Следовательно, этот человек может разобраться и в серверах, и в компьютерах пользователей и… может настроить мини-АТС, починить ксерокс, поменять бумагу в факсе. Ведь начальство действительно свято верит, что все это оборудование напрямую относится к «компьютерному хозяйству» компании, а значит, в ведении системного администратора. Другими словами, круг обязанностей системного администратора может оказаться весьма широк. Возросшая популярность профессии привела к понижению общего уровня зарплат и уровня востребованности системных администраторов. Я говорю о тех толпах студентов и просто безработных молодых людей, которые по какой-то причине вообразили, что они могут работать системными администраторами. Давайте посмотрим на ситуацию честно и непредвзято. Для большинства (относительно количественной, а не качественной массы) ныне существующих системных администраторов эта специальность есть способ заткнуть дыру своей незанятости как работающего человека. «Я нигде не работаю, но знаю компьютеры (это «знаю» заслуживает отдельного длинного обсуждения, ну да бог с ним) – значит, я могу работать системным администратором». Правильно? И да, и нет.
4
Любой специалист-сисадмин на пальцах разложит и докажет, что такое утверждение не верно в принципе. Но так ли это? Работа многих и многих системных администраторов заключается (разумеется, не всегда, но чаще всего) в следовании некоторым схемам. «Настроил, следишь за работой, избегаешь нештатных ситуаций, если таковые возникают, исправляешь последствия». По большому счету схему работы среднего системного администратора небольшой компании можно было бы описать в одной не самой толстой книжке. Даже места для картинок хватило бы. «А как же искусство?» – спросите вы. «Как же решение нестандартных проблем и задач?» Полностью с вами согласен – для решения действительно нетривиальных и сложных задач (проблем) нужен достаточный опыт и просто элементарное знание основ плюс голова на плечах. Но, господа хорошие, поймите, что работодателей устраивает «работа по схеме». Поэтому совсем не удивительно, что они предпочитают взять на работу студента-недоучку за маленькие деньги вместо матерого и опытного специалиста на весьма и весьма хорошие деньги. Более того, ситуация не стоит на месте, и многие уже пришли к тому, что можно и настоящего специалиста взять за копейки, дописав ему количество обязанностей, которые в принципе не способен выполнять один и тот же человек по причине отсутствия трех голов и восьми рук. Но кого это интересует? Реально – никого, кроме самих «обиженных» системных администраторов. Для работающих на данный момент специалистов назрела проблема – как быть дальше. Куда развиваться, где искать заработок нормального уровня. Причин возникновения данной ситуации существует немало, и если вдаваться в детали и тонкости, то получится как минимум хорошая курсовая для какого-нибудь студента. Давайте лучше подумаем, как выйти из этого положения. Первое, что приходит в голову, – это повышение профессионального уровня. Прохождение курсов и получе-
IMHO ние сертификатов помогают найти высокооплачиваемую работу по специальности. К слову сказать, некоторые сертификаты можно получить, не посещая курсы, а лишь совершенствуя свои познания в нужной области с последующей сдачей экзаменов. Настоящие системные администраторы-профессионалы не сидят на месте и не имеют возможности «зависать» на форумах и в чатах целыми днями. У них слишком мало времени и слишком много дел, за которые они и получают достойную оплату. Также возможен последующий переход на новые ступени. Известны случаи, когда администратор-профессионал перерастал в руководителя крупного IT-отдела большой компании или же вовсе начинал собственное дело. Но скажите честно, все ли вы действительно собираетесь всю жизнь работать системными администраторами, пусть даже в надежде стать начальниками IT-отдела? Не та это специальность, которая способствует постоянному профессиональному росту и радует душу будущими золотыми горами. (Исключаем из рассмотрения места в компаниях, где данное утверждение не верно по причине большого размера компании и широчайших возможностей карьерного роста – такая ситуация не настолько типична, как многим хотелось бы.) Если представить, что вы уже прошли тот период, когда вам «срочно нужны деньги на пиво» и предположить смещение приоритетов в более серьезную сторону, то представляется весьма неплохая картина для смены основной специализации. Работа у вас уже есть, нехватки свободного времени (как правило) нет, какие-никакие деньги компания ежемесячно вам «отслюнявит». Что мешает прекратить ныть о тяжести судьбы и начать менять ситуацию хотя бы относительного себя самого? Думаю, что только лень. Кем может быть человек, проработавший какое-то время системным администратором? Да кем угодно! Более того, у системных администраторов зачастую даже больше возможностей найти специальность «под себя», так как по воле работы им зачастую приходится поверхностно вникать в аспекты работы многих сотрудников. Примером смены основного профиля может послужить ситуация, когда люди в процессе работы понимали, что общая деятельность компании им как минимум интересна. Обычно впоследствии эти люди получали соответствующее их интересам образование (как вариант, если это возможно, прохождение нужных курсов) и, случалось, что меняли род деятельности на совершенно иной. Так, например, один мой знакомый, работавший в компании, занимающейся продажей мото- и автотехники, захотел быть руководителем отдела продаж (там и заработки выше, и работа много интересней, не говоря уже о различных перспективах «полезных знакомств»). Получил образование, прошел курсы менеджмента и сообщил своему начальству о желании работать в новом качестве. Разумеется, никто не назначил новоиспеченного менеджера руководителем уже состоявшегося отдела в тот же миг, и ему пришлось какое-то время работать, доказывая свою состоятельность как исполнителя, так и руководителя. Однако для нас гораздо важнее то, что спустя всего полгода
№7(20), июль 2004
(от момента «смены профиля») он добился желаемого результата и, насколько я знаю, весьма им (как результатом, так и профилем) доволен. Другой пример не связан с поиском места работы «вокруг себя», однако имеет прямое отношение к профессии системного администратора. Так как многие достаточно долго работающие в данной специальности люди имеют свои «рабочие» схемы и способы анализа как задач, так и различных внештатных ситуаций (другими словами, используют системный подход), не удивительно, что аналитическая часть ума у этих людей развита превосходно. Поэтому лично я ничуть не удивился, когда узнал, что два моих знакомых стали аналитиками в достаточно крупных компаниях. Разумеется, эта работа гораздо более интересна для них, так как не зациклена на чем-то одном (компьютеры, сервера и проч.), а охватывает несоизмеримо больший круг вопросов. Другими словами, примеров смены основного профиля масса. Зачастую они сделаны с учетом тех сильных сторон человека, которые были выявлены в процессе работы в должности системного администратора. Какая-то часть людей находит интересную для себя работу в том же офисе, где они обслуживали компьютерную сеть. Как правило, все вопросы, связанные с аналитическими, тактическими сторонами работы, достаточно легко даются человеку, работавшему системным администратором. Видимо, причина в типе мышления людей, выбирающих эту специальность. Так в чем же дело? Может быть, пора перестать делать большие круглые глазки, встречая очередное объявление о поиске «человека-комбайна» и вплотную заняться своим будущим? Я понимаю, что возможность целыми днями фактически ничего не делать, а только сидеть на форумах и в чатах, для многих притягательна, но пора задуматься о том, чем это обернется в будущем. Оглянитесь вокруг, может быть, «ваша» специальность скрывается за соседним столом менеджера по работе с корпоративными клиентами. Мне приходилось слышать уверения вроде «я больше ничего делать не умею». Думаю, нетрудно вспомнить, что когда-то вы и UNIX не смогли бы установить. Или вы родились, зная, как работать в шелле? Сомневаюсь. Специальность системного администрирования нельзя сравнивать со сферой коммунальных услуг, все-таки она требует некоторого интеллектуального труда, а не слепого следования инструкциям. Но и рассматривать ее как постоянную специальность для состоявшегося человека, мне кажется, не совсем правильно, так как эта профессия не может дать достойного развития и карьерного роста (повторюсь – за исключением редких случаев, рассматривать которые я не берусь особенно в свете глобальной популяризации этой специальности) для действительно умного сотрудника. Начальство зачастую и не подозревает, какие ценные кадры скрыты в их компании до тех пор, пока вы сами это им не покажете. Может быть, уже пришло время активных действий, чтобы не было мучительно больно за впустую потраченные годы лет так через десять?
5
администрирование
ПОЧТОВАЯ СИСТЕМА НА БАЗЕ MTA EXIM
ВСЕВОЛОД СТАХОВ 6
администрирование В этой статье пойдет речь о создании эффективного почтового сервера на базе MTA exim. Первый вопрос, который, конечно же, приходит в голову – «Почему именно exim?». Отвечают на этот вопрос по-разному, поэтому я скажу, что меня так привлекает в exim: ! логичная схема обработки почты; ! высокая скорость работы; ! удобный формат конфигурационного файла; ! широчайшие возможности по поиску каких-либо значений в файлах, СУБД, LDAP; ! встроенная поддержка smtp-аутентификации; ! небольшое число найденных уязвимостей (фактически я знаю только об одной, найденной недавно, она касалась версий exim до 4.20 включительно); ! очень большое количество возможностей, а также чрезвычайная гибкость; ! возможность полной замены sendmail (т.е. можно сделать): ln -sf /usr/local/sbin/exim /usr/libexec/sendmail
На мой взгляд, exim является весьма и весьма удачным продуктом, не зря он используется по умолчанию в ОС Debian GNU Linux. Два больших минуса exim состоят в том, что отсутствует качественная документация на русском языке (этот недостаток я, возможно, постараюсь в скором времени исправить написанием книги, посвященной целиком и полностью этому замечательному MTA) и необходимость правки Makefile для включения тех или иных возможностей exim. Итак, для начала подумаем, что должна содержать «идеальная» с точки зрения удобства администрирования и использования почтовая система. Сформулируем ряд требований к почтовой системе: ! простота управления пользователями; ! возможность предоставления доступа для отправки почты пользователям локальной сети и мобильным пользователям (при помощи smtp-аутентификации); ! максимальная защита от хакерских атак, вирусов и спама. Базовой системой для установки MTA явилась FreeBSD 5.2.1, что обусловило определенные особенности установки. Перечислю весь набор программного обеспечения для организации mail-сервера: ! exim-4.34; ! MTA (mail transfer agent – агент передачи почты); ! courier-3.0.4 – imap-сервер для доступа к почте пользователей, не имеющих локальных пользовательских учетных записей (виртуальные пользователи); ! ClamAV – для поиска вирусов; ! SpamAssasin – для поиска спамерских писем; ! PostgreSQL-7.4.2 – для хранения всех данных о пользователях почтовой системы; ! SquirrelMail – просто приятный веб-интерфейс для почты (требует веб-сервер и php). Конечно, можно собрать все вышеперечисленные компоненты и вручную, но я не поклонник такого подхода. Во-
№7(20), июль 2004
первых, надо очень четко представлять порядок сборки различных компонентов. Во-вторых, необходимо разбираться в куче опций для связки различных библиотек. Втретьих, надо четко представлять себе все пути и знать, каких пользователей надо добавлять. Ну и в-четвертых, это дело очень сложно обновлять. Потому мы воспользуемся услугами системы портов (если у вас, например, Debian GNU Linux, то нужный пакет называется exim4daemon-heavy). Для этого ставим следующие порты: databases/pogstgresql7 mail/exim
при компиляции необходимо указать следующие опции: make WITH_PGSQL=yo mail/p5-Mail-SpamAssassin security/clamav mail/courier-imap
при компиляции опять же указываем: make WITH_CRAM=yo WITH_POSTGRESQL=yo mail/squirrelmail
После сборки всего нужного переходим к стадии настройки. Тут придется затратить порядочное количество времени на настройку всех компонентов почтовой системы, и целью этой статьи является минимизация этого времени. Итак, начнем с настройки СУБД PostgreSQL, как основы для построения почтовой системы. Во-первых, Postgre SQL работает с правами системного пользователя pgsql (обратите внимание, этот пользователь имеет реальный shell и домашний каталог – /usr/local/pgsql). Поэтому для начала задаем пароль для данного пользователя: # passwd pgsql
Далее делаем su pgsql и начинаем создание базы данных: # su pgsql % psql
Вводим пароль пользователя pgsql и попадаем в командную строку SQL-запросов. Для подробного ознакомления с возможностями СУБД советую обратиться к руководству или же одной из книг. Создаем БД: CREATE DATABASE users;
Присоединяемся к данной БД: \c users
Создаем таблицу пользовательских аккаунтов, а также constraint для нее: CREATE TABLE accounts ( uid serial NOT NULL, login character varying(128), "password" character varying(128), maildir character varying(255), gecos character varying(255),
7
администрирование gid integer DEFAULT 150, home character varying(255), mailquota integer DEFAULT 20
); ALTER TABLE ONLY accounts ADD CONSTRAINT uid_k PRIMARY KEY (uid); ALTER TABLE ONLY accounts ADD CONSTRAINT login_k UNIQUE (login);
Создаем таблицу алиасов: CREATE TABLE aliases ( mail character varying(128) NOT NULL, alias character varying(128) ); ALTER TABLE ONLY aliases ADD CONSTRAINT mail_k PRIMARY KEY (mail);
Поясню назначение таблиц и полей:
! таблица accounts предназначена для хранения данных
!
о виртуальных пользователях почтовой системы, назначение полей: ! uid – уникальный номер пользователя, имеет автоинкрементный тип; ! login – строка, содержащая имя пользователя в формате «имя@домен» для возможности доставки почты для пользователей различных доменов; ! password – пароль в открытом виде (для возможности безопасной cram-md5 аутентификации); ! maildir – путь к почтовому ящику в формате maildir gecos – комментарий (для чего-нибудь да пригодится); ! gid – идентификатор группы (не нужен реально, но зачем-то требуется для courier); ! home – аналогично gid; ! mailquota – число в мегабайтах, указывающее квоту для пользователя; таблица aliases представляет собой просто замену /etc/ aliases, содержит два поля – mail (исходный адрес) и alias (адрес для перенаправления).
После этого будем считать, что PostgreSQL у нас работает. Однако для полного использования возможностей этой СУБД лучше почитать документацию. Далее перейдем к настройке собственно MTA exim. Конфигурационный файл для версии «из портов» хранится в /usr/local/etc/exim/configure. Для начала я бы хотел пояснить значение некоторых терминов и рассказать о базовых принципах работы exim. Представим себе путь прохождения любого почтового сообщения. Любое письмо состоит из так называемого конверта и собственно данных. То, с чем мы привыкли работать в почтовых клиентах, как раз является «данными» и не имеет к конверту никакого отношения. В конверте описываются 2 параметра: mail from: и rcpt to:, которые указывают отправителя и получателей соответственно. Сеанс работы с MTA может предваряться «приветствием» – стандартным (HELO) и расширенным (EHLO). В приветствии должно указываться FQDN клиента, хотя по ряду причин не стоит слишком строго следить за именно FQDN, т.к. многие машины могут находиться за NAT. MTA в ответ на HELO или EHLO сообщает о своих возможностях, обеспечивая тем самым синхронизацию клиента и сервера. Далее exim осуществляет проверку целевых адресов на пред-
8
мет их допустимости для данного сервера. Этот этап может предваряться аутентификацией и установкой TLS-сессии. Если пользователь прошел какой-либо из этих этапов, ему присваиваются определенные флаги, которые в дальнейшем могут использоваться на этапе проверки конверта. По умолчанию exim осуществляет проверку недопустимых символов в адресах и позволяет отправлять почту только из локальных доменов и только на локальные домены. Кроме того, при успешной аутентификации письмо проходит и в Интернет. Для остальных писем релей (передача писем) запрещена. Все проверки в exim4 осуществляются на основании механизма acl (access control list – список доступа). Также может осуществляться проверка данных (этот механизм добавляется при установке патча exiscan, который при установке портов уже ставится). После успешного прохождения этого этапа exim просматривает rewriteправила для переписывания отдельных частей конверта (может использоваться в целях перенаправления всех писем на какой-либо смарт-хост, например). Далее письмо попадает на цепочку так называемых роутеров, которые определяют, как именно доставлять письмо. Среди роутеров фигурирует dnslookup, доставляющий письма на основании MX-записей в DNS, проверка файла алиасов и .forward-файлов, ну и локальная доставка. Каждый роутер имеет некое условие срабатывания и соответствующий транспорт. При выполнении условия exim выбирает указанный транспорт для доставки письма, иначе письмо проходит на следующий роутер (поэтому важен порядок описания роутеров в конфигурационном файле). Транспорты же определяют порядок доставки письма. Таким образом, настройка exim состоит из нескольких частей: ! настройка глобальных параметров; ! настройка acl’s; ! настройка роутеров; ! настройка транспортов; ! настройка аутентификаторов; ! настройка очереди; ! настройка rewrite-правил. В настройке exim широко используются различные списки (hostlist, domainlist и dnslist) и так называемые lookup, при помощи которых exim извлекает данные из внешних источников. Многие мои знакомые, пытаясь освоить exim, считали lookup самой запутанной темой, потому я остановлюсь на этом вопросе чуть подробнее. Существует два типа поиска: поиск по одному ключу (single-key) и поиск по запросу (query). Первый используется для поиска в локальных файлах, а второй – в различного рода базах (sql, ldap и прочее). Любые lookup могут быть вложенными, то есть в одном поиске используются результаты другого. Поиск на основе single key строится примерно так: ${lookup{$var_to_search}lsearch{/path/to/file}}
Особо следует отметить расставление фигурных скобок. Если вы знакомы с функциональными языками вроде лиспа, то такой синтаксис для вас будет привычен, иначе просто нужно считать каждую конструкцию списком,
администрирование который обрабатывает exim. Тогда все строковые и другие константы тоже заключаются в фигурные скобки.То есть вышеприведенный пример можно рассматривать как: $ñïèñîê -> ${lookup ñïèñîê} -> ${lookup {$var_to_search} ↵ driver{driver_arguments}}
При этом сам файл, в котором осуществляется поиск, должен выглядеть так: $var_to_search: çíà÷åíèå
Приведу более конкретный пример – построение списка доменов из файла на основании адреса отправителя (пример из документации): domainlist domains = ${lookup{$sender_host_address} ↵ lsearch{/some/file}}
при этом сам файл должен выглядеть следующим образом: 192.168.3.4: domain1 : domain2 : ... 192.168.1.9: domain3 : domain4 : ...
При этом поиск осуществляется по первому полю. Отличие query-style-поиска в том, что мы явно не обозначаем, что искать, указывая вместо этого запрос. Так выглядит типичный запрос к SQL-базе: ${lookup driver{query}{action_if_query_succeed} ↵ {action_if_query_failed}}
Например: domainlist domains = ${lookup pgsql{select domains from ↵ domains where sender='$sender_host_address'}{$value}fail}
При успешном выполнении запроса возвращается значение из базы, иначе совершается специальное действие fail, означающее неудачный запрос. Несколько пугающими бывают запросы к LDAP. Разберу их поподробнее. Указание сервера и порта LDAP выглядит следующим образом: ldap_default_servers = 127.0.0.1::389
Запросы к директории выглядят так: ${lookup ldap{ldap://ldap.test.ru/dc=${domain},ou=mail, ↵ o=tehnopark?mail?sub?(&(objectClass=inetOrgPerson) ↵ (mail=${local_part}@${domain}))}{$value} fail}
Форма запроса описана в стандарте, но, думаю, ее стоит дополнительно пояснить. В запросе мы указываем адрес LDAP-сервера (ldap://ldap.test.ru/), basedn для поиска (dc=${domain},ou=mail,o=tehnopark), значение, которое нужно вернуть (mail), описание запроса (также в постфиксной форме, характерной для функциональных языков: (&(objectClass=inetOrgPerson)(mail=${local_part}@${domain}))). Когда требуется аутентификация на сервере, тогда просто указываем нечто вроде: ${lookup ldap {user="cn=manager,o=tehnopark of innovation,c=RU" pass=secret ldap:///o=tehnopark%20of%20innovation,c=RU?sn?sub?(cn=foo)} {$value}fail}
№7(20), июль 2004
Заметьте необходимость замены пробелов на %20, как того требует стандарт. Более подробно обо всем этом можно узнать на http://www.exim.org/exim-html-4.30/doc/ html/spec_9.html#CHAP9. Думаю, для дальнейшего понимания статьи этого вполне достаточно. Итак, собственно конфигурационный файл exim. Коегде я добавил свои комментарии, чтобы улучшить понимание различных директив. ########################################################## # Runtime configuration file for Exim # ########################################################## # Çäåñü ìû îïðåäåëÿåì ìàêðîñû, îïèñûâàþùèå ðàçëè÷íûå ïóòè CONFIG_PREFIX=/usr/local/etc/exim ACL_PREFIX=CONFIG_PREFIX/acls CERTDIR=CONFIG_PREFIX/certs # Çäåñü ìû óêàçûâàåì, ãäå íàõîäèòü íàø PostgreSQL-ñåðâåð, # ñîåäèíåíèå îñóùåñòâëÿåòñÿ ÷åðåç ëîêàëüíûé ñîêåò, êîìàíäà # hide ïîìîãàåò ñïðÿòàòü ýòó íàñòðîéêó ïðè âûçîâå exim -bP, # êîãäà exim âûâîäèò âñå êîíôèãóðàöèîííûå îïöèè â ñòàíäàðòíûé # âûâîä. Ó÷òèòå, ÷òî ñàì /usr/local/etc/exim/configure äîëæåí # èìåòü âëàäåëüöà root:wheel è èìåòü ïðàâà äîñòóïà 0600, ÷òî # îòëè÷àåòñÿ îò òîãî, ÷òî ïðèíÿòî ïî óìîë÷àíèþ (0644) hide pgsql_servers = (/tmp/.s.PGSQL.5432)/users/pgsql/pAsSwOrD # Òóò ìû îïèñûâàåì ñïèñêè äîìåíîâ # Local_domains âêëþ÷àåò äîìåíû, ñ÷èòàþùèåñÿ ëîêàëüíûìè, òî # åñòü òå äîìåíû, äëÿ êîòîðûõ exim äåëàåò ëîêàëüíóþ äîñòàâêó, # äëÿ îñòàëüíûõ äîìåíîâ ïî÷òà äîñòàâëÿåòñÿ ïî MX çàïèñÿì â DNS. # Îáðàòèòå âíèìàíèå íà äîïîëíèòåëüíûå ôàéëû ACL_PREFIX/localdomains # è ACL_PREFIX/hostingdomains, â êîòîðûõ ïåðå÷èñëåíû äîìåíû, # ðàçäåëåííûå ïåðåâîäîì ñòðîêè (òî åñòü ïî îäíîìó äîìåíó íà # êàæäóþ ñòðîêó) domainlist local_domains = unona.test.ru : ↵ ACL_PREFIX/localdomains : ACL_PREFIX/hostingdomains # Äîïîëíèòåëüíàÿ íàñòðîéêà domainlist hosting_domains = ACL_PREFIX/hostingdomains # Ñïèñîê õîñòîâ, ïî÷òó íà êîòîðûå ìû ÿâíî îòâåðãàåì hostlist host_reject = ACL_PREFIX/hostreject domainlist relay_to_domains = # Ñïèñîê àäðåñîâ, ñ êîòîðûõ ðàçðåøåíà ïåðåäà÷à ïî÷òû âî # âíåøíèé ìèð hostlist relay_from_hosts = localhost : 192.168.1.0/24 : ↵ ACL_PREFIX/relayfromhosts # Ïðîâåðêà ïîëó÷àòåëÿ acl_smtp_rcpt = acl_check_rcptú # Ïðîâåðêà mime ñîäåðæèìîãî acl_smtp_mime = acl_check_mime # Ïðîâåðêà íà ñïàì è âèðóñû acl_smtp_data = acl_check_virus # Çäåñü ìû îïèñûâàåì íàø àíòèâèðóñ av_scanner = clamd:127.0.0.1 3310 # È spamassasin spamd_address = 127.0.0.1 783 # Íàñòðîéêè ïîëüçîâàòåëÿ è ãðóïïû ïî óìîë÷àíèþ exim_user = mailnull exim_group = mail # Íèêîãäà íå îñóùåñòâëÿåì äîñòàâêó ïîä ðóòîì - root äîëæåí # áûòü àëèàñîì íà äðóãîãî ëîêàëüíîãî ïîëüçîâàòåëÿ. Êñòàòè, # ýòî îáÿçàòåëüíîå óñëîâèå, çàäàííîå åùå íà ýòàïå êîìïèëÿöèè never_users = root # Íàñòðîéêè äèðåêòîðèè äëÿ î÷åðåäè spool_directory = /var/spool/exim # Ðàçäåëÿåì spool_directory íà íåñêîëüêî áîëåå ìàëåíüêèõ, # àíàëîã õåø-òàáëèöû, óñêîðÿåò îáðàáîòêó spool split_spool_directory # Ïûòàåìñÿ ñäåëàòü ñîîòâåòñòâèå ïðÿìîé è îáðàòíîé çîíû DNS # äëÿ êàæäîãî õîñòà. Íåñêîëüêî çàòðàòíî, íî âåñüìà ïîëåçíî host_lookup = * # Óáèðàåì ïðîâåðêó identd íà êëèåíòñêîé ñòîðîíå. Èç-çà # íåïðàâèëüíî íàñòðîåííûõ firewall ýòî ÷àñòî âûçûâàåò # äëèòåëüíûå òàéì-àóòû, êðîìå òîãî, ýòîò ñåðâèñ ïîäíÿò # íå ó ìíîãèõ rfc1413_query_timeout = 0s # Óêàçûâàåì êîå-êàêèå ëèìèòû (èõ íàçíà÷åíèå ÿñíî èç íàçâàíèÿ)
9
администрирование smtp_accept_max = 50 smtp_connect_backlog = 40 smtp_accept_max_per_host = 10 smtp_accept_queue = 22 smtp_accept_queue_per_connection = 10 recipients_max = 16 recipients_max_reject = true message_size_limit = 16M accept_8bitmime # Èãíîðèðóåì ñîîáùåíèÿ, êîòîðûå ïðèõîäÿò íàì æå, äàâíîñòü # êîòîðûõ áîëåå 12 ÷àñîâ ignore_bounce_errors_after = 12h # Óäàëÿåì çàìîðîæåííûå ñîîáùåíèÿ, äàâíîñòü êîòîðûõ áîëüøå # íåäåëè. timeout_frozen_after = 7d # Íàñòðîéêè TLS tls_certificate = CERTDIR/mailed.crt tls_privatekey = CERTDIR/mailed.key tls_advertise_hosts = * tls_verify_certificates = * # Ñëåäóþùàÿ îïöèÿ çàêîììåíòèðîâàíà, íî âåñüìà ïîëåçíà, # ïîçâîëÿÿ àâòîðèçèðîâàòüñÿ # òîëüêî ÷åðåç áåçîïàñíûé ssl-êàíàë #auth_advertise_hosts = ${if eq{$tls_cipher}{}{}{*}} ########################################################## # ACL CONFIGURATION # # Specifies access control lists for incoming SMTP mail # ###################################################################### begin acl # Ýòîò ñïèñîê äîñòóïà îïèñûâàåò ïðîâåðêè, îñóùåñòâëÿåìûå # ïðè âûçîâå ëþáîé RCPT-êîìàíäû acl_check_rcpt: # Âíà÷àëå ïðîâåðÿåì äîñòîâåðíîñòü îòïðàâèòåëÿ require verify = sender # Ïðèíèìàåì ñîåäèíåíèÿ îò ëîêàëüíûõ MUA (òî åñòü íå ÷åðåç TCP/IP) accept hosts = : ########################################################## # Ïðîâåðêà ñîîòâåòñòâèÿ ïî÷òîâîãî àäðåñà ñòàíäàðòó deny message = Restricted characters in address domains = +local_domains local_parts = ^[.] : ^.*[@%!/|] accept domains = +local_domains # Çäåñü ïðîïèñàíû òàê íàçûâàåìûå dnsbl, òî åñòü ÷åðíûå # ñïèñêè MTA ñ îòêðûòûì ðåëååì, ìû ïðîâåðÿåì IP-àäðåñ # îòïðàâèòåëÿ íà ñîîòâåòñòâèå òàêèì ñïèñêàì è áëîêèðóåì ïèñüìî, # åñëè îòïðàâèòåëü áûë íàéäåí â òàêîì ñïèñêå deny message = host is listed in $dnslist_domain dnslists = blackholes.mail-abuse.org: ↵ dialups.mail-abuse.org: ↵ relays.mail-abuse.org: ↵ relays.ordb.org: ↵ work.drbl.caravan.ru: ↵ dul.ru:sbl.spamhaus.org # Ïðàâèëî íà ïðîâåðêó âñåõ ïî÷òîâûõ àäðåñîâ, êðîìå ëîêàëüíûõ # (ìåíåå ñòðîãîå) deny message = Restricted characters in address domains = !+local_domains local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./ ########################################################## # Ïðèíèìàåì ïî÷òó äëÿ ïîëüçîâàòåëÿ postmaster ëîêàëüíûõ # äîìåíîâ, íå âçèðàÿ íà îòïðàâèòåëÿ accept local_parts = postmaster domains = +local_domains # Deny unless the sender address can be verified accept
domains endpass verify
= +local_domains = recipient
# Åñëè äîìåí â ñïèñêå relay_to_domains, òî ðàçðåøàåì ðåëåé accept domains = +relay_to_domains endpass verify = recipient accept domains = +hosting_domains endpass verify = recipient accept
10
hosts
= +relay_from_hosts
# Ïðèíèìàåì ëþáûå ñîåäèíåíèÿ, êîòîðûå áûëè óñïåøíî # àâòîðèçîâàíû accept authenticated = * # Ðåàëèçàöèÿ íàøåãî áàí-ëèñòà deny hosts = +host_reject message = You are banned. Go away. # Çàïðåùàåì âñå, ÷òî íå ðàçðåøåíî, çàêðûâàÿ òåì ñàìûì ðåëåé # äëÿ ñïàìåðîâ deny message = relay not permitted # Ñïèñîê äîñòóïà äëÿ ïðîâåðêè mime-÷àñòåé ñîîáùåíèÿ acl_check_mime: # Ïðîèçîäèì äåêîäèðîâàíèå mime-ñîîáùåíèé. Ïîëåçíî äëÿ # äàëüíåéøåé ïðîâåðêè íà âèðóñû warn decode = default # Ìîæíî î÷åíü áûñòðî îòñåÿòü ñîîáùåíèÿ, ïðîñòî çàïðåòèâ # íåêîòîðûå mime-âëîæåíèÿ, ÷àùå âñåãî ñîäåðæàùèå âèðóñû, # õîòÿ, êîíå÷íî, ýòî íå ïàíàöåÿ deny message = Blacklisted file extension detected condition = ${if match {${lc:$mime_filename}} ↵ {\N(\.wav|\.cpl|\.pif| ↵ \.bat|\.scr|\.lnk| ↵ \.com)$\N} {1}{0}} # Ìíîãî ëè ó íàñ ëþäåé, çíàþùèõ êèòàéñêèé? À âîò êèòàéñêîãî # ñïàìà ýòî ïîóáàâèò deny message = Sorry, noone speaks chinese here condition = ${if eq{$mime_charset}{gb2312}{1}{0}} accept # Ïðîâåðêà ñîäåðæèìîãî íà âèðóñû è ñïàì acl_check_virus: # Ìû íå çàïðåùàåì ïèñüìà ñî ñïàìîì, à ïðîñòî äîáàâëÿåì # çàãîëîâîê, ñîäåðæàùèé êîëè÷åñòâî ñïàìåðñêèõ î÷êîâ, # à ïîëüçîâàòåëü íà ñâîåé ñòîðîíå óæå ïðîñòî íàñòðàèâàåò # ñâîè ôèëüòðû. Òàê ìû èñêëþ÷àåì æàëîáû ñî ñòîðîíû # ïîëüçîâàòåëåé î ïîòåðÿííûõ ïèñüìàõ warn message = X-Spam-Score: $spam_score ($spam_bar) spam = nobody:true # Äîáàâëÿåì çàãîëîâêè, óêàçûâàþùèå, ÷òî ïèñüìà áûëè ïðîâåðåíû # SpamAsssasin warn message = X-Spam-Scanned: Yes warn message = X-Spam-Scanner: SpamAssassin running ↵ on mail.test.ru # Âîò ÷òî-÷òî, à âèðóñû íàì íå íóæíû deny message = Message rejected: virus found. Your message was successfully trashed. hosts = * malware = * accept ########################################################## # ROUTERS CONFIGURATION # # Specifies how addresses are handled # ########################################################## # THE ORDER IN WHICH THE ROUTERS ARE DEFINED IS IMPORTANT!# # An address is passed to each router in turn until it is # # accepted. # ###################################################################### begin routers # Ðîóòåð, îñóùåñòâëÿþùèé ïîèñê ïî MX-çàïèñÿì â DNS dnslookup: driver = dnslookup domains = ! +local_domains transport = remote_smtp ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8 no_more # Âñå îñòàíëüíûå ðîóòåðû îáñëóæèâàþò äîñòàâêó ëîêàëüíîé ïî÷òû # Äðàéâåð àëèàñîâ ïîëüçîâàòåëÿ. Îáðàòèòå âíèìàíèå íà lookup # â pgsql-áàçå. ×òî èíòåðåñíî, ýòîò lookup ðàáîòàåò äàæå äëÿ # èåðàðõè÷åñêèõ àëèàñîâ, íàïðèìåð, postmaster -> root -> # cebka -> cebka@jet.msk.su Òàêæå îïðåäåëÿþòñÿ òðàíñïîðòû # äëÿ ïåðåäà÷è ïî÷òû â ôàéë (>/path/to/file) è â pipe # (|/usr/local/libexec/slocal) system_aliases: driver = redirect
администрирование allow_fail allow_defer data = ${lookup pgsql{select alias from aliases where ↵ mail ='$local_part@$domain'}{$value}fail} user = mailnull group = mail file_transport = address_file pipe_transport = address_pipe # Äëÿ ëîêàëüíûõ ïîëüçîâàòåëåé òàêæå ñîçäàåì âîçìîæíîñòü # ïåðåíàïðàâëåíèÿ ïî÷òû ÷åðåç ~/.forward-ôàéë. Åñëè âêëþ÷åíà # äèðåêòèâà allow_filter, òî â .forward-ôàéëå ìîæíî # èñïîëüçîâàòü ÿçûê sieve-ôèëüòðîâ. Äëÿ ïîäðîáíîñòåé # ñì. äîêóìåíòàöèþ íà www.exim.org, ò.ê. íà ðàññìîòðåíèå # ýòîé òåìû óéäåò ñëèøêîì ìíîãî âðåìåíè userforward: driver = redirect check_local_user file = $home/.forward no_verify no_expn check_ancestor # allow_filter file_transport = address_file pipe_transport = address_pipe reply_transport = address_reply condition = ${if exists{$home/.forward} {yes} {no} }
server.\n\ This means, that your mailbox is 75% full. If you would \n\ override this limit new mail would not be delivered ↵ to you!\n" quota_warn_threshold = 75% # Òðàíñïîðò, îñóùåñòâëÿþùèé äîñòàâêó â pipe address_pipe: driver = pipe return_output # Òðàíñïîðò, îñóùåñòâëÿþùèé äîñòàâêó ïðÿìî â ôàéë address_file: driver = appendfile delivery_date_add envelope_to_add return_path_add # Ýòîò òðàíñïîðò èñïîëüçóåòñÿ äëÿ àâòîìàòè÷åñêîãî îòâåòà íà # ñîîáùåíèÿ îá îøèáêàõ address_reply: driver = autoreply ########################################################## # RETRY CONFIGURATION # ##########################################################
# Ëîêàëüíàÿ äîñòàâêà, åñëè äàííûé ïîëüçîâàòåëü íàéäåí â áàçå
begin retry
localuser: driver = accept condition = ${lookup pgsql {select uid from accounts where ↵ login = '$local_part@$domain'}{yes}{no}} transport = local_delivery cannot_route_message = Unknown user
# Íàñòðîéêè ïî óìîë÷àíèþ, êîòîðûå ÿ íå òðîãàë, óïðàâëÿþò # èíòåðâàëîì ïîâòîðíîé ïåðåäà÷è ñîîáùåíèé
########################################################## # TRANSPORTS CONFIGURATION # ###################################################################### # ORDER DOES NOT MATTER # #Only one appropriate transport is called for each delivery.# ###################################################################### begin transports # Äðàéâåð äëÿ äîñòàâêè ÷åðåç ñîåäèíåíèÿ ñ óäàëåííûìè # smtp-ñåðâåðàìè remote_smtp: driver = smtp # Ýòîò òðàíñïîðò äîñòàâëÿåò ïî÷òó â ëîêàëüíûå maildir. Ïóòü # ê maildir õðàíèòñÿ îïÿòü æå â òàáëèöå accounts. Ðàçðåøåíèÿ # íà äèðåêòîðèþ 0700 äëÿ âîçìîæíîñòè ðàáîòû ñ äàííûìè # äèðåêòîðèÿìè imap-ñåðâåðà. Ïðè ýòîì âëàäåëüöåì ÿâëÿåòñÿ # ãðóïïà è ïîëüçîâàòåëü èç accounts (ïîòîìó ïðè âêëþ÷åíèè # çàïèñåé â ýòó òàáëèöó íàäî íà÷èíàòü çíà÷åíèÿ uid # ñ äîñòàòî÷íî áîëüøîãî ÷èñëà, íàïðèìåð, 2000 è ïåðåñåêàòüñÿ # ñ ðåàëüíûìè ïîëüçîâàòåëÿìè îíî äîëæíî, òîëüêî åñëè ðåàëüíîìó # ïîëüçîâàòåëþ íóæåí ëîêàëüíûé äîñòóï ê maildir). # Òàêæå èç òàáëèöû accounts èçâëåêàþòñÿ äàííûå î ðàçìåðå # êâîòû, è óñòàíàâëèâàåòñÿ ïîðîã â 75% îò êâîòû, êîãäà # ïîëüçîâàòåëþ ïîñûëàåòñÿ óêàçàííîå ïðåäóïðåæäåíèå îá ïîäõîäå # ê ïîðîãó êâîòû local_delivery: driver = appendfile directory = ${lookup pgsql{select maildir from accounts ↵ where login = '$local_part@$domain'}{$value}fail} create_directory directory_mode = 0770 maildir_format delivery_date_add envelope_to_add return_path_add group = ${lookup pgsql{select gid from accounts where ↵ login = '$local_part@$domain'}{$value}fail} user = ${lookup pgsql{select uid from accounts where ↵ login = '$local_part@$domain'}{$value}fail} mode = 0660 no_mode_fail_narrower quota = ${lookup pgsql{select mailquota from accounts ↵ where login = '$local_part@$domain'}{$value}fail}M quota_warn_message = "\ To: $local_part@domain\n\ From: postmaster@test.ru\n\ Subject: Your maildir is going full\n\ This message is automaticaly gnerated by your mail ↵
№7(20), июль 2004
# # # # # #
This single retry rule applies to all domains and all errors. It specifies retries every 15 minutes for 2 hours, then increasing retry intervals, starting at 1 hour and increasing each time by a factor of 1.5, up to 16 hours, then retries every 6 hours until 4 days have passed since the first failed delivery.
# Address or Domain Error # ----------------- -----
Retries -------
*
F,2h,15m; G,16h,1h,1.5; F,4d,6h
*
########################################################## # REWRITE CONFIGURATION # ########################################################## begin rewrite # Ñîçäàåì ïðàâèëî ïî ïåðåïèñûâàíèþ çàãîëîâêà To: ñ *@test.ru # íà ëîêàëüíûé smart-host - @unona.test.ru. Ýòî áûëî ñäåëàíî # äëÿ ëîêàëüíûõ ïîëüçîâàòåëåé, ïî èäåå ñ õðàíåíèåì # ïîëüçîâàòåëåé â postgres óæå íå íóæíî, ïîêàçàíî äëÿ ïðèìåðà. *@test.ru $1@unona.test.ru T ########################################################## # AUTHENTICATION CONFIGURATION # ########################################################## # Îïèñàíèÿ àóòåíòèôèêàöèè begin authenticators # CRAM-MD5-àóòåíòèôèêàöèÿ, òðåáóåò íàëè÷èÿ ïàðîëÿ â îòêðûòîì # âèäå, èìÿ ïîëüçîâàòåëÿ äîëæíî áûòü â ôîðìàòå user@domain, # êàê îíî õðàíèòñÿ â òàáëèöå accounts lookup_cram: driver = cram_md5 public_name = CRAM-MD5 server_secret = ${lookup pgsql {select password from ↵ accounts where login='$1'}{$value}fail} server_set_id = $1 # LOGIN-àóòåíòèôèêàöèÿ - íå òðåáóåò õðàíåíèÿ ïàðîëÿ # â îòêðûòîì âèäå, îäíàêî, ïî ñåòè ïàðîëü ïåðåäàåòñÿ # â îòêðûòîì âèäå - òðåáóåòñÿ ëèøü âûïîëíåíèå óñëîâèÿ # server_condition - $1 - èìÿ ïîëüçîâàòåëÿ, à $2 - ïàðîëü. # LOGIN áåçîïàñåí òîëüêî ïðè óñòàíîâëåíèè ssl-ñîåäèíåíèÿ. login: driver = plaintext public_name = LOGIN server_prompts = Username:: : Password:: server_condition = ${lookup pgsql {select login from ↵ accounts where login='$1' and password='$2'}{yes}{no}} server_set_id = $1
11
администрирование ########################################################## # CONFIGURATION FOR local_scan() # ########################################################## # # # # # #
Этот файл указывает настройки для программы freshclam, использующейся для обновления антивирусных баз ClamAV.
If you have built Exim to include a local_scan() function that contains tables for private options, you can define those options here. Remember to uncomment the "begin" line. It is commented by default because it provokes an error with Exim binaries that are not built with LOCAL_SCAN_HAS_OPTIONS set in the Local/Makefile.
# begin local_scan # End of Exim configuration file
Далее необходимо создать сертификат для smtp-сервера. В ходе эксплуатации выяснилось, что некоторые клиенты, например, The Bat!, требуют, чтобы в CN-сертификате (подробнее о сертификатах и способах их генерации можно прочитать в моей статье из данного журнала, располагающейся по адресу http://cebka.pp.ru/my/openssl.txt) содержалось имя (FQDN) сервера. Например, для сервера на mail.test.ru серификат должен содержать CN: mail.test.ru. Содержание остальных полей некритично. Создать сертификат можно, например, так: SpamAssassin работает без дополнительных настроек. # openssl genrsa -out mailed.key 2048 # openssl req -new -x509 -key mailed.key -days 365 -out mailed.crt
При этом все поля сертификата будут запрошены. Учтите, что файл секретного ключа не должен быть зашифрован, иначе exim не сможет его прочитать, просто установите владельцем этого файла root:wheel и установите права 0600. После этого можно попробовать запустить exim в режиме отладки: exim -bd -d+all
и понаблюдать за выводимыми сообщениями. Чтобы включить exim вместо sendmail, выполняем следующие действия: в файле /etc/rc.conf нужны строки: sendmail_enable="NONE" exim_enable="YES" # À òàêæå äëÿ spamassasin è clamav: clamav_clamd_enable="YES" spamd_enable="YES"
Затем настраиваем доставку почты для локальных приложений через exim, для чего файл /etc/mail/mailer.conf у нас должен выглядеть следующим образом: sendmail send-mail mailq newaliases hoststat purgestat
/usr/local/sbin/exim /usr/local/sbin/exim /usr/local/sbin/exim /usr/local/sbin/exim /usr/local/sbin/exim /usr/local/sbin/exim
После этого перейдем к настройке ClamAV: заходим в /usr/local/etc/ и копируем clamav.conf.sample в clamav.conf и правим его таким образом, чтобы он принимал соединения через tcp/ip, а не через локальный сокет, для чего в clamav.conf нужны следующие строки: TCPSocket 3310 TCPAddr 127.0.0.1
Далее копируем freshclam.conf.sample в freshclam.conf.
12
Перейдем к следующей части – настройка courier. Этот imap-сервер для своей работы использует несколько процессов, которые, в свою очередь, настраиваются из различных конфигурационных файлов. В моем примере запущено 3 процесса – authdaemon, courier-imapd и courierimapd-ssl. Причем во внешний мир открыт только courierimapd-ssl. Для начала создадим сертификат для imapd-ssl и для TLS-соединений с imapd: правим файл /usr/local/etc/ couier-imap/imapd.cnf на предмет своего dn для сертификата, для чего в части req_dn прописываем свои настройки (не забудьте, что cn должен совпадать с FQDN imapсервера): [ req_dn ] C=RU ST=Moskow region L=Moskow O=Tehnopark OU=Internet technologies CN=test.ru emailAddress=postmaster@test.ru
администрирование Далее генерируем сертификат специальным скриптом:
или из-под непосредственно DocumentRoot самого apache. Вот в принципе, и все.
cd /usr/local/share/courier-imap/ ./mkimapdcert
После чего настроим imap-аутентификацию: cd /usr/local/etc/courier-imap/ cp authdaemonrc.dist authdaemonrc
Далее в authdaemonrc заменяем список модулей для аутентификации на: authmodulelist="authpgsql"
Теперь courier знает, что ему нужно аутентифицироваться через pgsql. Надо только настроить сам pgsql-модуль, настройки которого хранятся в authpqsqlrc. Он должен содержать следующее: # Ñîåäèíÿåìñÿ ñ ÑÓÁÄ ÷åðåç ëîêàëüíûé ñîêåò (íå óêàçàí àäðåñ) PGSQL_PORT 5432 PGSQL_USERNAME pgsql PGSQL_PASSWORD pAsSwOrD PGSQL_DATABASE users # Íàñòðîéêè òàáëèöû äëÿ # â êàêèõ ïîëÿõ òàáëèöû # äàííûå: PGSQL_USER_TABLE PGSQL_CLEAR_PWFIELD DEFAULT_DOMAIN PGSQL_UID_FIELD PGSQL_GID_FIELD PGSQL_LOGIN_FIELD PGSQL_HOME_FIELD PGSQL_NAME_FIELD PGSQL_MAILDIR_FIELD
àóòåíòèôèêàöèè, çäåñü ìû óêàçûâàåì, õðàíÿòñÿ ðàçëè÷íûå ïîëüçîâàòåëüñêèå accounts password test.ru uid gid login home gecos maildir
Перейдем к настройке непосредственно imapd: скопириуем imapd.dist в imapd. Правим следующие строчки (для остальных настроек вполне подходят значения по умолчанию): # imapd ñëóøàåò òîëüêî íà ëîêàëüíîì àäðåñå, äëÿ âíåøíèõ # ñîåäèíåíèé ìû áóäåì èñïîëüçîâàòü imapd-ssl ADDRESS=127.0.0.1 # Ïî óìîë÷àíèþ â NO, äåëàåì â YES, ÷òîáû imapd ìîã çàïóñêàòüñÿ IMAPDSTART=YES
Этих настроек в принципе хватает. Далее копируем imapd-ssl.dist в imapd-ssl и правим следующие строчки: IMAPDSSLSTART=YES
После чего делаем запуск courier-imap при старте системы. Для этого заходим в /usr/local/etc/rc.d и снова немного попереименовываем файлы: # mv courier-imap-imapd-ssl.sh.sample courier-imap-imapd-ssl.sh # mv courier-imap-imapd.sh.sample courier-imap-imapd.sh
После этого считаем, что наша почтовая система практически готова. Нужно только немного понастраивать SquirrelMail, для чего заходим в /usr/local/www/squirrelmail и запускаем скрипт ./configure. В принципе интерфейс конфигурации понятен без лишних пояснений, потому я не буду особо останавливаться на этой проблеме (не забудьте, главное, указать imap- и smtp-сервера). Делаем симлинк на /usr/local/www/squirrelmail из какого-либо VirtualHost
№7(20), июль 2004
Есть некоторые особенности запуска SquirrelMail при включенном safe_mode в php. Во-первых, все php-файлы и каталоги SquirrelMail должны принадлежать тому пользователю, из-под которого работает apache (настройка VirtualHost или глобальная, по умолчанию – www:nogroup). Кроме того, необходимо сменить разрешения в /var/spool/squirrelmail: # chmod 1777 /var/spool/squirrelmail/attach # chmod 1777 /var/spool/squirrelmail/pref
Ну и владельца установить того же, что и на /usr/local/ www/squirrelmail. Комментарии вы можете присылать на cebka@jet.msk.su.
Cписок полезных ссылок: 1. http://www.exim.org – сайт MTA exim, очень хорошая и подробная документация; 2. ftp://ftp.exim.org/pub/exim/config.samples.tar.bz2 – примеры разнообразных конфигурационных параметров, полезно для ознакомления; 3. http://duncanthrax.net/exiscan-acl/ – патч exiscan с сопутствующей документацией; 4. http://www.clamav.net – сайт свободного антивируса ClamAV; 5. http://www.spamassasin.org – официальный сайт SpamAssasin; 6. http://www.courier-mta.org – ресурс courier продуктов, в том числе и courier-imap; 7. http://www.squirrelmail.org – сайт web-mail-системы SquirrelMail; 8. http://www.nixp.ru/cgi-bin/go.pl?q=articles;a=mta-home – exim+mutt+bmx – для домашней почтовой системы (хорошая и интересная статья); 9. http://tomster.org/geek/freebsdcookbook/ – альтернативный вариант реализации почтовой системы, где вместо courier используется cyrus imapd; 10. http://cebka.pp.ru/samag/clamstat.tar.bz2 – скрипт статистики ClamAV; 11. http://cebka.pp.ru/samag/uidchk.pl – скрипт проверки пересечения uid из БД и системных.
13
администрирование
НАСТРОЙКА ПОЧТОВОЙ СИСТЕМЫ НА БАЗЕ Postfix + Dovecot + PostgreSQL + Amavisd-new + SpamAssassin + ClamAV В данной статье будет рассмотрена установка почтового сервера на базе AltLinux Master 2.2 с использованием пакетов из репозитария Sisyphus. Наша система обеспечит поддержку виртуальных доменов в связке Postfix + Dovecot + PgSQL с защитой от спама и вирусов с помощью SpamAssasin и ClamAV.
РУСТАМ АТНАГУЛОВ Итак, посмотрим, для чего предназначен каждый из этих компонентов ! Postfix (http://postfix.org) – это популярный MTA (Mail Transfer Agent), позволяющий эффективно передавать письма адресатам. ! Dovecot (http://dovecot.org) – Secure IMAP server. IMAPсервер, рассчитанный на максимальную безопасность и надежность. Преимуществом этой программы является то, что для поиска в больших файлах применяется бинарный древовидный индекс. Как указывает автор этого сервера, он тестировал его с 367 000 почтовыми учетными записями и не обнаружил проблем. Также программа может обслуживать запросы пользователей с помощью протоколов imap, imaps, pop3, pop3s. ! ClamAV (http://clamav.sourceforge.net) – opensource-антивирус. ! SpamAssasin (http://spamassasin.org) – почтовый фильтр. Предназначен для борьбы со спамом. ! Amavisd-new (http://www.ijs.si/software/amavisd) – высокоэффективный и надежный интерфейс между MTA и одним или несколькими фильтрами содержимого: вирусные сканеры, спам-фильтры (в нашем случае Spam Assassin). Amavisd-new написан на Perl, обеспечивающим высокую надежность, мобильность и ремонтопригодность. Он взаимодействует с MTA через (E) SMTP или LMTP или при использовании программ-посредников. Сначала устанавливаем все необходимые пакеты из репозитария: [root@mosqit /]# apt-get install postfix postfix-pgsql ↵ postgresql postgresql-server dovecot amavisd ↵ clamav spamassasin
Далее переходим к конфигурированию нашей почтовой системы. Создаем пользователя, от имени которого будет работать наша почтовая система: [root@mosqit /]# groupadd -g 5000 mailuser [root@mosqit /]# adduser -d /var/spool/mail -M -g mailuser ↵ -s /sbin/nologin -u 5000 mailuser
PostgreSQL Настройку и конфигурирование PostgreSQL не привожу, т.к. информации об этом предостаточно.
14
Создадим пользователя mailuser в базе данных Postgre SQL, c правами которого Postfix и Dovecot будут подключаться к базе данных и выполнять запросы: [root@mosqit
/]# createuser -U postgres
Устанавливаем пароль пользователю mailuser: [root@mosqit
/]# psql -U postgres template1
Следующий этап – это создание базы данных, в которой будет все храниться. [root@mosqit
/]# createdb -U postgres mails
База данных mails создана. Далее создаем в ней таблицы. Есть 2 способа: писать SQL-команды руками в клиенте postgresql или приготовить файл с SQL-запросом, а потом создать структуру таблиц, выполнив этот самый запрос.Я выбираю последний способ. Создаем файл createtables.sql: CREATE TABLE "public"."users" ( "userid" VARCHAR(128) NOT NULL, "password" VARCHAR(128), "realname" VARCHAR(128), "uid" INTEGER NOT NULL, "gid" INTEGER NOT NULL, "home" VARCHAR(128) NOT NULL, "mail" VARCHAR(255), CONSTRAINT "users_pkey" PRIMARY KEY("userid") ) WITHOUT OIDS;
администрирование grant select on users to mailuser; CREATE TABLE "public"."aliases" ( "alias" VARCHAR(128) NOT NULL, "dest" VARCHAR(128) NOT NULL, "comment" TEXT DEFAULT ''::text ) WITH OIDS; grant select on aliases to mailuser;
Структура таблиц создана: [root@mosqit /]# /usr/bin/psql -U postgres mails < createtable.sql
queue_directory = /var/spool/postfix mail_owner = postfix # Ýòîò ïàðàìåòð îòíîñèòñÿ ê Amavisd # Ïîêà êîììåíòèðóåì, ÷òîáû ìîæíî áûëî ïðîâåðèòü # ðàáîòîñïîñîáíîñòü postfix. #content_filter=smtp-amavis:[127.0.0.1]:10024
Остальные параметры по умолчанию, но их можно изменить по собственному усмотрению. Далее редактируем конфигурационный файл master.cf: master.cf:
Запускаем PostgreSQL: [root@mosqit /]# service postgresql start
Postfix Переходим к правке конфигурационного файла main.cf, который находится в /etc/postfix.main.cf. Он хорошо комментирован, поэтому внимательно вчитываемся в описание и настраиваем согласно своим требованиям. У меня main.cf выглядит так: main.cf: # Çàäàåì èìÿ íàøåãî ïî÷òîâîãî óçëà myhostname = testdomain.ru # Èìÿ íàøåãî äîìåíà, åñëè íå óêàçûâàòü, òî ïî óìîë÷àíèþ # âûñòàâèòñÿ ìèíóñ ïåðâûé êîìïîíåíò èìåíè óçëà mydomain = testdomain.ru myorigin = $myhostname # RECEIVING MAIL # Çäåñü çàäàåì, íà êàêèõ ñåòåâûõ èíòåðôåéñàõ áóäåò ðàáîòàòü # Postfix, â ìîåì ñëó÷àå íà âñåõ inet_interfaces = all # Ñïèñîê äîìåíîâ, íà êîòîðûå áóäåò îñóùåñòâëÿòüñÿ äîñòàâêà # ÷åðåç local transport mydestination = localhost, $myhostname, ↵ localhost.$mydomain, $config_directory/mydestination # REJECTING MAIL FOR UNKNOWN LOCAL USERS # Çäåñü ìû óêàçûâàåì, ÷òî ïîëüçîâàòåëåé testdomain.ru íóæíî # ïðîñìàòðèâàòü â $virtual_maibox_maps local_recipient_maps = $virtual_maibox_maps unknown_local_recipient_reject_code = 550 # TRUST AND RELAY CONTROL # çäåñü óêàçûâàåì, êîìó ðàçðåøåí relay, ò.å. ïîëüçîâàòåëÿì # îáñëóæèâàåìîé ñåòè mynetworks = 127.0.0.0/8, 10.70.1.0/24 # Çàäàåì äèðåêòîðèþ, ãäå áóäåò õðàíèòüñÿ ïî÷òà mail_spool_directory = /var/spool/mail mailbox_command = /usr/bin/procmail -a $DOMAIN -d $LOGNAME # Íàø ïî÷òîâûé ñåðâåð òðåáóåò ñíà÷àëà êîììàíäó helo smtpd_helo_required = yes transport_maps = hash:/etc/postfix/transport.cf # Çàäàåì äèðåêòîðèþ, ãäå áóäåò õðàíèòüñÿ ïî÷òà # èç âèðòóàëüíûõ äîìåíîâ virtual_mailbox_base = /var/spool/mail/ # Ïðîñìîòð òàáëèö ñ öåëüþ ïðîâåðêè äåéñòâèòåëüíîñòè àäðåñà virtual_mailbox_maps = pgsql:/etc/postfix/mailbox.pgsql virtual_alias_maps = pgsql:/etc/postfix/aliases.pgsql # Èñïîëüçóþ ñòàòè÷åñêèå uid & gid; 5000 – ýòî mailuser virtual_uid_maps = static:5000 virtual_gid_maps = static:5000 virtual_minimum_uid = 5000 # INSTALL-TIME CONFIGURATION INFORMATION readme_directory = /etc/postfix/README_FILES sample_directory = /etc/postfix/samples sendmail_path = /usr/sbin/sendmail setgid_group = postdrop command_directory = /usr/sbin manpage_directory = /usr/share/man daemon_directory = /usr/lib/postfix newaliases_path = /usr/bin/newaliases mailq_path = /usr/bin/mailq
№7(20), июль 2004
# äàëåå îòíîñèòñÿ ê Amavisd -o smtp_data_done_timeout=1200 -o smtp_send_xforward_command=yes -o disable_dns_lookups=yes -o -o -o -o -o -o -o -o -o -o -o -o
content_filter= relay_recipient_maps= smtpd_restriction_classes= smtpd_client_restrictions= smtpd_helo_restrictions= smtpd_sender_restrictions= smtpd_recipient_restrictions=permit_mynetworks,reject mynetworks=127.0.0.0/8 strict_rfc821_envelopes=yes smtpd_error_sleep_time=0 smtpd_soft_error_limit=1001 smtpd_hard_error_limit=1000
И соответственно создаем файлы aliases.pgsql, maibox.pgsql, transport.cf: aliases.pgsql: hosts = localhost user = mailuser password = topsecret dbname = mails table = aliases select_field = dest where_field = alias mailbox.pgsql: hosts = localhost user = mailuser password = topsecret dbname = mails table = users select_field = home where_field = userid transport.cf: testdomain.ru another.domain.ru
virtual: virtual:
15
администрирование В transport.cf указываем, каким образом будут обрабатываться домены, наши домены – через virtual, в противном случае они будут обработаны с помощью local_ transport. Запускаем postfix: [root@mosqit /]# service postfix start
Проверяем работу нашего сервера: [root@mosqit /]#
telnet 127.0.0.1 25
Работает, иначе смотрим лог-файлы в /var/log/maillog.
Создание учетных записей пользователей Для этого я написал пару простейших скриптов на bash: add_mailuser.bash: #! /bin/bash basedir=/var/spool/mail
add_alias.bash: #! /bin/bash echo "Ââåäèòå aliasname âèäå name@domain" read aliasname pos=`expr match "$aliasname" '[a-z1-9A-Z.]*@'` if [ $pos = "0" ] then echo "Íåïðàâèëüíûé aliasname" exit fi echo "Ââåäèòå destination â âèäå name@domain" read dest pos=`expr match "$dest" '[a-z1-9A-Z.]*@'` if [ $pos = "0" ] then echo "Íåïðàâèëüíûé destination" exit fi
echo "Ââåäèòå e-mail â âèäå name@domain" read email pos=`expr match "$email" '[a-z1-9A-Z.]*@'` if [ $pos = "0" ] then echo "Íåïðàâèëüíîå èìÿ ïîëüçîâàòåëÿ" exit
echo "Ââåäèòå êîììåíòàðèé" read comment echo -E "INSERT INTO public.aliases (email, alias, comment) ↵ VALUES ('$aliasname', '$dest', '$comment');" ↵ | /usr/bin/psql -U postgres mails echo "Alias ñîçäàí óñïåøíî"
fi domain=${email:$pos} name=${email:0:($pos-1)} cd $basedir/$domain/$name &> /dev/null if [ $? = "0" ]; then echo 'Òàêîé ïîëüçîâàòåëü ñóùåñòâóåò !! ' exit fi cd $basedir/$domain &> /dev/null if [ $? = "1" ]; then echo 'Òàêîãî äîìåíà íå ñóùåñòâóåò' exit fi echo "Ââåäèòå ïîëíîå èìÿ " read fullname echo "Ââåäèòå ïàðîëü " read pass echo "ïàðîëü $pass" echo -E "INSERT INTO public.users (userid, password, ↵ realname, uid, gid, home, mail) VALUES ('$email', '$pass', '$fullname', '42', '42','$domain/$name/inbox', NULL);" | /usr/bin/psql -U postgres mails mkdir $basedir/$domain/$name touch $basedir/$domain/$name/inbox chmod 600 $basedir/$domain/$name/inbox chmod 700 $basedir/$domain/$name chown postfix:postfix $basedir/$domain/$name -R ls $basedir/$domain/$name/inbox &> /dev/null if [ $? = "1" ]; then echo 'Îøèáî÷êà âûøëà! ' exit fi echo "Ïîëüçîâàòåëü ñîçäàí óñïåøíî"
read email pos=`expr match "$email" '[a-z1-9A-Z.]*@'` if [ $pos = "0" ] then echo "Íåïðàâèëüíîå èìÿ ïîëüçîâàòåëÿ" exit fi domain=${email:$pos} name=${email:0:($pos-1)} cd $basedir/$domain/$name &> /dev/null if [ $? = "1" ]; then echo 'Òàêîé ïîëüçîâàòåëü íå ñóùåñòâóåò !! ' exit fi echo "DELETE from public.users where userid='$email';" | ↵ usr/bin/psql -U postgres mails rm -rf $basedir/$domain/$name &> /dev/null cd $basedir/$domain/$name &> /dev/null echo $? if [ $? = "0" ]; then echo 'Îøèáî÷êà âûøëà! ' exit fi echo "Ïîëüçîâàòåëü óäàëåí óñïåøíî"
Скрипты созданы на скорую руку и поэтому не совершенны, но всегда существует возможность модифицировать их по своему усмотрению. Создаем почтового пользователя test@testdomain.ru: [root@mosqit /]# add_mailuser.bash
Выполняем инструкции, которые выводятся на экран. Далее проверяем, как Postfix доставляет почту: ↵ ↵
[root@mosqit /]# service postfix restart [root@mosqit /]# telnet 127.0.0.1 25
del_mailuser.bash: #! /bin/bash basedir=/var/spool/mail echo "Ââåäèòå e-mail, êîòîðûé õîòèòå óäàëèòü â âèäå name@domain"
16
Обращаю внимание на точку c новой строки после test
администрирование message, это означает, что передача данных завершена. Далее идем в каталог /var/spool/mail/testdomain/test и смотрим содержимое файла inbox, в нем и должно находиться наше сообщение. Если файл пуст, то опять же смотрим лог-файл (maillog).
ClamAV Идем в /etc/clamav.conf и находим строчку ScanMail и раскомментируем ее, для того чтобы антивирус проверял файлы формата mailbox. Запускаем ClamAV: [root@mosqit /]# service clamd start
Amavisd-new Далее настраиваем /etc/amavis/amavisd.conf по своему усмотрению, конечно же, перед этим прочитав документацию. Лично я ничего не изменял, но скорее всего со временем придется внести коррективы. Запускаем Amavisd-new:
auth_user = root
После этого нам требуется сгенерировать SSL-сертификат, т.к. у нас заявлены протоколы imaps и pop3s (s – secure). Для этого правим файл /etc/dovecot/dovecot-openssl.cnf: [ req ] default_bits = 1024 encrypt_key = yes distinguished_name = req_dn x509_extensions = cert_type prompt = no [ req_dn ] # country (2 letter code) C=RU # State or Province Name (full name) ST=Bashkortostan # Locality Name (eg. city) L=Ufa # Organization (eg. company) O=Home Organizations.
[root@mosqit /]# service amavisd start
# Organizational Unit Name (eg. section) OU=POP3 IMAP server
Проверяем его работу:
# Common Name (*.example.com is also possible) CN=testdomain.ru
[root@mosqit /]# telnet 127.0.0.1 10024
# E-mail contact emailAddress=master@testdomain.ru [ cert_type ] nsCertType = server
Раскомментируем строчку: filter=smtp-amavis:[127.0.0.1]: 10024 в файле main.cf. Перезапускаем Postfix, запускаем ClamaV, Amavisd:
Запускаем скрипт для генерации сертификата. [root@mosqit /]# /usr/share/dovecot/mkcert.sh
[root@mosqit /]# service postfix restart
Как и ранее, проверяем с помощью telnet работоспособность postfix. Если все нормально, то переходим к настройке Dovecot.
Dovecot Далее редактируем файл настроек dovecot /etc/dovecot.conf. Так же внимательно вчитываемся в комментарии к параметрам. dovecot.conf: # Ïðîòîêîëû, êîòîðûå ìû èñïîëüçóåì protocols = imap imaps pop3 pop3s ## IMAP login process login = imap ## POP3 login process login = pop3 ## Mail processes first_valid_uid = 5000 # Ïóòü ê äèðåêòîðèè, ãäå õðàíèòñÿ ïî÷òà, ãäå %d – # èìÿ äîìåíà, %n – èìÿ ïîëüçîâàòåëÿ default_mail_env = mbox:/var/spool/mail/%d/%n/: ↵ INDEX=/var/spool/mail/%d/%n mbox_locks = fcntl flock ## Authentication processes auth = default auth_mechanisms = plain auth_userdb = pgsql /etc/dovecot/pgsql.conf auth_passdb = pgsql /etc/dovecot/pgsql.conf # root ò.ê. èñïîëüçîâàòü ïîðòû äî 1024 ïîðòà ìîæåò òîëüêî root
№7(20), июль 2004
Правим файл /etc/dovecot/pgsql.conf, в котором указываем, как Dovecot будет подключаться и забирать данные. connect = host = localhost dbname = mails user = mailuser ↵ password = secret default_pass_scheme = PLAIN password_query = SELECT password FROM users WHERE userid = '%u' user_query = SELECT '/var/spool/mail/'|| home, uid, gid ↵ FROM users WHERE userid = '%u'
Все настройки произведены, можно испытать работоспособность нашего pop3-сервера: [root@mosqit
/]# service dovecot start
Настраиваем наш почтовый клиент и проверяем, доставляется почта или нет. Имя пользователя указывается полное, т.е. test@testdomain.ru, а не test. Если же не получается, то заглядываем в /var/log/ messages и устраняем причину (довольно часто причиной бывает банальная опечатка при редактировании конфигурационных файлов).
Используемая литература: 1. Postfix Documentation: http://postfix.org/documentation.html 2. Documentation: http://dovecot.org/documentation.html 3. DovecotPostgresql: http://wiki.dovecot.org/moint.cgi/Dovecot Postgresql
17
администрирование
«КТО СТУЧИТСЯ В ДВЕРЬ КО МНЕ…» ОБЗОР СИСТЕМЫ ПОДДЕРЖКИ СПИСКОВ РАССЫЛОК GNU MAILMAN Нет, это, конечно, не милиция (надеюсь, вы не используете знания, полученные со страниц журнала, в целях, противоречащих вашему местному законодательству). Речь пойдет об открытом программном продукте, предназначенном для создания собственных списков рассылки – Mailman (в переводе с англ. – почтальон). Сей продукт расположился в глобальной паутине по адресу http:// www.list.org. Mailman распространяется в соответствии с General Public License. Используется в своих рассылках такими маститыми IT-компаниями, как Red Hat, Apple Computer, Dell Computers и большим числом GNU-проектов, включая The XFree86 Project, Samba, Exim, XEmacs. Со списком сайтов, где используется рассматриваемый продукт, можно ознакомиться по ссылке http://www.list.org/ inthenews.html. Программа обладает обширным списком возможностей. Вот некоторые из них: ! Управление списками, подпиской и пользовательской конфигурацией через веб-интерфейс. ! Модерирование рассылок, поддержка открытых и закрытых списков рассылки, фильтров. ! Поддержка архивов списков рассылки. ! Автоответчик. Прежде чем приступить к установке, ознакомимся с требованиями к системе, на которую ставится Mailman: ! Естественно, должен присутствовать почтовый сервис. Mailman работает с Sendmail, Postfix, Exim и qmail. На моей тестовой системе стоял Sendmail, поэтому на него и буду ориентироваться в описании. ! Веб-сервер с поддержкой CGI/1.1 API. ! GCC 2.8.1 или выше. ! Интерпретатор Python, на котором, собственно, и написана система (с небольшими кусочками кода на C). Поддерживаются версии 2.1 и выше. Я проверял установку системы на дистрибутиве White Box Enterprise Linux 3 (http://www.whiteboxlinux.org/), который является версией Red Hat Enterprise Linux 3, заново собранной из свободно доступных исходников. В моем случае интеграция «почтальона» производилась с веб-сервером Apache. Далее в статье будет дан необходимый минимум сведений, достаточный для установки Mailman и создания своего собственного списка рассылки.
Установка Скачиваем с сайта проекта либо с одного из зеркал архив mailman.tar.gz. Последняя версия «почтальона» на
18
АНДРЕЙ МАРКЕЛОВ момент написания статьи была 2.1.5. Конечно, можно было бы найти и поставить Mailman из rpm-файла. Так было бы проще, но, учитывая, что не все читатели используют системы на основе менеджера пакетов rpm, рассмотрим установку из исходных текстов. Разархивируем скачанный файл в каталог /tmp. gunzip mailman.tar.gz tar –xvf mailman.tar
Для дальнейших действий требуются полномочия супервизора. Создаем пользователя mailman, являющегося членом группы mailman. В дистрибутивах, основывающихся на Red Hat Linux это делается одной командой: useradd mailman
Теперь создаем директорию, в которую будет установлен Mailman и даем на нее права: mkdir /usr/local/mailman chgrp mailman /usr/local/mailman chmod a+rx,g+ws /usr/local/mailman
Далее заходим в систему как пользователь mailman или другой пользователь, которого вы предварительно включили в группу mailman. Важно, чтобы эти шаги выполнялись не от лица суперпользователя: cd /tmp/mailman-2.1.5/ ./configure --with-cgi-gid=apache --with-mail-gid=mail make install
Теперь проверим корректность разрешений на файлы при помощи специального скрипта, входящего в пакет: cd /usr/local/mailman ./bin/check_perms
Скрипт должен выдать сообщение о том, что проблем с правами не обнаружено. Далее, даем команду exit, и уже под пользователем root правим httpd.conf, добавив туда строчки: ScriptAlias /mailman/ /usr/local/mailman/cgi-bin/ Alias /pipermail/ /usr/local/mailman/archives/public <Directory “/usr/local/mailman/archives/public/”> AddDefaultCharset Off </Directory>
Таким образом, мы предписываем Apache, что из указанного каталога разрешается выполнять CGI-скрипты, со-
администрирование здаем алиас для /archives/public/ и выключаем для этого каталога кодировку «по умолчанию». Потом копируем логотипы Mailman, Python, GNU Logo в доступное вашему веб-серверу место, и не забываем перезапустить сам веб-сервер: cp /usr/local/mailman/icons/* /var/www/icons service httpd restart
Создаем служебный «site-wide»-список под названием mailman: ./bin/newlist mailman
По запросу вводим mail ведущего список и пароль (на этот адрес будет выслано письмо с подтверждением и указанным паролем). Теперь, согласно инструкциям, выданным после отработки команды, добавляем в /etc/aliases псевдонимы (в качестве примера был создан список под именем test): test: "|/usr/local/mailman/mail/mailman post test" test-admin: "|/usr/local/mailman/mail/mailman admin test" test-bounces: "|/usr/local/mailman/mail/mailman bounces test" test-confirm: "|/usr/local/mailman/mail/mailman confirm test" test-join: "|/usr/local/mailman/mail/mailman join test" test-leave: "|/usr/local/mailman/mail/mailman leave test" test-owner: "|/usr/local/mailman/mail/mailman owner test" test-request: "|/usr/local/mailman/mail/mailman request test" test-subscribe: "|/usr/local/mailman/mail/mailman ↵ subscribe test" test-unsubscribe: "|/usr/local/mailman/mail/mailman ↵ unsubscribe test"
Если ваш Sendmail идет с включенной по умолчанию опцией smrsh, ограничивающей исполнение скриптов через MTA (а в случае использования дистрибутивов, не базирующихся на Red Hat Linux, это именно так), то вам необходимо в каталоге /etc/smrsh/ создать симлинк на mailman: cd /etc/smrsh/ ln –s /usr/local/mailman/mail/mailman mailman
чанию). Шаблон содержит практически все настройки системы, и хорошо задокументирован. При первой, тестовой установке я предлагаю его не изменять, так как с настройками можно «поиграться» и через веб-интерфейс. Так будет намного нагляднее. ./bin/config_list –I data/sitelist.cfg mailman
Теперь задаем пароль администратора сайта. Он понадобится для создания публичных списков рассылки. ./bin/mmsitepass
Если в процессе конфигурирования у вас что-то пойдет не очень гладко, рекомендую просмотреть FAQ по адресу: http://www.python.org/cgi-bin/faqw-mm.py?req=index. Также обратите внимание на логи «почтальона», ведущиеся в каталоге /usr/local/mailman/logs/, и возможные блокировки в /usr/local/mailman/loсks/.
Создаем список рассылки Теперь у нас все готово для создания первого списка рассылки. Идем по ссылке: http://сервер.домен.ru/mailman/ create, и заполняем поля: имя списка, пароль, почтовый ящик владельца, язык. В самом последнем поле вводим пароль администратора сайта, который мы задавали командой mmsitepass. После успешного создания списка вам придет письмо с подтверждением, и будет доступна ссылка: http:// сервер.домен.ru/mailman/admin/имя_списка, по которой можно вызвать интерфейс администратора. А по http:// сервер.домен.ru/mailman/listinfo/имя_списка можно подписаться на список. Также по http://сервер.домен.ru /mailman/ listinfo приведен общий перечень всех списков рассылки, существующих на сайте. Естественно, в нем пока присутствует только один элемент.
Следующий шаг – создание списка периодически выполняемых задач при помощи crontab (например, оповещение администратора о запросах на включение пользователей в список рассылки): cd /usr/local/mailman/cron crontab –u mailman crontab.in cd ..
Теперь запустим демона вручную, а на будущее добавим возможность автоматического старта после перезагрузки сервера. Команды для дистрибутивов, поддерживающих chkconfig, следующие: ./bin/mailmanctl start cp scripts/mailman /etc/init.d/mailman chkconfig –add mailman
Проверьте правильность выставленных значений переменных DEFAULT_EMAIL_HOST и DEFAULT_URL_HOST в файле ./Mailman/Defaults.py. Они должны содержать полное имя вашего почтового и веб-сервера. Далее применяем шаблон сайта (его можно было предварительно отредактировать, но мы все оставим по умол-
№7(20), июль 2004
Протестировать новый список, предварительно подписавшись на него, можно, отправив письмо на адрес имя_ списка@сервер.домен.ru. Я не буду подробно описывать интерфейс администратора и настройки списков, поскольку все параметры снабжены вполне понятными описаниями на русском языке.
19
администрирование
КАК РАЗЛИЧАТЬ И СЧИТАТЬ МАРКИРОВАННЫЙ ТРАФИК?
ПАВЕЛ ЗАКЛЯКОВ В предыдущей статье о пиринге [2] я специально не затрагивал технических вопросов, неизбежно возникающих при раздельной тарификации. Сейчас я хотел бы в двух словах рассказать, что такое маркировка, зачем она нужна, когда и где может пригодиться. А также поделиться опытом подсчёта маркированного трафика. Подобно тому, как биологи и химики в своих химических реакциях используют помеченные атомы (нуклиды, а точнее, изотопы1) для выяснения в подробностях, какие именно из них участвуют в реакции, и в какие вещества они переходят, в сети используют аналогичную схему с маркировкой с целью выяснения маршрутов и разделения различных видов трафика. Если выяснение перемещения атомов – задача ско-
20
рее научная, малоприменимая на практике, то вопрос с трафиком более чем насущен для большинства сетей и на практике очень актуален. С философской точки зрения получается интересная ситуация, когда конечным пользователям не очень интересен вопрос с маркировкой. Что и говорить, если кругозор большинства пользователей обычно не распространяется дальше, чем IPадрес их компьютера, а их компьютеру, использующему в большинстве случаев ОС Windows, вообще не ведомо такое понятие, как маркировка. Но в то же время, эти же пользователи являются зависимыми от маркировки и маршрутизации, так как большинство из них привыкло экономить, а цена и другие параметры трафика напрямую зависят от маршрута.
администрирование Любой уважающий себя владелец сети, желая повысить её надёжность, как только появляется такая возможность, создаёт резервные каналы на ответственных участках. В графе сети появляются петли, а это означает, что на этих участках уже не существует однозначного маршрута между двумя узлами. Проблема появляется в тот момент, когда выясняется, что себестоимость доставки трафика по различным граням графа оказывается разной. Помимо стоимости у граней (маршрутов) имеются и другие важные характеристики. Вот некоторые из них: задержка, надёжность передачи, пропускная способность. Можно точно сказать, что пиринг неразрывно связан с поставленной проблемой. Допустим, имеется две сети с реальными адресами в двух соседних микрорайонах, обслуживаемых различными провайдерами. Для прохода пакетов из одной сети в другую пакеты уходят к одному провайдеру, передаются другому и попадают в соседнюю сеть. Пользователи двух сетей понимают, что даже если между провайдерами заключены пиринговые соглашения и трафик обходится в копейки, то канал между сетями, какой бы он быстрый не был, так или иначе будет узким местом. В попытках расширить это узкое место создаётся дополнительный канал и с этого момента трафик между сетями может ходить по разным маршрутам. Если в данном примере между провайдерами нет пиринговых соглашений, то одни и те же пользователи могут быть готовы к обмену файлами через бесплатный канал, а через платные каналы провайдеров – нет. Как же быть в этой ситуации? Когда-то бесплатный шлюз между сетями работает, а когда-то нет. Открыть свой ftp для скачивания и закачивания адресам другой сети – значит привлечь к себе дополнительный трафик в те моменты, когда шлюз между сетями по каким-то причинам не работает или находится на профилактике. Не разрешать доступ к открытому ftp из другой сети – лишить себя возможности получить новые файлы. На уровне провайдеров проблемы нет, так как его основные маршрутизаторы, принимая трафик, всегда видят откуда (с какого интерфейса) он был получен, поэтому и могут его правильно подсчитывать. Проблемы начинаются у пользователей, которые, наоборот, не знают по какой цене им учитывать те или иные пакеты. Тут однозначно работает придуманное мной правило (из разряда следствий закона Мёрфи): «меньше знаешь – больше платишь». Узнав, откуда приходит трафик, можно нежелательный ограничить и платить меньше. Логичным и давно используемым решением данной проблемы служит маркировка трафика. Под маркировкой
следует понимать изменение значений определённых битов в пакетах в соответствии с принятыми договорённостями. По идее можно менять что угодно, как угодно и где угодно, однако в большинстве случаев это негативно скажется на правильной работе тех или иных уже существующих протоколов, поэтому согласно RFC791 (Internet Protocol) было решено использовать для целей маркировки только один, второй байт в заголовке IP-пакетов, именуемый ToS, сокращённо от Type of Service, изменения в рамках которого должны пониматься всеми корректно.
Ðèñóíîê 1. Çàãîëîâîê IP-ïàêåòà. Êðàñíûì âûäåëåíî ïîëå, îáû÷íî èñïîëüçóåìîå äëÿ ìàðêèðîâêè ïàêåòîâ
Согласно тому же самому RFC791 выделенное красным цветом поле ToS подразделяется на несколько подполей, которые имеют следующее предназначение для используемых в них битах. Ðèñóíîê 2. Ðàçäåëåíèå ïîëÿ ToS íà áèòû ñîãëàñíî RFC791
Первые три бита с 0 по 2 – это поле Precedence. Всего может быть 8 комбинаций значений этих битов: ! 111 – Network Control ! 110 – Internetwork Control ! 101 – CRITIC/ECP ! 100 – Flash Override ! 011 – Flash ! 010 – Immediate ! 001 – Priority ! 000 – Routine Несмотря на то что согласно рекомендации поле Precedence входит в состав поля ToS, иногда под полем ToS подразумевают всё, что идёт после поля Precedence, не включая его и зарезервированные 2 бита, а встречающиеся далее в нём биты имеют следующее предназначение. Бит 3 (буква D (Delay) на рис. 2) отвечает за задержку трафика и рекомендует в случае, если в этом поле стоит 1 минимизировать задержку (0 = Normal Delay, 1 = Low Delay). Бит 4 (буква T (Throughput) на рис. 2) отвечает за пропускную способность: 0 – обычная (Normal Throughput), 1 – по возможности, повышенная (High Throughput). Бит 5 отвечает за надёжность передачи: 0 – обычная надёжность (Normal Reliability), 1 – повышенная (High Reliability). Биты 6 и
1
Изотопы: (от изо... и греч. topos – место ), разновидности одного химического элемента, занимающее одно место в периодической системе элементов Менделеева, но отличающиеся массами атомов. Химические свойства атомов, т.е. принадлежность атома к тому или иному химическому элементу, зависят от числа электронов и их расположения в электронной оболочке атома. Место химического элемента в периодической системе элементов определяется его порядковым номером Z, равным числу электронов в оболочке атома или, что то же самое, числу протонов, содержащихся в атомном ядре. Кроме протонов, в ядро атома входят нейтроны, масса каждого их которых приблизительно равна массе протона. Количество нейтронов N в ядре атома с данным Z может быть различным, но в определённых пределах... От соотношения чисел протонов и нейтронов в ядре зависят различные свойства атомов. Атомы с одинаковым Z, но с различным числом нейтронов N обладают идентичными химическими свойствами... и также называются изотопами. Большая Советская Энциклопедия, том 10, М.: Издательство «Советская энциклопедия»,1972, стр 109.
№7(20), июль 2004
21
администрирование 7 не используются и зарезервированы для будущих целей. Замечание. Согласно RFC 792 (Internet Control Message Protocol), так как у ICMP-пакетов используется заголовок, идентичный протоколу IP, то в них также может использоваться маркировка. Программа sing [9] не только показывает значение поля ToS приходящих пакетов, но и позволяет с помощью параметров менять это поле у отправляемых ею пакетов. Использование маркировки IP-пакетов в поле ToS было задумано в 1979 году с самого начала появления этого протокола, определяемого устаревшим на сегодня документом IEN 123. Появившиеся вместо него документы RFC 760 (1980 г.) и RFC 791 (1981 г.) не изменили ситуацию существенным образом. Последний документ действует и поныне, однако через 10 лет после появления протокола IP вышел RFC 1122 (Requirements for Internet Hosts – Communication Layers), который включил два зарезервированных бита в TOS, и разделение полей стало выглядеть так: Ðèñóíîê 3. Ðàçäåëåíèå ïîëÿ ToS íà áèòû ñîãëàñíî RFC 1122
Чуть позже, когда назначение сети Интернет сместилось в коммерческую область, да и многие другие вещи стали замещаться денежными эквивалентами, в 1992 году к вопросу о маркировке пакетов вышло дополнение – RFC 1349 (Type of Service in the Internet Protocol Suite), согласно которому у поля ToS был отобран последний бит, и оно получило следующую трактовку битов. Ðèñóíîê 4. Ðàçäåëåíèå ïîëÿ ToS íà áèòû ñîãëàñíî RFC 1349
По сравнению с предыдущей редакцией из двух последних неиспользуемых битов (6-го и 7-го) только 6-му биту было дано новое определение – минимизация возможной стоимости передачи трафика. Если 6-й бит равен 1, то следует выбирать более дешёвые маршруты. В 1993 году вышел RFC 1455, который предлагал вариант использования поля TOS (биты 3-6) для задания гарантированного уровня безопасности соединений. С появлением IP-телефонии и других сервисов, ставших доступными в сети, в декабре 1998 года вышел RFC 2474 (Definition of the Differentiated Services Field (DS Field) in the IPv4 and IPv6 Headers), согласно которому значение поля ToS целиком должно быть заменено полем DS (for differentiated services). После чего значения поля ToS, отмеченного на рис. 1, приобрели следующие значения. Ðèñóíîê 5. Ðàçäåëåíèå ïîëÿ DS (áûâø. ToS) íà áèòû ñîãëàñíî RFC 2474 DSCP: differentiated services codepoint CU: currently unused
Cвято место пусто не бывает, через месяц, в январе 1999-го, неиспользуемые два бита CU изменили своё название на ECN и получили другое назначение, которое стал определять документ RFC 2481 (A Proposal to add Explicit Congestion Notification (ECN) to IP). Ðèñóíîê 6. Ðàçäåëåíèå ïîëÿ DS (áûâø. ToS) íà áèòû ñîãëàñíî RFC 2481
22
В сентябре 2001 вышел RFC 3168 (The Addition of Explicit Congestion Notification (ECN) to IP), который был нацелен на то, чтобы внести стабильность в поле маркировки и закрепить сложившуюся ситуацию, обозначенную в устаревшем RFC 2481. Новая рекомендация заменила старые RFC 2481 и RFC 2474 и внесла поправки в некоторые другие документы. Такая бурная история изменения рекомендаций по использованию нескольких бит в заголовках IP-пакетов сказалась на том, что используемые оборудование и программы работают каждый по своей рекомендации. Каждая программа или железка считает своё понимание проблемы единственно верным и действует по своему, субъективному наиболее правильному алгоритму. Администраторы по большей части, если и подозревают о существовании таких скрытых возможностей, вообще с этим полем стараются не связываться без особой на то надобности. Некоторые вообще не видят никакой выгоды от использования этого поля. Всё и так прекрасно работает с их точки зрения.
Маркировка и подсчёт различных видов трафика на практике Насколько мне известно, провайдеров, использующих раздельную тарификацию, мало [2], а использующих маркировку – вообще один – ИМТЦ «МГУ». Помимо маркировки существуют и другие способы решения проблемы раздельной тарификации. Например, упомянутая мной в [2] сеть gagarino.net, как было недавно выяснено, не использует маркировку трафика для абонентов, а использует списки доступа [3]. При этом договорённость с вышестоящим провайдером у них близка по политике к провайдеру Zenon N.S.P., когда не важно, как пришли пакеты, если они из сети, принадлежащей списку [3], то тогда они считаются российскими. Такая система разделения трафика, с точки зрения конечных пользователей наиболее благоприятна, конечно после полностью безлимитного Интернета. Даже не смотря на частичную безлимитность, пользователь, поставивший скачиваться десяток-другой фильмов на ночь из российского сегмента сети, может спать спокойно. Если вдруг на каких-то участках сети нарушится связанность и пакеты начнут приходить из-за рубежа, то на тарификации это не отразится. Подсчёт скачанного трафика в этом случае с помощью iptables осуществляется следующим образом. Создаются цепочки, каждая отвечающая за ту или иную российскую сеть и осуществляющая пометку принадлежащего ей трафика как российского, например семёркой. До прохождения правил весь трафик также метится, допустим шестёркой. Пакет, прошедший через все правила, либо изменит маркировку на российскую, либо так и останется зарубежным. Например, если российскими из всего списка [3] будут только две сети 217.26.176.0/20 и 217.146.192.0/20, а входящим интерфейсом будет eth0, то вышесказанное на языке iptables будет выглядеть так: iptables -A PREROUTING -t mangle -i eth0 -j MARK --set-mark 6 iptables -A PREROUTING -t mangle -i eth0 -s 217.26.176.0/20 ↵ -j MARK --set-mark 7
администрирование iptables -A PREROUTING -t mangle -i eth0 -s 217.146.192.0/20 ↵ -j MARK --set-mark 7
Естественно, цель MARK должна поддерживаться ядром.
пользователю, тут есть своя логика. Несомненно, более правильно при такой схеме дополнительно для удобства пользователей маркировать, например, зарубежный трафик. ИМТЦ «МГУ» маркирует трафик в поле Precedence установкой 1-го и 2-го битов (Flash)(ToS=0x60) [4]. Ðèñóíîê 8. Ìàðêèðîâêà ïëàòíîãî òðàôèêà ÈÌÒÖ "ÌÃÓ" â ïîëå ToS
Ðèñóíîê 7. Ïîääåðæêà öåëè MARK â ÿäðå
После того как пакеты были помечены, их можно уже раздельно учитывать и выполнять над ними различные действия. Например, подсчёт приходящего зарубежного трафика на адрес 192.168.0.5 можно осуществить следующим правилом: iptables -A FORWARD -d 192.168.0.5 -m mark --mark 6
а российского: iptables -A FORWARD -d 192.168.0.5 -m mark --mark 7
эти же самые метки трафика можно скармливать и другим программам вроде tc для задач выборочного ограничения пропускной способности, например: tc filter add dev eth0 handle 6 fw flowid tc filter add dev eth0 handle 7 fw flowid
parent 1:0 prio 0 protocol ip ↵ 1:6 parent 1:0 prio 0 protocol ip ↵ 1:7
С финансовой точки зрения для провайдера данная схема не очень удобна, особенно если пользователей у провайдера тысячи. В этом случае лучше разграничить ответственность таким образом, чтобы проблемы нарушения связанности каких-то участков сети переложить на плечи пользователей. А именно, провайдер не гарантирует идентичность маршрутов доставки трафика от одних узлов к другим и не занимается вопросами минимизации стоимости. Если пакеты от некоторых российских узлов по каким-то причинам начинают приходить с зарубежного интерфейса и их стоимость значительно увеличивается, то в этом случае затраты пользователей растут. Минимизировать затраты один раз и навсегда невозможно. Пользователи, если желают экономить, должны сами ограничивать нежелательный трафик, выделяя его из общего числа косвенными способами, например, отслеживая маршруты движения пакетов с помощью traceroute, либо анализируя запоздавшую статистику уже пришедших пакетов. Такая схема используется некоторыми провайдерами, упомянутыми мной в [2]. Нельзя сказать, чтобы это было совсем нечестно по отношению к конечному
№7(20), июль 2004
Недостатки этого способа в том, что при маркировке трафика провайдером затрачиваются значительные вычислительные ресурсы маршрутизаторов. Возможно, поэтому не все провайдеры используют маркировку, так как просто не могут себе это позволить. Если вы изменили какие-то биты в пакете, то необходимо пересчитать и изменить контрольную сумму. А теперь представьте, что пакетов миллионы, в этом случае не так-то просто сохранить пропускную способность маршрутизатора, промаркировав при этом несколько терабайт пакетов за месяц. Попытка выяснить в ИМТЦ «МГУ», кому и почему пришла в голову идея маркировать трафик так, а не по-другому, результатов не дала. «Так исторически сложилось, – был их ответ, – работавшему на то время администратору маркируемые биты, видимо, показались наиболее правильными, а после его ухода за ненадобностью никто ничего не менял». При маркировке трафика решить проблемы ограничения скачивания чего-то лишнего гораздо проще, чем без таковой. А именно, если поставить скачиваться какой-нибудь фильм на ночь из российской части сети и установить фильтр на запрет прихода любых пакетов из зарубежной части, то можно смело сказать, что вместо скачанных гигабайт в случае изменения маршрутизации вы максимум получите несколько лишних килобайт платного трафика. При подсчёте внешне маркированного трафика с помощью iptables первое, что приходит в голову – это воспользоваться расширением dscp: iptables -A FORWARD -d 192.168.0.5 -m dscp --dscp 0x18
Это правило будет считать все пакеты, у которых установлены два бита маркировки, за исключением тех, у которых почему-то оказались поднятыми и другие биты, кроме отведённых двух. Например, если поле dscp будет содержать значение 19 (биты 01 1001), то формально биты платности будут в таком пакете установлены, но считаться вышеустановленным правилом он уже не будет. Получится, что для подсчёта всех возможных случаев потребуется 16 правил (оставшихся битов 4, 24=16), что не очень-то и удобно, если учесть, что эти правила надо будет создавать на каждый адрес. От того, чтобы не писать так много правил, можно застраховаться. Например, перед проверкой дополнительно потребовать, чтобы пакеты, идущие на правило подсчёта, имели другие биты равными нулю. Сделать это можно с помощью модуля ToS, реализующего более раннюю рекомендацию, биты которого не пересекаются с битами маркировки. Например:
23
администрирование iptables -N user-chain iptables -A FORWARD -d 192.168.0.5 -m tos ↵ --tos Normal-Service -j user-chain iptables -A FORWARD -d 192.168.0.5 -m dscp --dscp 0x18
Однако толку от таких конструкций не будет, так как пакеты с ToS, не равным нулю, и так бы не посчитались последним правилом. Чтобы в правилах, описанных выше появился смысл, их надо изменить таким образом, чтобы во всех приходящих пакетах насильно в поле ToS выставлялось нулевое значение (Normal-service). Например, так:
жения, в которых указано местонахождение исходников ядра и iptables. Если они окажутся пустыми, то нам будут заданы соответствующие вопросы. После ответа на них вам будут предлагаться на выбор различные патчи с их кратким описанием и примерами. Следует выбирать «n» до тех пор, пока у вас на экране не появится запрос на установку u32.
iptables -t mangle -A PREROUTING -j TOS --set-tos 0 iptables -t mangle -A FORWARD -j TOS --set-tos 0
Тогда подсчитывающему правилу: iptables -A FORWARD -d 192.168.0.5 -m dscp --dscp 0x18
не понадобятся никакие другие усложняющие конструкции. Этот метод прост и хорош, и, возможно, даже не потребует перекомпиляции ядра, так как большинство ядер, по умолчанию поставляемых с системами, уже знает про используемые нами модули и осуществляет их поддержку. (Для тех, кто будет компилировать ядро самостоятельно, поддержку DSCP и TOS следует включить, см. рис. 7.) Но в то же время такой способ решения проблемы неудачен, потому что требует очень больших ресурсов. Если пользователь Вася настраивает правила у себя на домашнем компьютере, то скорее всего, как бы он ни старался, у него не получится более 100 правил. Его система заведомо справится со всеми пакетами без потерь и задержек. Но если речь идёт о маршрутизации на уровне небольшого или среднего провайдера, то все ресурсы на счету. Правил может быть больше тысячи, к тому же на сервере могут работать другие задачи. Если в каждом пакете придётся менять какие-то биты, обнулять что-то в поле ToS, то непременно придётся у всех этих пакетов пересчитывать контрольную сумму, что может значительно загрузить систему. Поэтому лучше и красивее использовать другой способ подсчёта. Наложить на ядро патч u32 [6, 7] от patch-omatic, написанный Don Cohen (don@isis.cs3-inc.com). Для этого, как обычно, скачиваем последние версии ядра (http://www.ru.kernel.org/pub/linux/kernel/v2.4/linux2.4.26.tar.gz), patch-o-matic (http://www.iptables.org/files/ patch-o-matic-ng-20040302.tar.bz2) и заодно iptables (http:/ /www.iptables.org/files/iptables-1.2.9.tar.bz2). Затем, распаковав исходники, следует наложить на них патч. Особенность patch-o-matic состоит в том, что предлагаемые патчи, хотя и были написаны отнюдь не дураками, всё же могут содержать неумышленные ошибки. Также никто не гарантирует совместимость всех патчей между собой. Поэтому, чтобы лишний раз не понижать надёжность системы, следует устанавливать только те патчи, которые вам реально необходимы. В нашем случае это u32. Наложение патча несколько отличается от обычного синтаксиса команды patch, поэтому об этом чуть подробнее. Прочитав прилагающийся файл README, можно узнать, что следует запустить файл runme с ключом extra. Затем программа проверит значения переменных окру-
24
Ðèñóíîê 9. Ïðèãëàøåíèå, ïðåäëàãàþùåå óñòàíîâèòü ïàò÷ u32
После этого выбираем «t» (test), и в случае успешного тестирования устанавливаем патч выбором «y». Далее появится предложение установить следующий патч и т. д. Чтобы не выбирать много раз «n», можно сразу выйти с помощью «q» (quit). По окончании вы увидите сообщение о том, что исходники готовы к компиляции.
Ðèñóíîê 10. Ñîîáùåíèå: èñõîäíèêè ãîòîâû ê êîìïèëÿöèè
Далее следует выполнить конфигурацию и компиляцию ядра и модулей обычным образом. Следует обратить внимание, что после наложения патча в разделе Networking options IP: Netfilter Configuration должен появиться пукт «U32 match support», который следует выбрать.
Ðèñóíîê 11. Ïóíêò U32 match support
После установки нового ядра, а также компиляции и инсталляции iptables, можно приступать к написанию правил с использованием нового фильтра u32. К сожалению,
администрирование документация, подготовленная автором модуля (Don Cohen) и поставляемая в комплекте очень слаба, поэтому лучше воспользоваться руководством, написанным William Steams [7]. Краткий формат написания правил для u32 таков:
даемое, хотя и не очень удобна в том плане, что сравниваемое значение приходится окружать большим количеством нулей. Куда разумнее оба сравниваемых значения сдвинуть на 2 байта (16 бит) вправо, тогда лишних нулей можно будет избежать, при этом правило для iptables более элегантно можно будет записать так:
iptables ... -m u32 --u32 "Start&Mask=Range"
где Start – это то, что будет сравниваться, Mask – маска тех битов, которые будут сравниваться, Range – сравниваемое значение, если оно совпадёт с тем что будет в пакете, то правило «сработает». Из-за особенностей реализации u32 в Start указывается не абсолютное значение байта в пакете, начиная с нулевого, а относительное, смещённое на -3 байта от его реального месторасположения. Например, если вы хотите фильтровать пакеты с TTL<4, то глядя на рис. 1, вы определяете, что полю TTL в заголовке соответствует 8-й байт пакета (вначале пакета идёт заголовок). После чего можно подсчитать (8-3=5), что в поле Start следует указать число 5. Для сравнения следует учитывать все биты, поэтому используемая маска будет 1111 1111 (0xFF). Значения меньше 4 – это те, что принадлежат отрезку значений [0, 3]. Полученное таким образом правило будет выглядеть так: iptables ... -m u32 --u32 "5&0xFF=0:3"
при этом оно будет аналогично правилу: iptables ... -m ttl --ttl-lt 4
Так как переменная Start не может быть меньше 0, то получить вышеописанным образом доступ к первым трём байтам не получится. Для доступа к ним используется более сложная конструкция со сдвигом. Подробнее об этом можно прочитать в [7]. Так как маркируемые биты находятся во втором байте заголовка (2-3=-1), то простым образом обратиться к ним не получится. Конструкция Start&Mask позволяет считать первые 4 байта сразу без сдвига с помощью «0&0xFFFFFFFF», конструкция, считывающая второй по счёту (первый по нумерации, начиная с нуля) байт выглядит так «0&0x00FF0000». Фактически это то, что нам нужно. Уже сейчас можно написать аналог применяемого нами ранее правила: iptables -A FORWARD -d 192.168.0.5 -m dscp --dscp 0x18
который будет выглядеть так: iptables -A FORWARD -m u32 --u32 "0&0x00FF0000=0x00600000" ↵ -d 192.168.0.5
Это правило полностью аналогично, то есть оно содержит все те недостатки, что были обсуждены ранее. Однако теперь их можно исправить, используя для сравнения не все биты, а только те, которые нас интересуют 0110000 (0x60). iptables -A FORWARD -m u32 --u32 "0&0x00600000=0x00600000" ↵ -d 192.168.0.5
Такая конструкция полностью делает всё от неё ожи-
№7(20), июль 2004
iptables -A FORWARD -m u32 --u32 "0&0x00600000>>16=0x60" ↵ -d 192.168.0.5
Логично догадаться, что если у вас маркируются другие биты, то поменять значения в правиле не очень сложно. Замечание 1. Вадимом Беркгаутом было обнаружено, что маршрутизаторы Cisco (в частности 7206) помечают не все пакеты в поле DSCP. Почему-то не помечаются пакеты с установленными флагами SYN или FIN и иногда некоторые пакеты, идущие перед последним. Также не всегда помечаются и ICMP-пакеты. Если на счёт SYN- и FIN-пакетов есть некоторые соображения, то по поводу случайной не маркировки некоторых ICMP-пакетов сделать выводы сложно. В частности, есть предположение, что существуют те или иные RFC, которые рекомендуют так поступать. Например, RFC 2873 (TCP Processing of the IPv4 Precedence Field), указывающий, что во время установки соединения поле precedence должно игнорироваться обеими сторонами. Это значит, что Cisco может думать так: «а зачем маркировать пакеты, если поле всё равно игнорируется». Возможно, что это особенности конкретного экземпляра, если кто-то из читателей знает, почему так происходит и как ведут себя другие маршрутизаторы, буду рад услышать ваше мнение. Замечание 2. Если поле ToS (DSСP+ECN по современной трактовке) никак не используется в сети и не обнуляется на межсетевом экране, а при этом есть возможность свободному выходу определённых пакетов из сети (в сеть), то создаётся хорошая лазейка для скрытной передачи информации. Много передать по такому каналу не получится, но можно без особых усилий отправлять небольшие сообщения, не привлекая к происходящему процессу внимания. Спасибо Павлу Янченко за ответы на некоторые мои вопросы по теме данной статьи.
Литература, ссылки: 1. Список документов RFC: http://www.rfc-editor.org/rfcindex.html 2. Закляков П. Пиринг. – Журнал «Системный администратор» №6(19), июнь 2003 г. – 82-87 с. 3. IP-адреса Российских сетей по данным gagariono.net: http://www.gagarino.net/ips.txt 4. ИМТЦ «МГУ» тарифы и маркировка трафика: http:// www.direct.ru/internet/index_line.html 5. Classifying packets with filters: http://www.tldp.org/ HOWTO/Adv-Routing-HOWTO/lartc.qdisc.filters.html 6. Расширение u32 для iptables: http://www.iptables.org/ patch-o-matic/pom-base.html#pom-base-u32 7. William Steams IPTables U32 Match Tutorial: http:// www.sans.org/rr/special/iptables_u32.pdf 8. Страница со ссылками для скачивания iptables и patcho-matic: http://www.iptables.org/downloads.html 9. Сайт проекта sing: http://sourceforge.net/projects/sing
25
администрирование
PostgreSQL: первые шаги Сейчас довольно трудно представить себе более или менее серьезный программный проект, который не использовал бы базу данных для хранения информации. В статье я хочу рассмотреть одну из лучших (на мой взгляд) систем управления базами данных, распространяемых бесплатно – PostgreSQL. Существуют версии этой СУБД как для различных UNIX-систем, так и для Windows. Учитывая, что разрабатывается она прежде всего для работы в среде UNIX, то рассмотрим ее установку, первичную настройку и основные принципы использования на примере ОС FreeBSD.
СЕРГЕЙ СУПРУНОВ Все примеры в данной статье приведены для FreeBSD 5.2 и PostgreSQL 7.4.2, однако в принципиальном отношении все изложенное остается верным и для других актуальных сегодня операционных систем и версий. Статья носит обзорный характер и предназначена прежде всего администраторам, желающим познакомиться с этой СУБД и оценить ее возможности для решения тех или иных задач. Предполагается, что читатель знаком с такими вещами, как реляционная база данных, СУБД, а также с базовыми понятиями и операторами языка SQL. PostgreSQL – это объектно-реляционная система управления базами данных, работающая как клиент-серверная система. Основываясь на базовых понятиях реляционных БД, PostgreSQL поддерживает и ряд «объектных» операций, например наследование. PostgreSQL соответствует базовой спецификации SQL99 и поддерживает большое число возможностей, описанных стандартом SQL92. Может возникнуть вопрос – почему именно PostgreSQL? Лучше всего ответить на него, сравнив этот продукт с не менее популярным и, пожалуй, более известным – MySQL. Моей целью не является доказательство, что один из них лучше другого – это несколько разные продукты, области применения которых во многом пересекаются, но отнюдь не совпадают, и сравнением я хочу определить нишу
26
PostgreSQL, те категории задач, для которых эта СУБД более эффективна. Прежде всего следует отметить различия в организации данных. Если в MySQL каждая таблица заносится в собственный файл (для большинства типов БД), то Postgre SQL организует единую файловую структуру, в которой отдельные файлы не соответствуют непосредственно таблицам или другим объектам базы данных. То есть в MySQL вы можете создать резервную копию лишь части БД, сохранив соответствующие файлы. Для PostgreSQL такой номер пройдет только для всей структуры, включающей в себя все обслуживаемые сервером БД базы. Как следствие – MySQL полагается при организации БД на файловую систему ОС, в то время как PostgreSQL зависит от нее меньше, но требует дополнительного обслуживания – периодической дефрагментации базы данных командой VACUUM. Далее эти две СУБД используют различную реализацию нескольких подключений. PostgreSQL имеет более широкие возможности, как для обработки данных, так и для администрирования, но это, в свою очередь, несколько повышает сложность работы с этой СУБД. Наиболее характерные различия сведены в таблицу, после которой приведены некоторые комментарии-выводы.
администрирование раторов и т. д. Что, впрочем, не мешает использовать эту СУБД и для веб-приложений, например, форума или галереи изображений.
Типовая установка
Существуют также различия в реализации некоторых SQL-функций (PostgreSQL лучше соответствует стандартам), в наборе типов данных и т. д. Как видно из таблицы, разработчики MySQL делают акцент на наилучшую скорость чтения (выборки) данных, чем и объясняется популярность этой СУБД в среде веб-разработчиков, где выборка – основная операция. Достигается это отсутствием транзакций (они реализованы только для некоторых типов таблиц, например InnoDB, BerkleyDB) и многопоточной работой, однако это же и является причиной несколько меньшей надежности данной СУБД. В плане прав доступа MySQL несколько превосходит PostgreSQL, позволяя задавать права доступа не только на уровне таблицы, но и на уровне столбца, однако в PostgreSQL это с лихвой компенсируется возможностью создавать пользовательские представления («view»). Наиболее острый вопрос, вызывающий нескончаемые споры, – сравнение скоростных характеристик этих двух пакетов. Результаты тестов, доступные в Интернете, весьма противоречивы, что говорит об отсутствии явного лидерства той или иной СУБД. Кроме того, результаты будут сильно зависеть от конкретной реализации теста, преобладания тех или иных операций и нагрузки на систему. В одних случаях первенство будет за MySQL, в других – за PostgreSQL. Также PostgreSQL часто сравнивают с коммерческой СУБД Oracle. Oracle несколько превосходит PostgreSQL в таких вопросах, как использование индексов, репликация и восстановление данных, да и вообще инструменты администрирования Oracle более развиты (но вместе с тем и более сложны). С другой стороны, PostgreSQL предоставляет возможность использовать в качестве процедурного языка помимо PL/pgSQL (очень схожего с PL/SQL, используемым в Oralce) также PL/Perl, PL/Python, PL/Tcl, что позволяет разработчику выбрать более привычный инструмент. Таким образом, можно сказать, что место PostgreSQL – базы данных, требующие высокой степени надежности хранения информации, предъявляющие повышенные требования к проверке всех изменений, имеющие необходимость в автоматической корректировке большого числа данных при изменении информации в одной из таблиц, а также задачи, где требуется возможность разработки нетривиальных решений, использование нестандартных опе-
№7(20), июль 2004
Для установки PostgreSQL во FreeBSD лучше всего воспользоваться системой портов (/usr/ports/databases/ postgresql7 или другая актуальная версия). Однако в данном случае установка по умолчанию, скорее всего, вас не устроит, поскольку в ней отсутствует поддержка расширенных кодировок, а следовательно, операции сортировки данных, хранимых в кириллической кодировке (например, KOI8-r), будут работать неправильно. Прежде всего нужно собрать PostgreSQL с поддержкой расширенных кодировок. Для этого добавьте в файл Makefile, размещенный в директории порта, следующие строки после первого упоминания переменной CONFIGURE_ARGS: CONFIGURE_ARGS+= --enable-locale --enable-multibyte=KOI8 ↵ --enable-recode
Параметр --enable-locale необходим для поддержки специфических для страны настроек (так называемой локали), --enable-recode включает поддержку перекодировки (данный параметр для описываемого варианта, в котором везде используется только KOI8-r, необязателен, но зачастую может быть весьма полезен). Параметр --enablemultibyte собственно и включает поддержку кодировки KOI8-r. Если вы предпочитаете работать с другой кодовой таблицей, укажите ее в качестве аргумента. Параметр, заданный без аргумента, включит поддержку всех мультибайтовых кодировок. Таким же образом вы можете добавить другие нужные вам опции или изменить поведение СУБД по умолчанию. Например, изменить номер порта, который PostgreSQL будет прослушивать в ожидании запросов, можно с помощью параметра --with-pgport=NUM (по умолчанию – 5432). А ключ --with-perl позволит в дальнейшем использовать процедурный язык PL/Perl для разработки хранимых процедур в дополнение к PL/pgSQL. Для получения информации по всем ключам запустите configure -help. После того как в Makefile внесены все нужные параметры конфигурации, установка выполняется как обычно: # make && make install
В ряде случаев может оказаться полезным выполнить регрессионное тестирование, которое позволит убедиться в полной работоспособности откомпилированных программ и выявить возможные проблемы еще до их инсталляции. В этом случае сборку системы нужно выполнить со следующей опцией: # make –DWITH_TESTS
И затем, убедившись в успешности тестирования, выполнить установку: # make install
27
администрирование Если ваша система не поддерживает коллекцию портов или вы предпочитаете ставить все вручную, скачав предварительно архив с исходниками, то после распаковки вам нужно будет выполнить конфигурацию с приведенными выше параметрами: ./configure --enable-locale --enable-multibyte=KOI8 ↵ --enable-recode .
Ну и любителям прекомпилированных RPM-пакетов нужно будет найти пакет, собранный с требуемыми опциями. Теперь для инициализации базы данных нужно выполнить следующую команду: # su –l pgsql –c “initdb –E KOI8”
Обратите внимание, что инициализацию БД нужно выполнять с правами того пользователя, от имени которого процесс будет в дальнейшем запускаться, и это не должен быть root. Если вы ставили PostgreSQL из коллекции портов, то для этих целей автоматически будет создан пользователь pgsql, чье имя мы и указываем в команде su как аргумент ключа –l. Если же вы собирали систему самостоятельно, то этого пользователя вам придется создать вручную и позаботиться о правах доступа к нужным исполнимым файлам, библиотекам и каталогам данных. Обратите внимание на ключ –E команды initdb. Он позволяет задать кодировку по умолчанию, с которой будет создана база данных. Поддержка указываемой как аргумент кодировки должна быть включена во время сборки пакета. В процессе инициализации в папке /usr/local/pgsql будет создан каталог data со структурами двух БД – template0 и template1. Изменить путь к БД можно с помощью ключа –D команды initdb: # su –l pgsql –c “initdb –E KOI8 –D /var/databases/postgres/data”
По умолчанию для FreeBSD установка исполнимых файлов происходит в папку /usr/local/bin. Если вы изменили это и новый путь не включен в переменную PATH, то потребуется указать полный путь к файлу initdb. По окончании инициализации вам будет предложено два способа запуска процесса postmaster, который является сервером СУБД: $ /usr/local/bin/postmaster –D /usr/local/pgsql/data
или $ /usr/local/bin/pg_ctl –D /usr/local/pgsql/data –l logfile start
В первом случае запускается непосредственно процесс postmaster. Причем он будет выполняться в интерактивном режиме, то есть все его сообщения будут выдаваться на терминал запустившего его пользователя. Прерывание процесса приведет к остановке сервера СУБД (под сервером здесь понимается процесс postmaster, а не сам компьютер). Такой режим работы может быть удобен на стадии от-
28
ладки, но для реальной работы он явно неудобен. Можно, конечно, запустить его в фоновом режиме, указав в конце команды знак амперсанда (&), но мне больше нравится управлять процессом postmaster с помощью специально для этого разработанной утилиты pg_ctl. В обоих вариантах ключ –D позволяет задать каталог, в котором хранятся данные. Каталог должен быть инициализирован командой initdb, как описывалось ранее. В принципе ничто не мешает инициализировать несколько каталогов данных и запустить одновременно несколько процессов postmaster (при этом также нужно позаботиться о том, чтобы каждый из них прослушивал свой порт). Один процесс может одновременно обслуживать только один каталог данных. Теперь для проверки, что все работает так, как ожидается, присоединитесь к базе template1 следующей командой: $ psql –U pgsql template1
Если вы получили приветствие Интерактивного Терминала PostgreSQL, значит, БД проинициализировалась и сервер запущен правильно. А если это сообщение еще и на русском языке, то вообще замечательно. Если же нет, то убедитесь, что переменная окружения LANG соответствует выбранной кодировке:
В случае возникновения проблем проанализируйте полученные сообщения об ошибках, поступающие на экран и в журнальные файлы (в зависимости от характера ошибки это могут быть файлы /var/log/pgsql, /var/log/ messages и т. п.). Убедитесь, что вы запускаете PostgreSQL от имени созданного ранее пользователя pgsql (впрочем, имя может быть иным, например postgres). Запустите процесс postmaster в интерактивном режиме, как было описано выше. Если анализ диагностических сообщений не позволяет выявить источник проблем, может помочь возврат на стадию инсталляции и выполнение регрессионных тестов. Итак, мы подключились к БД, интерактивный клиент общается с нами на нашем родном (или, по крайней мере, на очень знакомом) языке. Еще одна вещь, которую желательно дополнительно проверить, – кодировка, с которой БД была инициализирована. Для этого в терминале выполните команду \l, которая выводит список всех БД:
администрирование Поскольку у нас все работает, как и ожидалось, то теперь можно позаботиться об автоматической загрузке сервера PostgreSQL при старте системы. Если вы ставили СУБД из коллекции портов, то беспокоиться вам не о чем – в папке /usr/local/etc/rc.d уже есть файл 010.pgsql.sh, который и будет отвечать за запуск и останов сервера СУБД. Если же установка выполнялась вручную, то вам нужно будет перенести в соответствующий каталог (/etc/ rc.d, /usr/local/etc/rc.d) сценарий автозапуска, соответствующий вашей операционной системе, из подкаталога contrib/start-scripts той папки, куда был распакован дистрибутив. Что еще важно иметь в виду: пользователь pgsql является администратором СУБД, то есть его полномочия по отношению к базам PostgreSQL практически ничем не ограничены. Кроме того, настоятельно не рекомендуется использовать базы template0 и template1 для работы. Поэтому первое, что нужно сейчас сделать, это создать новую базу данных и нового пользователя, который будет являться владельцем этой БД. Для выполнения этих операций, как и всех остальных, будем использовать терминал psql:
хоста localhost без ввода пароля, поскольку этот хост определен как доверенный (trust). Если вы – единственный пользователь, имеющий терминальный доступ к серверу (например, PostgreSQL установлена на вашей машине для изучения), то это можно не менять. Но поскольку никогда нельзя быть уверенным, что злоумышленник не получит доступ к командной оболочке, то лучше не пренебрегать дополнительной защитой. Поэтому имеет смысл ужесточить права доступа к данным. Но об этом речь пойдет чуть ниже, а пока – небольшой раздел для тех, кто практически не имеет опыта работы с базами данных.
Пример простой базы данных (основам SQL посвящается…) Решим простейшую задачу: создадим телефонный справочник, который технически будет реализован как одна таблица с тремя полями (ФИО, должность, телефон). Вся работа будет показана как листинг одного сеанса. Создаем таблицу с полями person, post и phone:
Просмотр списка таблиц в базе:
Теперь у нас есть база данных, с которой в дальнейшем и будем экспериментировать. Обратите внимание, что нового пользователя мы создавали с опциями nocreatedb и nocreateuser, то есть он не будет иметь прав на создание баз и пользователей. В дальнейшем следует подключаться под этим пользователем к вновь созданной базе test: $ psql –U test test
Чтобы не указывать имя пользователя и базу каждый раз при вызове терминала, вместо базы test и пользователя test создайте базу и пользователя с именем, совпадающим с вашим системным именем. В этом случае запуск клиента можно осуществлять просто командой psql. Хотя это и достаточно очевидно, все же следует указать, что пользователи СУБД и системные пользователи – не одно и то же. Наверняка вас удивило, что при подключении к БД не указывается пароль. Действительно, настройка PostgreSQL по умолчанию позволяет пользователям подключаться с
№7(20), июль 2004
Просмотр структуры таблицы:
Добавляем запись:
Смотрим, что получилось:
Добавляем еще одну запись. Как видно, после имени таблицы не обязательно перечислять заполняемые поля. В этом случае поля будут заполняться значениями по порядку:
29
администрирование Выходим:
Команда \h позволяет получить подсказку по любой SQL-команде:
Так можно просмотреть все команды интерактивной оболочки (здесь приведены наиболее часто используемые):
Вот так мы познакомились с интерактивным терминалом и убедились, что с PostgreSQL вполне можно общаться на языке SQL. Обратите внимание на синтаксис приглашения. Оно состоит из имени базы данных (в нашем случае – test). Далее следует символ «=», если редактируется новая строка; «--», если продолжается ввод команды, начатый на предыдущей строке; или «(», если продолжается ввод и в предложении имеется незакрытая скобка. Завершает приглашение символ «#», если текущий пользователь является владельцем подключенной БД, или «>» в противном случае. Каждая вводимая SQL-команда должна завершаться точкой с запятой. Для команд терминала это не требуется.
Специфика PostgreSQL
Просматривать можно не всю таблицу, а только ее часть, для чего условие выборки следует указать в предложении where:
Изменим поле записи. Обратите внимание, что если не указать условие where, то будут изменены соответствующие поля всех записей:
Удалим одну из записей. Отсутствие предложения where очистит всю таблицу:
Наигравшись, удаляем таблицу phbook.
30
Теперь более подробно рассмотрим конкретные особенности PostgreSQL. Было бы жестоко пытаться втиснуть весь материал в одну журнальную статью, поэтому этот раздел будет носить скорее обзорный характер. А наиболее интересным особенностям постараюсь посвятить несколько отдельных статей. Сперва – о типах данных. Помимо стандартных, соответствующих SLQ99 и SQL92 (numeric, char, varchar, bool и т. д.), поддерживается и ряд специфических, например, геометрические типы данных (point, line, box, circle, polygon), сетевые типы (cidr – спецификация сети IP, inet – IP-адрес, macaddr – MAC-адрес) и т. д. Конечно, дополнительные типы данных не являются незаменимыми, но в ряде случаев существенно упрощают логику и структуру базы. Например, геометрические типы могут оказаться весьма полезными при разработке картографических приложений. Особо следует отметить специальный тип данных serial. Он описывает поле типа integer, значения которого задаются автоматически создаваемой последовательностью (см. далее) и так же автоматически индексируются. То есть это, по сути, аналог ограничения AUTO_INCREMENT в MySQL. Будучи объектно-реляционной СУБД, PostgreSQL поддерживает и составные типы данных – массивы. Чтобы определить поле как массив, его тип дополняется парой квадратных скобок (например, «char(10)[]»). Массив (как константа) записывается с помощью фигурных скобок: «’{1,2,3}’», «’{“один”, “два”, “три”, “четыре”}’». Обращение к элементу массива, как и в других языках программирования, выполняется по индексу: arra[2]. Пример:
администрирование
Как видно, первый элемент массива имеет индекс «1». С точки зрения PostgreSQL массив представляет собой специально форматированную строку, и именно этим обусловлена необходимость заключать запись массива в апострофы независимо от типа его элементов. Для массива строк каждый элемент также должен быть заключен в двойные кавычки. Очевидно, что скорость обработки массивов оставляет желать лучшего, однако в ряде случаев возможность работать с этим типом может оказаться весьма удобной. Теперь про обещанные ранее последовательности. Последовательность (sequence) представляет собой счетчик, то есть автоматически увеличивающееся целое число. При вызове специальной функции nextval(‘seq_name’), где seq_name – имя последовательности, значение последовательности увеличивается на шаг, заданный при ее создании (по умолчанию – 1), и возвращается полученное значение. Прочитать это значение еще раз без увеличения последовательности можно с помощью функции currval(‘seq_name’). Следующий листинг демонстрирует основные принципы работы с последовательностью:
PostgreSQL имеет довольно развитые возможности работы с индексами. Создаются они командой CREATE INDEX, синтаксис которой можно посмотреть в терминале командой «\h create index». В ряде случаев индексы создаются автоматически (например, для полей с ограничением PRIMARY KEY). Созданные пользователем индексы можно просмотреть командой \di. Поддерживается три типа индексов: B-tree (по умолчанию), R-tree и хэш. В большинстве случаев следует использовать B-деревья, хотя иногда (например, при операциях с геометрическими типами данных) R-tree-индекс может показать лучшие результаты. В любом случае выбор остается за пользователем. Модифицировать таблицы можно с помощью команды ALTER TABLE. Однако нужно заметить, что она не позволяет удалять столбцы или менять их тип, что связано с особенностями организации базы данных. В большинстве случаев достаточно переименовать ненужный столбец во что-нибудь типа «deleted» и забыть про него. Если же очень хочется удалить его физически, то воспользуйтесь командой CREATE TABLE AS, которая создаст новую таблицу на базе существующей (или даже нескольких, поскольку источником данных может быть любой запрос). Кроме того, всегда можно воспользоваться «универсальным алгоритмом»: ! CREATE TABLE newtable .. – создаем новую таблицу с нужной структурой; ! INSERT INTO newtable SELECT .. FROM oldtable – вставляем в новую таблицу нужные поля записей из старой; ! DROP TABLE oldtable – удаляем старую таблицу; ! ALTER TABLE newtable RENAME TO oldtable – переименовываем новую. Очень интересная возможность, имеющаяся в Postgre SQL, – наследование таблиц. Если таблица создается как производная (дочерняя) от некоторой базовой (родительской), то она наследует структуру последней, то есть в ней, помимо собственных полей, доступны и поля родительской таблицы, а записи дочерней можно прочитать из родительской. В качестве примера рассмотрим такую зада-
№7(20), июль 2004
31
администрирование чу: требуется БД для учета имеющихся на предприятии компьютеров (IP-адрес машины, место установки, операционная система), при этом для серверов дополнительно требуется указать работающие на них службы (WWW, FTP, DNS и т. д.). В классической реляционной СУБД это решалось бы созданием отдельной таблицы для хранения информации по службам и привязкой ее к таблице машин по IP либо по дополнительному полю-идентификатору. Объектные возможности PostgreSQL позволяют решить указанную задачу следующим образом: Создаем родительскую таблицу (как обычно):
Добавляем в нее запись для рабочей станции (все операции – обычные):
Создаем дочернюю таблицу (предложение inherits с указанием родительских таблиц):
Хотя таблица servers создавалась с одним полем, три других она унаследовала от родительской:
В родительской таблице видим записи и из родительской, и из дочерней, но без учета полей, заданных только в дочерней:
В дочерней – только те, которые заносились непосредственно в нее:
Ключевое слово ONLY позволяет выбрать записи из родительской таблицы без учета содержимого дочерней:
Как видите, наследование позволяет создавать вполне логичные и удобные для работы структуры. Следует также заметить, что удалить базовую таблицу можно толь-
32
ко со всеми производными (указав ключевое слово CASCADE в команде DROP TABLE). PostgreSQL позволяет создавать собственные функции, которые сохраняются в базе данных и могут быть использованы при модификации полей записи, в операциях выборки по условию, как триггеры, и т. д. Разработчику, помимо процедурного языка PL/pgSQL (являющегося аналогом PL/SQL в СУБД Oracle), доступны PL/Perl, PL/Python и др. Для этого PostgreSQL должна собираться с соответствующими опциями. Те, кому доводилось работать с Oracle, знакомы с такой удобной возможностью для автоматизации операций с БД, как триггеры. Спешу их обрадовать – PostgreSQL также поддерживает триггеры в полном объеме. Триггер связывает функцию с операцией модификации таблицы. Например, с его помощью можно проверять правильность вводимых данных или при удалении одной записи автоматически удалять и все, логически связанные с удаляемой (или соответствующим образом модифицировать). В данной статье я не буду останавливаться на описании работы с функциями и триггерами, поскольку это довольно обширная тема. Следующая особенность СУБД – возможность создавать представления пользователя (view), которые представляют собой результаты выборки (запроса), доступные для непосредственного обращения к ним в будущем без пересылки повторных запросов. Помимо упрощения логики работы с БД (когда вместо сложных запросов к нескольким таблицам можно отправить весьма простой запрос к представлению пользователя), эта возможность позволяет гибко управлять доступностью данных для различных пользователей. Например, если в БД есть таблица, содержащая имена и пароли клиентов, но оператор должен получить доступ только к именам, то достаточно создать представление, в которое выбрать поле «Имя», и дать оператору доступ к нему, а возможность чтения самой таблицы исключить. Ну и напоследок несколько слов о транзакциях. Этот механизм позволяет выполнять несколько операций по модификации БД единым блоком, что гарантирует целостность данных. Например, если при оформлении покупки через интернет-магазин нужно занести запись о покупке и одновременно уменьшить количество соответствующих товаров на складе, то в результате сбоя может сложиться ситуация, когда запись о покупке будет сделана, а товар – не зарезервирован. В ходе же транзакции изменения в базу данных записываются только в случае успешности всех операций. Транзакционный блок открывается командой BEGIN (не забывайте завершать эту команду, как и все остальные, точкой с запятой). Все изменения, выполняемые в блоке, не отражаются на базе данных, пока не поступит команда COMMIT, по которой выполняется запись всех сделанных изменений. Отменить транзакцию (например, в случае выявления ошибки) позволяет команда ROLLBACK. Если вы недостаточно хорошо представляете, как это работает, попробуйте поработать с таблицами внутри транзакционного блока, анализируя изменения, происходящие при подтверждении транзакции (COMMIT) и ее откате (ROLLBACK).
администрирование Обратите внимание, что такие операции, как удаление таблицы, внутри транзакции выполнены быть не могут.
Вопросы администрирования В начале статьи упоминалось, что локальные пользователи компьютера, на котором запущен сервер postmaster, могут подключаться к БД под именем любого пользователя без указания пароля. Связано это с тем, что хост localhost по умолчанию является доверенным. Изменить это можно в файле pg_hba.conf, находящемся в каталоге data, инициированном командой initdb. Синтаксис этого файла хорошо прокомментирован в нем самом. Поле TYPE содержит тип записи, который может быть одним из следующих: local (соединение, устанавливаемое с того же компьютера, на котором работает сервер СУБД; использует сокеты UNIX), host (соединение TCP/IP) или hostssl (защищенное соединение TCP/IP с использованием протокола SSL). Поле METHOD может содержать следующие записи: ! trust – доверенный хост, подключения не требуют пароля; ! reject – отклонить соединение; ! password – требовать соответствия пароля (передается в открытом виде); ! crypt – требовать соответствия пароля (пароль шифруется); ! krb4, krb5 – аутентификация Kerberos; ! ident – аутентификация по карте соответствия имени пользователя PostgreSQL системному имени пользователя. Карты соответствия содержатся в файле pg_ident.conf. Назначение остальных полей файла pg_hba.conf пояснений, думаю, не требует. При запросе соединения проверка условий выполняется с начала файла до обнаружения соответствия, после чего выполняется действие, заданное полем METHOD. Если соответствие не будет найдено, соединение не будет установлено. Следует заметить, что после внесения изменений в этот файл требуется перезапустить процесс postmaster, послав ему сигнал SIGHUP, либо перезагрузить сервер СУБД командой: # pg_ctl reload
Чтобы сервер мог обслуживать подключения по протоколу TCP/IP, процесс postmaster должен быть запущен с ключом –i. Для SSL-соединений также требуется ключ –l, и, кроме того, СУБД должна быть собрана с поддержкой SSL (ключ --with-openssl). Управление базой данных может осуществляться с помощью SQL-команд. Ряд функций может быть выполнен внешними программами. Ниже представлена таблица, описывающая некоторые полезные команды:
Некоторого пояснения требуют команды назначения и отмены привилегий (GRANT и REVOKE). Команда GRANT позволяет давать пользователю или группе следующие права (перечислены только основные):
Аналогично, команда REVOKE ограничивает соответствующие права. В качестве объекта, для работы с которым назначаются права, могут выступать таблицы, представления пользователя и последовательности. Посмотреть назначенные привилегии доступа позволяет команда терминала \z. При необходимости программного доступа к информации о пользователях и привилегиях весьма полезны служебные таблицы pg_shadow (таблица пользователей) и pg_class (права доступа к объектам БД). Для тех, кто клавиатуре предпочитает мышь, разработан пакет phpPgAdmin (аналог phpMyAdmin для MySQL), позволяющий работать с СУБД через веб-интерфейс. Для FreeBSD найти его можно в коллекции портов: /usr/ports/ databases/phppgadmin. Также существует мультиплатформенная графическая оболочка pgAdmin (www.pgadmin.org/pgadmin3), обеспечивающая доступ ко всем функциям администрирования и разработки.
Заключение Итак, были рассмотрены основные особенности СУБД PostgreSQL. Описание получилось несколько расплывчатым и отрывочным, но надеюсь, эта статья все же позволит вам оценить возможности этой системы управления базами данных и принять принципиальное решение о ее использовании (или неиспользовании) для решения стоящих перед вами задач.
Дополнительная информация:
! Страницы справочного руководства man (см. каталог ! !
№7(20), июль 2004
/usr/local/pgsql/man/man1 (программы «обвязки») и man7 (справки по командам SQL)); Встроенная справка интерактивного терминала (\h – справочник по SQL-командам, \? – справочник по командам терминала); Официальный сайт проекта – www.postgresql.org, один из русскоязычных сайтов – www.linuxshare.ru/docs/ software/SQL/postgresql)
33
администрирование
УПРОЩАЕМ СЕБЕ ЖИЗНЬ С WEBMIN
СЕРГЕЙ ЯРЕМЧУК У UNIX-подобных операционных систем много положительных сторон: безопасность, стабильность и, что особенно привлекает внимание многих, – бесплатность. Но для начинающих системных администраторов настройка системы может превратиться в настоящий ад. Копание в конфигурационных файлах, постоянное чтение документации, к тому же на не для всех понятном английском языке, может отпугнуть любого, привычного к удобному интерфейсу Windows-систем. Да что там отпугнуть, ошибки в конфигурационных файлах могут привести к серьезным проблемам с безопасностью. Мне часто в письмах приходят просьбы от новичков помочь подобрать дистрибутив для использования в качестве шлюза, но с обязательным наличием веб-интерфейса, сегодня попробуем раз и навсегда решить эту проблему. Webmin (http://www.webmin.com) представляет собой веб-интерфейс для системного администрирования UNIX, использование которого существенно упрощает процесс управления системой. Кроме того, он будет полезен для удаленного управления системой тем администраторам,
34
у которых нет в данный момент доступа к компьютеру с установленной UNIX-подобной системой, а стандартные сетевые средства Windows не блещут особым выбором необходимых утилит. В этом случае, используя любой веббраузер, поддерживающий таблицы и формы (и JAVA для некоторых модулей), можно без затруднений в наглядной форме с локального или удаленного компьютера настроить пользовательские аккаунты, установить/удалить программы, настроить Web, mail, DNS-сервер, сервер печати, управлять базами данных, организовать совместное использование файлов, применяя Samba или NFS, настроить firewall, синхронизировать время, организовать совместный доступ в Интернет, мониторинг состояния сервисов, осуществить резервное копирование, записать CDдиск, получить доступ к файлам на удаленном компьютере, в том числе документации и прочие бесчисленные задачи администрирования, коих не счесть, но требующих определенных умений. При этом Webmin избавит вас от синтаксических ошибок и неточностей, как сможет предупредит о потенциально опасных действиях. Но это, прав-
администрирование да, не значит, что совсем не обязательно все знать, и умная утилита сделает все сама. Иметь некоторые представления о строении UNIX, значениях тех или иных утилит, особенностях работы протоколов и сервисов все-таки необходимо. Опытным администраторам также не стоит игнорировать наличие такой удобной утилиты, ведь помнить все параметры в конфигурационных файлах и тонкости настройки сервисов на различных системах, согласитесь, довольно тяжело. Написанный Джейми Камероном (Jamie Cameron) на языке Perl, Webmin имеет модульную структуру, позволяющую свободно наращивать требуемую функциональность или, наоборот, убрать все ненужное. В настоящее время доступно около 200 модулей как входящих в стандартную поставку, так и доступных для загрузки отдельно. Дополнительно ко всему интерфейс переведен на 32 языка, среди которых имеется и русский. На странице http://www.webmin.com/support.html найдете список из 35 UNIX-подобных операционных систем (в том числе и Cygwin, Mac OS X), на которых Webmin без проблем будет работать. Если вашего дистрибутива в нем нет, то отчаиваться не стоит, скорее всего, Webmin удастся заставить работать без трудностей. При этом одним из основных преимуществ Webmin является то, что он фактически подстраивается, в том числе изменяя свой интерфейс и поведение в соответствии с используемой ОС, т.е. фактически скрывает от конечного пользователя различия между всеми вариантами UNIX. Еще одним преимуществом Webmin является возможность делегировать часть полномочий по настройке определенных сервисов другим пользователям. Установить Webmin проще простого: скачиваем архив размером чуть меньше 7 Мб (доступны и прекомпилированные пакеты), распаковываем его, заходим внутрь и даем команду ./setup с указанием каталога, в котором вы хотите видеть установленный Webmin. По умолчанию установка будет произведена в текущий каталог. После чего необходимо будет ответить на ряд вопросов о будущем размещении конфигурационных файлов, нахождении интерпретатора Perl, номере порта, на котором Webmin будет ожидать соединения (по умолчанию 10000), логин и пароль для доступа, автоматический запуск при старте системы.
Еще один момент. Для возможности работы по защищенному каналу (а из внешней сети работать нужно только так, да и во внутренней не будет лишней подстраховка) требуется наличие модуля Net::SSLeay и OpenSSL (http:// www.openssl.org/). Поэтому при появлении сообщения
загрузите модуль с CPAN и повторите установку, за дополнительной информацией обращайтесь по адресу: http:/ /www.webmin.com/ssl.html. Иначе в строке веб-браузера набирайте http вместо https. Кстати, удалить Webmin также просто. Запускаем скрипт /etc/webmin/uninstall.sh, и его как не бывало. Дальнейшую настройку можно производить уже через Webmin. Но, например, если вы забыли пароль, то локально его изменить можно при помощи скрипта changepass.pl. #/usr/local/webmin-1.140/changepass.pl /etc/webmin ↵ ïîëüçîâàòåëü íîâûé_ïàðîëü
Также изменить некоторые настройки можно, напрямую редактируя конфигурационные файлы miniserv.conf и config, которые лежат в каталоге с Webmin. В них возможно изменить значение сетевого порта, включить/выключить SSL (опция ssl=0/1), изменить месторасположение рабочих файлов, установить другой язык интерфейса (для русского значение lang должно быть ru_RU (для кодировки СР-1251) или ru_SU (koi8-r)). Если планируете часто заходить с компьютеров, где установлена ОС Windows, выберите лучше вариант с СР-1251, тогда будете реже перенастраивать кодировку в браузере. После изменений перезапустите webmin. # /etc/webmin/stop && /etc/webmin/start
Но все настройки, повторяю, теперь доступны через веббраузер. Набираем в строке браузера https://hostname:10000, авторизируемся (рис. 1):
В большинстве случаев можно со всем соглашаться и нажимать Enter. Если программа установки что-либо сама не найдет, то тогда в другой консоли при помощи whereis самостоятельно находим и прописываем путь вручную. После чего запустится свой мини-веб-сервер и будет выдана подсказка, по которой можно найти в сети webmin.
№7(20), июль 2004
Ðèñóíîê 1
После чего попадаем в главное окно программы (рис. 2).
35
администрирование
Ðèñóíîê 2
Как видите, вполне привычная большинству пользователей рабочая обстановка. Небольшое примечание: если Webmin уже был установлен в системе, то для входа вместо admin придется воспользоватья root (нужного пользователя можно посмотреть в файле webmin.acl, он идет первым, например, admin), но затем заведите лучше отдельного пользователя для работы с Webmin. Также в некоторых дистрибутивах может быть выставлен другой порт, используемый Webmin, иногда при выборе его значения следует учитывать, что некоторые провайдеры не любят пропускать пакеты на порты с адресами выше 1024, и, например, если не используется защищенный веб-доступ, то вполне подойдет порт 443, также и проследите, чтобы нужный порт был открыт firewall. А так, зная особенности работы тех или иных сервисов, настроить их с Webmin особого труда не составит, просто выбираете нужную категорию (рис. 3) и заполняете или выбираете параметры, к тому же по адресу http:// www.swelltech.com/support/webminguide-1.0/index.html доступна неплохая книга по использованию, которая будет особенно полезна начинающим «The book of Webmin».
Ðèñóíîê 3
Остановлюсь лишь на мерах безопасности и удобстве. Вполне возможно, что после регистрации перед вами предстанет английский интерфейс. Хоть английский и яв-
36
ляется родным языком админа, но, согласитесь, общаться на родном все же приятней. Заходим «Webmin Configuration → Language» и выбираем из выпадающего списка нужный язык, после чего подтверждаем свой выбор нажатием на «Change Language». И возвращаемся теперь уже к пункту «Настройка Webmin». По умолчанию Webmin будет принимать подключения со всех адресов, что не всегда нужно и безопасно, используя пункт «Управление доступом по IP» (IP Access Control), можно ограничить доступ, оставив его только для тех адресов, которым вы доверяете (например, внутренняя сеть), полный доступ можно разрешить, только если вы, например, уезжаете в отпуск и хотите иметь возможность попасть на свой сервер. Здесь возможно задать в качестве параметра сразу сеть или подсеть, отдельные узлы по IPадресу или доменному имени. В последнем случае для подстраховки установите галочку в пункте «Определять IP-адрес по имени при каждом запросе». Для быстроты эту операцию можно проделать и вручную, использовав в файле /etc/webmin/miniserv.conf параметры allow= или deny=. Первый содержит список узлов и компьютеров, перечисленных через пробел, которым позволено соединяться, второй, наоборот, попытки доступа с которых будут игнорироваться. После установки при положительном ответе на вопрос «Start Webmin at boot time» Webmin будет загружаться при старте системы, другим вариантом запуска будет использование для этих целей xinetd. Для этого в файл miniserv.conf добавляем строчку inetd=1 и убираем session=1. После этого пишем в файл /etc/xinetd.conf (в некоторых дистрибутивах вроде Slaskware его сначала создаем командой touch /etc/xinetd.conf): service webmin { user = root env = LANG= port = 10000 socket_type = stream protocol = tcp wait = no disable = no type = UNLISTED server = /usr/local/webmin-1.140/miniserv.pl server_args = /etc/webmin/miniserv.conf }
После чего перезапускаем xinetd. Теперь самое время заняться пользователями. Для начала используем модуль «Аутентификация», который позволяет включить увеличение задержки между неудачными попытками ввода пароля для одного и того же пользователя и блокирование доступа после нескольких неудачных попыток на определенное время, что затруднит попытки подбора пароля в лоб, все подобные действия можно также заносить в журнал. Для забывчивых и ленивых пользователей можно активировать опцию, позволяющую автоматически отключать доступ после определенного интервала времени, и здесь же отключить возможность запоминания введенного пароля. Для улучшения защиты от перебора стоит также установить утилиту logwatch http://www.logwatch.org/, с которой отлично ладит Webmin.
администрирование Следующий полезный модуль «Пользователи Webmin» (Webmin Users) позволит создать пользователя или группу, наделенную определенными правами по работе с теми или иными сервисами. Здесь же можно преобразовать пользователей UNIX в пользователей Webmin с определенными правами, настроить синхронизацию пользователей, при которой вновь созданные пользователи автоматически попадают в одну из групп Webmin, настроить аутентификацию пользователей, просмотреть журналы работы и прервать сеанс пользователя. Webmin может вести журнал событий в стандартном формате журнальных файлов CLF (combined log format), при этом имеет довольно гибкие возможности по регистрации. Так, зайдя в меню «Настройка Webmin» → «Журнал» можно указать, какие IP-адреса и имена узлов будут заноситься в журнал, действия каких пользователей будут регистрироваться, а также как часто журнал будет очищаться. Записи журнала будут заноситься в файл /var/ webmin/miniserv.log. Кроме того, при активации опции «Заносить изменения, произведенные в файлах при каждом действии» также будут вестись подробные записи действий пользователей в файл /var/webmin/webmin.log. Этот журнал может быть просмотрен и проанализирован с помощью модуля «Журнал действий Webmin» (Webmin Actions Log). Если возможностей Webmin не хватает или соскучились по командной строке, то во вкладке «Прочее» (Others) имеются несколько специальных пунктов. «Вход по SSH/ Telnet» (SSH/Telnet Login), который по умолчанию пытается подключиться к 23 порту сервиса telnet, чтобы изменить поведение, нажимаем вкладку «Настройка модуля» и в строке «Тип соединения» выбираем Secure Shell, здесь же можно выставить количество строк, другой узел для работы и использование отдельного окна. Так что Webmin может пригодиться для того, чтобы попасть на свои компьютеры, используя SSH в местах, где не так просто найти необходимые приложения (даже из интернет-кафе), для этого достаточно запустить его на одной из своих машин. Далее во вкладке «Прочие» обнаруживается и интерфейс к командному интерпретатору «Командная оболочка (shell)» (Command Shell), позволяющий удаленно выполнять команды оболочки и поддерживающий историю команд. Следующий пункт «Команды пользователя» (Custom Commands) позволяет неподготовленным пользователям выполнять команды или скрипты нажатием одной кнопки. Админу достаточно создать команду, заполнив соответствующие поля (прописать комманду, задать параметры пользователя, от имени которого будет выполняться команда, добавить описание и пр.). Здесь же во вкладке «Прочее» найдете и менеджер файлов (рис. 4) (требует подержки JAVA), который позволит получить доступ к файлам на удаленном компьютере в удобной графической оболочке. Модульность Webmin позволяет нарастить функциональность до нужного уровня, а с другой стороны, убрать лишнее, чтобы не мозолило глаза и не отвлекало от рабо-
№7(20), июль 2004
ты. Некоторые модули доступны с главного сайта, большую коллекцию модулей найдете по адресу http://www.thirdparty modules.com/webmin. В первую очередь хотелось бы отметить модуль Usermin (http://www.usermin.com), написанный также Джейми Камероном. В отличие от Webmin, нацеленного на администратора, Usermin ориентирован на конечного пользователя, позволяя читать почту, работать с GPG, иметь удаленный доступ к своим файлам, управлять заданиями cron и пр.
Ðèñóíîê 4
Для установки достаточно зайти в раздел «Настройка Usermin» (Usermin Configuration) и нажать на кнопку «Install Usermin tar.gz package». Для работы с модулями (обычно файл с расширением .wbm, хотя возможна установка и rpm-пакетов) заходим в «Модули Webmin» (Webmin Modules), где можно установить новый, указав его местонахождение на локальном диске или в Интернете, удалить модуль, выбрав его из списка, или копировать модуль. Копирование модулей позволяет иметь один и тот же модуль с разными настройками, который можно положить в разные группы для быстрого доступа и разрешить доступ разным пользователям. Из других удобств следует отметить возможность изменения внешнего вида (в одноименной вкладке), позволяющую подобрать цвета по вкусу и более глобально изменить вид, в том числе значки, цвета, фон и расположение страниц темы. Может, кому это покажется и игрушкой, но когда приходится удаленно настраивать работу нескольких узлов одновременно, то разное оформление позволяет меньше путаться. Вот такой он, Webmin. Простой в установке и настройке, полезный в работе, позволяющий администраторам больше сосредоточиться на процессе настройки системы и дающий возможность безопасно работать и получать информацию из тех мест, где нет доступа нужным утилитам. Есть, конечно, и другие утилиты, имеющие аналогичные (почти) возможности, наиболее популярен здесь linuxconf (http://www.solucorp.qc.ca/linuxconf), традиционно входящий в состав RedHat и умеющий работать как в консоли, так и в Х, и позволяющий подключаться через сеть. Но все-таки linuxconf, как мне кажется, менее удобен, особенно для новичка, имеет меньше возможностей и ориентирован в первую очередь на локальное применение.
37
администрирование
FreeBSD TIPS: ARP В «ЗАМОРОЗКЕ»
При работе в локальной сети правила безопасности на сервере чаще всего относятся сразу ко всей подсети. Причем вполне обычная практика – оставлять в адресном пространстве резерв для будущего расширения. Да и невозможно в принципе задать подсеть, содержащую ровно 7 адресов, – все равно придется выделять 14. То есть практически в любой сети есть неиспользуемые адреса, на которые распространяются общие правила доступа. Конечно, любые поползновения извне можно очень эффективно отсекать пакетными фильтрами и прочими изобретениями изворотливого человеческого разума. Но вряд ли найдется организация, в которой системный администратор может свято верить в грамотность, ответственность и порядочность всех без исключения сотрудников, работающих в сети изнутри. А если политика компании включает еще и попытки учитывать и ограничивать трафик с каждого IP-адреса, то нет никаких гарантий, что никому не придет в голову «случайно» прописать в своих настройках адрес соседа…
СЕРГЕЙ СУПРУНОВ Данная заметка о том, как решить указанную выше проблему, если роутер, через который локальная сеть выходит в «мир», построен на базе FreeBSD, а сама сеть включает два-три десятка машин, сосредоточенных в одном сегменте (при большем количестве компьютеров эффективность описываемой методики заметно снижается, хотя она и остается вполне работоспособной). Не могу гарантировать, что на всех версиях этой ОС все будет работать так, как описано, но для FreeBSD 5.2 никаких проблем обнаружено не было. Да, собственно, и взяться им неоткуда, настолько все просто. Сначала немного теории. Для передачи кадра (речь будет идти об Ethernet) другому устройству сетевой адаптер машины должен знать его физический (MAC) адрес. Удаленное устройство будет принимать только кадры, содержащие в заголовке (поле Destination) его адрес, то есть адресованные именно ему. Ну еще широковещательные кадры, адресованные на специальный адрес ff:ff:ff:ff:ff:ff. Но поскольку работа протоколов верхних уровней основана на
38
IP-адресах, то кто-то должен уметь сопоставлять IP-адрес сетевого устройства с MAC-адресом его адаптера. Этим «кем-то» является протокол ARP (address resolution protocol). Собственно говоря, вся работа ARP заключается в том, чтобы по IP-адресу хоста возвратить MAC-адрес его адаптера, который используется для связи с данной машиной. Для этого ARP формирует в памяти компьютера (или другого устройства, на котором он запущен, например, коммутатора) таблицу соответствия, именуемую далее ARPтаблицей. Если требуемый физический адрес в ARP-таблице существует, то все соответствующие кадры направляются на него. Если нет, то отправляется широковещательный фрейм с IP-адресом искомого хоста. Этот фрейм принимают все адаптеры, и если, обработав этот запрос, удаленный хост видит в нем свой интернет-адрес, то он отсылает ответ запросившему устройству, в котором помимо прочего содержится и MAC-адрес его адаптера. Эта информация добавляется в ARP-таблицу для дальнейшего использования.
администрирование Формируемые описанным выше способом, то есть динамически, записи в ARP-таблице сохраняются временно. Если в течение 20 минут (значение по умолчанию) обращения к записи не происходит, она удаляется из таблицы. Помимо динамических записей в ARP-таблице могут быть созданы постоянные (permanent), они же статические, записи. Они хранятся «вечно» (точнее, до перезагрузки), и, кроме того, попытка установить соединение с «чужим» IP-адресом с треском провалится, а в системном журнале появится сообщение об ошибке. К слову сказать, изменение динамической записи также сопровождается соответствующим сообщением, но соединение при этом устанавливается без каких-либо трудностей. Кроме того, если на момент подмены IP-адреса динамической записи с его участием в ARP-таблице не существует, то и никаких разоблачающих сообщений не появится. Управлять записями ARP-таблицы позволяет одноименная утилита arp. Ее полный синтаксис доступен в man arp(8). Нам понадобятся следующие команды: ! arp –a – выводит содержимое таблицы ARP. ! arp <host> – выводит ARP-запись для заданного хоста. ! arp –d <host> – удаляет запись, соответствующую хосту. ! arp –d –a – удаляет все записи таблицы. ! arp –s <host> <MAC-address> – добавляет запись. ! arp –f <file> – добавляет записи из файла соответствия <file>. Таким образом, дальнейшие действия понятны – для всех «критических» (а если сеть небольшая, то просто для всех) IP-адресов можно создать статические записи в ARPтаблице, тем самым исключив их подмену. Для этого существует два пути: ! Использовать команду arp –s <host> <MAC-address> для создания каждой записи; ! Создать файл соответствия и выполнить команду arp –f <file>. Первый способ слишком трудоемкий, поэтому возьмем на вооружение второй вариант. В этом случае файл соответствия требуется создать один раз, и в дальнейшем будет очень легко обеспечить автоматическое заполнение ARP-таблицы статическими записями при перезагрузке компьютера. Формат файла соответствия продемонстрирован на следующем примере:
То есть в каждой строке записывается IP-адрес хоста (либо его каноническое имя) и через пробелы или символы табуляции – соответствующий ему MAC-адрес. Заполнить его достаточно просто – включите все машины сегмента и обратитесь с них к серверу (или с сервера к ним) любым способом, например, запустите на них браузер или выполните обычный пинг. Это обновит ARP-таблицу, после чего достаточно будет сохранить в файле результат выполнения команды «arp –an» и немного его подправить, удалив все лишнее и добавив нужное. Сократить необходимые правки позволит следующая команда:
№7(20), июль 2004
# arp –an | awk –v OFS=”\t” ‘{print(substr($2, 2, ↵ length($2)-2), $4)}’ > ethers
Теперь файл ethers будет заполнен нужным образом. Останется только удалить записи, которые мы хотим оставить динамическими (например, маршрутизаторы CISCO при перезагрузке могут менять MAC-адрес своих интерфейсов, выбирая их из некоторого пула, а добираться среди ночи на работу, чтобы подправить ARP-таблицу – занятие не из приятных). Для адресов, которые не задействованы в подсети, можно создать статические записи с фиктивными физическими адресами (например, 00:11:22:33:44:55). Это исключит возможность использования кем-либо данных IPадресов. Далее очищаем ARP-таблицу и заполняем ее из сформированного файла: # arp –d –a # arp –f /usr/local/etc/ethers
Естественно, имя и местоположение файла соответствия может быть любым – удобным для вас. Теперь осталось занести указанные команды в сценарий автозапуска, например, с именем /usr/local/etc/rc.d/statarp.sh: #!/bin/sh # Static ARP-table loader case $1 in start)
stop)
arp -d -a > /dev/null arp -f /usr/local/etc/ethers > /dev/null echo 'Static ARP-table is loaded' ;;
arp -d -a > /dev/null echo 'Static ARP-table is unloaded' ;; restart) arp -d -a > /dev/null arp -f /usr/local/etc/ethers > /dev/null echo 'Static ARP-table is reloaded' ;; status) arp -an ;; *) echo "Usage: `basename $0` ↵ {start|stop|restart|status}" >&2 ;; esac exit 0
Осталось не забыть сделать данный файл исполняемым. Итак, мы получили статическую таблицу соответствия между IP- и MAC-адресами. Теперь выход в Интернет через FreeBSD-сервер с «чужого» IP-адреса станет невозможным, если, конечно, заодно не подменить и MAC-адрес (с последним явлением можно бороться разве что организационными методами). Но любая палка, как известно, о двух концах. За повышение защищенности сети придется платить дополнительными заботами по администрированию, поскольку теперь добавление новой машины в сеть, замена сетевого адаптера, смена IP-адреса должны сопровождаться правкой и перезагрузкой ARP-таблицы. Хотя это все равно лучше, чем бегать по этажам, «вычисляя» недобросовестного пользователя.
39
администрирование
СТРОИМ ШЛЮЗ С LUINUX
Не всегда и не всем при построении шлюза в Интернете требуются все возможности, заложенные в дистрибутивах вроде Securepoint, о котором шла речь в предыдущем номере журнала. Для домашнего использования или небольшой организации требуется в первую очередь простота настройки. Посмотрим, может быть дистрибутив, о котором сегодня пойдет речь, то, что вам нужно.
СЕРГЕЙ ЯРЕМЧУК Построенный на основе Debian испанский дистрибутив Luinux http://www.masilla.org/luinux/ предназначен как раз для использования в качестве интернет-шлюза (ПК, видеоконсоль, ТВ) в домашних сетях или для небольших организаций. Построен по принципу «установил и используй». Распознает большую часть Ethernet-адаптеров, кроме традиционного для Linux firewall, имеет в своем составе и NIDS Snort, поддерживается возможность легкого создания бекапов в iso, поддержка QoS позволяет расположить по приоритетам важный трафик, поддерживается
40
прозрачное http-кэширование, для удаленной настройки используется SSH, статистику о работе можно получить при помощи веб-интерфейса, возможность создания своего сервера для обмена мгновенными сообщениями, настройка совместного использования файлов, в том числе и NetBios, автоматическая установка времени при помощи NTP (Network Time Protocol). Для работы Luinux требуется по нынешним временам далеко не самый мощный компьютер: процессор 166 МГц+, ОЗУ 64 Mб+ (128 Mб рекомендуется) и жесткий диск не
администрирование менее 1 Гб (SCSI не поддерживаются). И два сетевых адаптера: к первому eth0 подключается провайдер, ко второму eth1 – локальная сеть.
Установка Установка особых проблем вызвать не должна. Загружаем ISO-образ размером 77 Мб, записываем и загружаемся с него. Если BIOS не поддерживает загрузку с CD-ROM, то используйте образ bootflop.img для загрузки с дискеты. Через некоторое время система, пожужжав, выдаст такое сообщение.
Все, как видите, просто. Для установки переходим в каталог /etc/recovery и вводим ./install.sh, после чего скрипт все проделает сам – и диск разобьет, и пакеты распакует. Для работы в режиме expert, когда вы хотите разбить диск по своему усмотрению, запускаем fdisk /dev/hda и затем вводим ./clone-disk.sh. Для восcтановления старой файловой системы может понадобиться ./restore-fs.sh. Система разбивает диск на четыре раздела hda1-4 (все данные при этом будут потеряны), на которые будут примонтированы соответственно swap, корневой, /var и /home, последние три форматируются с файловой системой ext3. После окончания процесса копирования систему можно перезагружать.
Настройка и работа После загрузки заходим в систему как пользователь root с паролем luinux. Первым делом требуется сменить пароли по умолчанию у пользователей root и admin. Вводим соответственно.
настроен на адрес 192.168.1.1, рекомендую его так и оставить, если не хотите потом перестраивать самостоятельно все остальные сервисы. Проверяем, как поднята сеть. #ifconfig
Если утилита выдала информацию о двух сетевых интерфейсах с выставленными IP-адресами, значит можете считать себя счастливчиком и приступать к тестированию сервисов. К сожалению, на сайте разработчика нет списка поддерживаемых адаптеров, поэтому узнать, будет ли работать ваше сетевое оборудование, можно только экспериментальным путем. Как вариант выхода из ситуации можно скомпилировать необходимый модуль в Debian и подгрузить его самостоятельно при помощи insmod. Плюс ко всему можно воспользоваться утилитой apt-get для того, чтобы обновить или доустановить приложения, которые посчитаете нужными. Для того чтобы выставить другую конфигурацию сетевых интерфейсов, которая будет работать и после перезагрузки системы, изменяем значения параметров в файле /etc/network/interfaces, взяв за пример конфигурацию eth1. #mcedit /etc/network/interfaces auto lo iface lo inet loopback #òåïåðü äëÿ eth0 óêàçûâàåì íóæíûé ñòàòè÷åñêèé IP-àäðåñ auto eth0 iface eth0 inet static adress 190.2.34.100 netmask 255.255.255.0
И заносим в файл имена серверов DNS. #mcedit /etc/resolv.conf search nameserver ip_adress_your_provider_primary_dns nameserver ip_adress_your_provider_secondary_dns
И в файле /etc/hostname можно выставить новое имя компьютера, но теперь придется и перенастроить другие сервисы, например, чтобы получать почту (в качестве МТА используется Exim). Для того чтобы найти все применения имени, используемого по умолчанию, – servidor, попробуйте команду: grep -r servidor /etc /var /usr. После чего выполняем: #/etc/init.d/hostname.sh
После чего рекомендую сменить клавиатурную расскладку – установленная по умолчанию несколько неудобна для работы. # cp /usr/share/keymaps/i386/qwerty/ru4.kmap.gz ↵ /etc/console/boottime.kmap.gz #/etc/init.d/keymap.sh reload
Введя в консоли tzconfig, изменяем значение временного пояса. Все сервисы уже запущены и настроены, вам необходимо только проверить настройку сети и их работу. Интерфейс eth0 по умолчанию автоконфигурируется при помощи DHCP, если DHCP провайдером не используется, необходимо будет его настроить вручную, интерфейс eth1
№7(20), июль 2004
И вводим в действие новые настройки: #/etc/init.d/networking restart
После чего еще раз сверяемся при помощи ifconfig. После этого достаточно указать на пользовательских компьютерах IP-адрес в диапазоне с 192.168.1.2 до 192.168.1.254, сетевую маску 255.255.255.0, адрес шлюза 192.168.1.1 и в качестве DNS указанные выше в /etc/resolv.conf адреса (по обещанию разработчика в следующей версии 1.1 будет свой кэширующий DNS-сервер) и можно тестировать работу шлюза и установленных сервисов. Теперь попробуйте погулять по Интернету, Luinux будет кэшировать посещенные странички, затем зайдите в ftp-
41
администрирование архивы, отправьте почту, зайдите на сервер по протоколу SSH. У меня здесь проблем не было, что даже подозрительно. После чего зайдите по адресу: http://192.168.1.1/ stats/ для просмотра статистики работы сервера (рис. 1).
Bittorrent, Gnutella (Bearshare, Limewire и др.), Gnutella2 (Shareaza), Fasttrack (Kazaa, Imesh, Grobster), Soulseek (beta), Direct-Connect (alpha) и Opennap (alpha). На Luinux запускается демон, для настройки и проверки работы которого набираем в строке браузера http://192.168.1.1:4080/ (рис. 2). Для тестирования общедоступного каталога на сервере используем пользователя – edonkey с паролем edonkey. Вся документация по работе и настройке Mldonkey находится в /var/opt/mldonkey/, ограничить максимальную скорость upload/download можно, изменив значения переменных в файле start. Если Mldonkey не нужен, то отключите его. #update-rc.d mldonkey remove
Ðèñóíîê 1
Для сбора статистики в Luinux используется приложение HotSaNIC – «HTML overview to System and Network Information Center» (домашняя страница http://hotsanic.sourceforge.net). Представляющее собой набор скриптов, написанных на Perl и использующих для работы rrdtool http://ee-staff.ethz.ch/ ~oetiker/webtools/rrdtool/. Эти скрипты, основываясь на данных, выдаваемых системными утилитами каждые 10 секунд, просматривают статистику проходящего через интерфейсы трафика, загрузку процессора, памяти, пользователи, данные, полученные о работе жесткого диска, и в результате через 15 минут генерируют графики, по которым можно просмотреть статистику работы системы и нагрузку на отдельные компоненты за последний час, неделю, месяц и год. К сожалению, HotSaNIC не выводит статистику отдельно по каждому клиентскому IP-адресу, но, очевидно, исходя из задач дистрибутива, это и не требовалось, а кому понадобится – на сайте есть документация, рассказывающая, как создать к нему свой модуль. Следующий на очереди проверки – сервис Mldonkey, представляющий собой мультиплатформенный мультисетевой peer-to-peer клиент, распространяемый под лицензией GNU.
И еще одна утилита mkCDrec (http://mkcdrec.ota.be/) позволяет создать загрузочный iso-образ с копией системы, которую в случае неприятностей можно легко восстановить. Поддерживаются файловые системы ext2 , ext3, minix, xfs , jfs, reiserfs, LVM и software RAID и примонтирванные разделы с ФС msdos, fat, vfat и ntfs, сжатие при помощи gzip, bzip2, lzop и других утилит. Создать такой образ легко, только процедура создания потребует наличия 3 Гб свободного места на жестком диске. #cd /var/opt/mkcdrec/ #make clean #make CD-ROM
И затем копируем с другого компьютера. #scp root@192.168.1.1:/tmp/CDrec.iso
Записываем образ на болванку. #cdrecord -v speed=4 dev=0,0 CDrec.iso
В составе имеется также настроенный веб-сервер Apache, который используется для вывода статистики HotSaNIC, можно естественно использовать его и по прямому назначению, дополнительно имеются интерпретаторы perl и php, так что разогнаться есть где. Изменив значения параметров UP и DOWN в файле /etc/init.d/packetfilter, можно изменить значения полосы upload/download для интерфейса eth0, т.е. Интернета. После чего не забудьте перезапустить сервис. #/etc/init.d/packetfilter stop #/etc/init.d/packetfilter start
Ðèñóíîê 2
В данный момент работает с сетями: eDonkey, Overnet,
42
Для работы Samba-сервера отредактируйте файл /etc/ samba/smb.conf под свои нужды (как это сделать, описано в моей статье «Что такое Samba», журнал «Системный администратор», октябрь 2002 г.). После чего сервер Luinux можно использовать еще и в качестве файл-сервера. Вот вкратце и все. Дистрибутив хоть и не обладает суперпродвинутыми возможностями, это не всегда и требуется, но основной конек – простота настройки, что может подойти неподготовленному пользователю, не желающему вникать в тонкости настройки сервисов на UNIX-системах.
администрирование
«СТАЛЬНОЙ ГЛАЗ НА СТРАЖЕ ЖИЗНИ» HA-кластер LifeKeeper компании SteelEye
АНТОН БОРИСОВ Слово «кластер» означает «подмножество объектов с определенными наборами признаков». Кластер – это две или более самостоятельные системы, соединенные в единую систему высокого уровня доступности посредством специального программного и аппаратного обеспечения. Существует четыре основных преимущества использования кластерных систем: ! высокая доступность (High Availability); ! масштабируемость; ! гибкость; ! простота управления. Кластер – это также возможность использовать вычислительные ресурсы системы так, что полученная система превосходит по своим возможностям суммарные возможности ее частей.
№7(20), июль 2004
Кластеры имеют свою специализацию, но тем не менее все они обладают схожими чертами, присущими только кластерам, что в свою очередь отличает их от других вычислительных платформ. Из компьютерного словаря: Cluster (кластер) – группа терминалов или рабочих станций, подключенных к общему серверу, или группа нескольких серверов, которые совместно выполняют общую задачу и способны заменить друг друга, если одно из устройств выйдет из строя. Cluster (кластер) – группа компьютеров, объединенных высокоскоростными каналами связи, и представляющая с точки зрения пользователя одну многопроцессорную машину. Кластерная архитектура обеспечивает высокую надежность и широкие возможности для масштабирования. Кластеры, которые мы будем собирать, сконструированы из обычных персоналок, которые называются узлами,
43
администрирование без использования разделяемой физической памяти. Сей факт, а также то, что на отдельно взятом узле запущена операционная система, отличает наш кластер от кластеров на платформе NUMA (Non-Uniform Memory Architecture) и SMP (Symmetric Multi-Processor). Более подробно смотрите в «вопросах и ответах» [8]. Однако никто не мешает построить кластер на основе и этих платформ. Кстати, в списке top 500 [9], содержащем самые быстродействующие кластеры планеты, есть несколько штук, собранных на PCплатформе. Их, в частности, можно определить по строчкам self-made (самособранные). Все остальное работает на основе специального оборудования. С точки зрения тепловыделения, потреблямых энергоресурсов и занимаемого пространства персоналки явно не лучший материал для создания сколько-нибудь быстродействующих кластеров. Задача кластера заключается в обеспечении согласованной работы всех узлов для достижения поставленной цели. Целью может быть высокая устойчивость (HA – High Availability), высокая вычислительная способность (HP – High Performance), параллельное вычисление, параллельное обслуживание запросов. Наконец, кластер отличается от клиент-серверных распределенных вычислений тем, что кластерное взаимодействие – это все-таки равноценное взаимодействие каждого узла друг с другом, по принципу пиринговых сетей (peer-to-peer), когда каждый узел, в свою очередь, является членом более крупной системы. Условно в кластере выбирается мастер-узел, а другие являются «подшефными» узлами. В случае отказа мастер-узла выбирается по заранее выбранному алгоритму новый мастерузел, который и курирует в дальнейшем весь кластер.
Типы кластеров Высокопроизводительными (вычислительными) кластерами считаются Beowulf-кластеры, которые конструируются для задач, когда требуется запускать параллельные программы, например, симуляторы погоды, обработка данных и т. п. В таком типе кластера обычно присутствует мастер-узел, который и управляет всем кластером, в то время как остальные узлы работают и взаимодействуют в кооперативном режиме. Кластерами с распределением нагрузки (балансировочными) являются Mosix-кластеры. Они позволяют пользователю одного узла прозрачно распределить нагрузку одного узла кластера по всем остальным. Применение целесообразно в тех случаях, когда требуется использовать задачи с интенсивными вычислительными запросами, которые, в частности, характеризуются высокой длительностью вычислений. К числу задач также следует отнести приложения, которые не были специально оптимизированы под параллельное вычисление. Кластеры проекта LVS (Linux Virtual Server) и кластеры типа Piranha (RedHat Linux) считаются кластерами с параллельным обслуживанием запросов. Они в чем-то похожи на Mosix-кластеры, так как также занимаются распределением нагрузки, правда, в несколько другом ключе. Приходящие веб-запросы распределяются системой между набором стандартных веб-серверов. Данный тип кластера больше напоминает ферму, нежели кластер, так как узлы с веб-службами обычно не подозревают друг о
44
друге и не взаимодействуют между собой. Включены же они были по одной простой причине – с течением времени техника обслуживания запросов, возможно, претерпит изменение, и узлы наконец-то начнут знать друг о друге. А может быть, узлы так и не будут знать о существовании друг друга. Время покажет. Когда говорят о кластерах хранения информации, подразумевают системы, разработанные фирмой Sistina (GFS – Global FileSystem) и проектом OpenGFS. Кластеры состоят из узлов, которые обеспечивают параллельный и высоконадежный доступ к данным единой файловой системы. Кластер баз данных – это Oracle Parallel Server (OPS; сейчас известный как Oracle 9I RAC), состоит из узлов, которые обеспечивают параллельное, согласованное и высоконадежное соединение с базой данных. Высоконадежные кластеры представлены такими решениями, как LifeKeeper, FailSafe и Heartbeat. Они также называются отказоустойчивыми кластерами. Происходит мониторинг ресурсов, а именно приложений и состояния узлов. При обнаружении сбоя система замещает IP-адреса, дисковые устройства, файловые системы на резервные, т.е. другого узла. Также на новом узле происходит старт целевых приложений, которые упали на отказавшем узле. Приложения могут и не упасть. Например, в случае, если у элемента кластера всего лишь вышла из строя сетевая карта и он стал недоступен для контроля. Поэтому такой тип отказа оборудования равноценен падению узла. Сегодня мы рассмотрим последний тип кластеров, а именно HA-кластеры, т.е. из семейства отказоустойчивых. Остановим свое внимание на продукте LifeKeeper фирмы SteelEye. Согласно техническим характеристикам поддерживается до 32 узлов в кластере. Узлы могут быть построены как на базе Linux (в первую очередь это Red Hat и совместимые с ней системы), так и на базе Windows 2000 и выше. Хотя я подозреваю, что есть и решения для узлов на базе Sun Solaris, впрочем, это не афишируется, но любознательные читатели всё равно посмотрят установочные и сервисные скрипты. Как сервера узнают о нормальном функционировании друг друга? Очень просто – с помощью heartbeat-механизма, т.е. с помощью обмена информацией по выделенным каналам. Под термином heartbeat подразумевается сердцебиение. В нашем случае «сердцебиение» узлов кластера. Если один из серверов перестает отвечать другому, значит произошел отказ/сбой и предпринимаются аварийные меры. В нашем случае проверки происходят с помощью запросов, отправленных на порты 81, 82. Если удаленная машина не формирует правильный ответ по данным портам, то очевидно, что на узле не запущено кластерное ПО или же узел «упал». В этом случае происходит перемещение функций на другой узел. Выделенными каналами могут выступать как отдельное, дополнительное ethernet-соединение, так и соединение по RS-232 протоколу. В первом случае ethernet-соединение может использоваться серверами как дополнительный канал для обмена информацией по TCP/IP-протоколу. Рекомендуют делать не один канал для heartbeat-целей, а два и более. Подразумевается, что даже в случае отказа коммуника-
администрирование ционного heartbeat-канала в системе будет задействован резервный heartbeat-канал. В самом деле, название кластера обязывает к повышенной надежности. В случае, когда у нас кластер состоит из двух узлов, применяется соединение, представленное на рисунке.
Для мультисерверной конфигурации узлов следует использовать схему, представленную на рисунке.
Как я уже отмечал выше, поддерживается в первую очередь Red Hat Linux. С выпуском LifeKeeper версии 4.4.3 кластер можно развернуть на следующих релизах:
Попытка установки «в лоб» кластера, например, на Slackware Linux, к сожалению, пока не удалась. Поэтому пришлось обратиться к дистрибутиву ASP Linux 9.2. Зароботало все достаточно быстро и беспроблемно. Кстати, запрос в компанию Nordicmind, а именно она осуществляет техническую поддержку для стран Европы, Балтии и СНГ, подтвердил, что на данный момент последним сертифицированным ядром является 2.4.21, т.е. работающие под 2.6.x. ядрами остаются пока не у дел, к сожалению. Итак, начинаем установку пакетов на ASP Linux-системе. Кластерное ПО можно взять по адресу [7], предварительно зарегистрировавшись. Там же получить ключи на испытательный срок в 30 дней. Маленькое отступление. Вашим идентификатором, на основе которого компанияразработчик сформирует вам ключи, является именно MAC-адрес сетевой карты. Что ж, время передать бразды правления root. Убедимся, что у вас присутствуют те же пакеты, что и у меня.
По словам представителя службы технической поддержки из Nordicmind, подверсия ядра (т.е. -14, -19, -24) принципиальной разницы не играет. Поэтому ставим пакет HADR. # rpm -ih HADR-RedHat-2.4.18-14-4.4.2-3.i386.rpm
Аналогично поставим пакеты:
№7(20), июль 2004
45
администрирование ! HANFS-RedHat-2.4.18-14-4.4.2-3.i386.rpm – для NFS ! ! !
служб; steeleye-lkRedHat70-4.2.0-13.i386.rpm – ядро LifeKeeper под RedHat-платформу; steeleye-lkLIC-4.2.0-13.i386.rpm – пакет для управления лицензиями; jre-1.3.1.i386.rpm – Java для GUI-интерфейса управления кластером. [root@pc-box1 java]# rpm -ih jre-1.3.1.i386.rpm
Из директории licensing установим пакет управления лицензиями.
10.0.0.101 bb-box1 10.0.0.200 pc-box2 10.0.0.201 bb-box2 10.0.0.150 triton
где pc-box1, pc-box2 – названия узлов; bb-box1, bb-box2 – названия plip-интерфейсов, которые будут использоваться как heartbeat-каналы; triton – собственно разделяемый ресурс. Ну а теперь запускаем приложение, управляющее кластером. [root@pc-box1]# /opt/LifeKeeper/bin/lkGUIapp
# cd licensing/ # rpm -ih steeleye-lkLIC-4.4.2-3.i386.rpm
И наконец устанавливаем основные пакеты для кластера: steeleye-lk-4.4.2-2.i386.rpm steeleye-lkIP-4.4.2-2.i386.rpm steeleye-lkAPA-4.4.0-1.i386.rpm steeleye-lkMAN-4.4.2-2.i386.rpm steeleye-lkGUI-4.4.2-2.i386.rpm steeleye-lkRAW-4.4.2-2.i386.rpm steeleye-lkHLP-4.4.2-2.i386.rpm [root@pc-box1]# rpm -ih steeleye-lk*
В ходе установки этих пакетов вы получите информационные сообщения, последнее из которых сообщает о том, что для чтения man-документации следует добавить строчку в файл .profile или .bash_profile в вашей домашней директории.
В итоге программное обеспечение поставлено в директорию /opt/LifeKeeper, параметры управления кластером лежат в директории /etc/default/LifeKeeper. Не забудьте записать ключи, полученные вами от компании steeleye или её представителя, в директорию /var/ LifeKeeper/license. Затем запускаем кластер – /opt/LifeKeeper/bin/lkstart. В /etc/inittab пропишутся необходимые для работы сервисы. Также следует прописать в файл /etc/default/LifeKeeper строку NOBCASTPING=1, т.к. тестировать кластер мы будем, вероятно, в отдельной сетке, где нет дополнительных машин. Она означает, что кластерное ПО будет игнорировать пакеты бродкастов. При переносе в дикую среду не забудьте раскомментировать её. Следует обратить внимание, что в /etc/hosts (или в DNSсервер) следует прописать как названия машин с их IPадресами, так и информацию о тех ресурсах, которые будут считаться разделяемыми. Например, на каждом узле мой файл hosts выглядит следующим образом: 10.0.0.100 pc-box1
46
Вводим названия узла, к которому подключаемся, имя пользователя и его пароль. Обычно это суперпользователь, но рекомендуется создать отдельного, который и будет заведовать кластером. Аналогичную процедуру проводим для 2-го узла кластера. После этого у вас должна получиться такая же картина, как и у меня. На заднем плане видно 2 узла, на которых мы будем проводить наши дальнейшие действия.
Для того чтобы узлы «знали» о состоянии друг друга, необходимо установить коммуникационный канал.
Этим мы сейчас и займемся. Начало процесса приведено на lk-step2.png. Начинаем настройку с узла pc-box1. Локальный IP-адрес выбираем такой же, что и в /etc/hosts,
администрирование а именно 10.0.0.100. Собственно говоря, LifeKeeper оттуда все и берет. Но помним, что 10.0.0.101 зарезервирован в качестве backbone-канала (или в данном случае как heartbeat-соединение). Далее заполняем необходимые значения для второго узла.
В общем-то, мы могли принять настройки по умолчанию (кнопка «Accept Defaults»). Само кластерное ПО «догадывается» о настройках. В итоге должны получить следующий результат, представленный на рисунке.
Проверяем доступен ли новый IP-адрес. Да, он откликается, причем с каждого из узлов. Достигается это следующим образом. На устройство eth0 добавляется алиас в виде eth0:1 с новым IP-адресом. При переезде с одного узла на другой алиас с первого узла удаляется и создается на новом. [anthony@pc-box1 anthony]$ /sbin/ifconfig
Дальнейшая наша задача – создать ресурс, который будет отказоустойчивым. В данном случае это IP-адрес с кодовым названием «triton». Он-то и является ключевой фигурой нашего повествования. Ведь именно он находится в иерархии отказоустойчивых ресурсов, которыми мы сегодня занимаемся. Создание ресурса comm/ip напоминает наши предыдущие действия по созданию коммуникационного канала. Отмечу, что ресурс я создал под названием «ip-triton-tag». Можно назвать его по-другому, на ваше усмотрение. Следующей нашей задачей будет создание ресурса для запуска веб-сервера Apache, который создается аналогично предыдущим, показанным выше.
Теперь «расширяем» ресурс на второй узел.
Укажем, где находится бинарный файл от Apache, и расположение файлов конфигурации.
Видим, что на первом узле создался новый ресурс.
№7(20), июль 2004
47
администрирование
Теперь предстоит «расширить» настройки на второй узел и завершить на этом настройку ресурсов.
Чтобы переключить узел с активного состояния на пассивное или же перевести ресурсы с одного узла на другой, надо выбрать пункт In Service/Out of Service на вкладке конкретного узла. Это производится через GUI-приложение. Вполне возможно произвести эту операцию через командную строку: [root@pc-box2]# /opt/LifeKeeper/bin/perform_action ↵ -a restore -t ip-triton-tag
Убедимся, что на двух узлах у нас стоят одинаковые версии.
Как видите, я произвожу переключение кластера со второго узла, на который переедут ресурсы. [anthony@pc-box1 anthony]$ nmap -v triton
При обновлении следите также за тем, чтобы ключи от кластера не затерлись в /var/LifeKeeper/license. Установку кластера можно считать законченной. Несколько слов о конфигурации apache. В файле httpd.conf команду Listen следует применять по отношению к нашему «устойчивому» ресурсу triton. У меня она выглядит на обоих узлах как Listen 10.0.0.150:80. Остальные опции не изменялись. Маленький нюанс: при падении одного из узлов также упадет и вся статистика от веб-сервера, т.к. в этой статье мы не рассматривали создание отказоустойчивого ресурса для файловой системы. Очевидно, что конфигурация на каждом из узлов должна совпадать. Условимся, что первый узел является основным, а второй – резервным. Тогда при остановке основного узла ресурсы «переедут» на второй. Что мы и можем видеть на приведенном рисунке.
48
Несколько слов о том, как перенести файлы с одного узла на другой средствами ПО самого кластера. Для этих целей существует утилита lcdrcp. Я нахожусь на втором узле в директории /var/www/html, и требуется перенести файл
администрирование index.html на первый узел. Запускаем следующим образом:
Команды для создания настроек коммуникационных путей:
[root@pc-box2 html]# /opt/LifeKeeper/bin/lcdrcp index.html pc-box1:/var/www/html
Уточняем, куда именно следует положить файл – в директорию /var/www/html, но уже на первом узле. В первом примере мы переводили ресурс ip-triton-tag с узла на узел, но тогда в иерархии он не был связан с ресурсом apache-tag. Сейчас же давайте посмотрим, как пройдет процесс переезда двух ресурсов. Уже скопировали индексный файл для apache и хотим посмотреть, как запустятся ресурсы на первом узле. [root@pc-box1 bin]# ./perform_action -a restore -t apache-tag
Пример: net_create -d pc-box1 -s pc-box2 -D MyPath -n TCP ↵ -r 10.0.0.200 -l 10.0.0.100 -p 1 crelcm pc-box1 pc-box2 TCP 10.0.0.100 10.0.0.200 10
Команды по управлению приложениями:
Пример: app_create -d pc-box1 -a apache-tag app_list -d pc-box1
Ошибок в ходе «переезда» не возникло. Единственное, что следует отметить – это прекращение срока деятельности лицензионного ключа. Впрочем, месяца на тестирование компонентов вполне должно хватить. Управлять кластером можно не только посредством GUI-приложения, но из-командной строки, а также окошка браузера. Впрочем, последнее не слишком сильно отличается от GUI-управления. Приведу несколько команд и их назначение. Для старта и остановки кластера LifeKeeper, GUI-приложения:
P.S. В брошюре, поставляемой с данным решением, прозвучало, что время простоя – около 50 минут в год. Будем надеяться, что это не только рекламный ход, но и действительно грамотное решение. Спасибо за поддержку сотрудникам Esko Wessman и Marko Lehtimaki из компании NordicMind, а также за техническую помощь сотрудникам Denise Hodges и Randy Hinz компании SteelEye.
Ссылки: 1. http://www.openmosix.org.ru/docs/openMosix-HOWTOmulti/ch02.html 2. http://www.cio-world.ru/techniques/effect/29933/ 3. http://parallel.ru/parallel/russia/russian_clusters.html 4. http://distributed.org.ru/?main&newsday=24-Mar-2004 5. http://www.steeleye.com/ 6. http://licensing.steeleye.com/support/download_get.php/ configclusters.pdf?dir=0&file=configclusters.pdf 7. http://licensing.steeleye.com/download/ 8. http://lse.sf.net/numa/faq/index.html 9. http://www.top500.org//lists/2003/11/
Мониторинг кластера:
Запуск ресурса на выполнение/остановку:
№7(20), июль 2004
49
администрирование
ВЕЖЛИВЫЙ ОТКАЗ – Капитан, барометр упал. – Сильно? – Вдребезги!
АЛЕКСАНДР ШИБЕНКО Обеспечение бесперебойной работы и сохранности данных – головная боль для системного администратора, причем независимо от размеров обслуживаемой им сети. Однако при обсуждении проблем отказоустойчивости в основном рассматриваются либо конструктивные особенности достаточно дорогих моделей серверов, либо различные кластерные конфигурации или построение сетей хранения данных, что, на мой взгляд, может представлять интерес лишь для крупных и/или достаточно богатых компаний. И очень мало информации по решениям, пригодным для использования в небольших фирмах, которых существенно больше, чем близких к нефтяным или финансовым потокам. Поэтому когда в рамках одного из проектов заказчик попросил включить в него предложения по обеспечению отказоустойчивости для сети из нескольких ПК и сервера под управлением ОС Windows 2000, на котором размещен внутренний веб-сайт и хранятся совместно используемые файлы, в первый момент возникло некоторое замешательство. Готового рецепта не было. Начался поиск во всевозможных источниках, результат которого материализовался в виде CD-диска с программным продуктом SurviveIT 2000 фирмы Computer Associates. Для чего же предназначен SurviveIT? В документации написано следующее: «Это высоконадежное решение для платформы Windows NT/2000, обеспечивающее бесперебойный доступ к вашим приложениям и данным даже в случае отказа сервера». Выглядит это следующим образом. В локальной сети устанавливается дополнительный компьютер, называемый вторичным, который берет на себя выполнение всех функций, включая имя и IP-адрес основного (первичного) сервера в случае выхода последнего из строя. Воз-
50
можен и более сложный вариант: один компьютер резервирует несколько серверов (рис. 1). При этом осуществляется дублирование приложений и данных со всех первичных серверов, но в случае сбоя (программного или аппаратного) вторичный может подменить только один из них. клиентские ПК
коммутатор ЛВС первичные (основные) сервера
вторичный (резервный) сервер
коммутатор
выделенный сегмент Ðèñóíîê 1.
При отсутствии сбоя, когда вторичный компьютер не замещает какой-либо из вышедших из строя защищаемых первичных серверов, он может работать как обычный сервер. Данная особенность оказалась очень полезной, т.к. по-
администрирование зволила возложить на вторичный сервер дополнительные функции резервного копирования, что уменьшило общую стоимость проекта. На момент тестирования под рукой не было двух одинаковых компьютеров, но оказалось, что тип процессора, сетевого адаптера и контроллера дисков первичных и вторичного серверов, а также объем установленной в них памяти могут отличаться. И действительно, ПК с процессором Pentium III 500 МГц и 20-ти Гб IDE-диском без проблем заработал в качестве вторичного для старенького сервера с обычным Pentium 200 и SCSI-диском на 8 Гб. Но прежде чем это произошло, естественно, необходимо было установить SurviveIT на оба компьютера. Процесс установки не вызвал никаких проблем, достаточно было следовать инструкциям инсталлятора и в конце перезагрузиться. Сложнее оказалось получить ключ активации. У меня был демо-диск с ограниченной лицензией, но даже ее необходимо было зарегистрировать. Я попытался сделать это в режиме «on-line», но все попытки заполнить соответствующую форму системой регистрации отвергались. Точно не знаю, какое па из исполняемых шаманских танцев возымело действие, но ключ получить удалось. Возможно, проще было бы сделать все это по электронной почте. И еще одну особенность регистрации хочется отметить. Похоже, ключ привязывается к IP-адресу сервера, и в случае смены адреса он может оказаться недействительным. После установки пакета на оба сервера с помощью консоли управления можно приступать к конфигурированию. Сначала необходимо задать серверам соответствующие роли: первичный или вторичный. Первичному серверу можно указать, какое имя и IP-адрес он должен будет получить, после того как снова перейдет в рабочее состояние после аварии. Это может оказаться нужным, ведь под его исходным именем и адресом в сети будет присутствовать дублер. Затем на вторичном сервере создается задание на репликацию. Для обеспечения дополнительной гибкости каждое задание может содержать несколько работ, в рамках которых предлагается выбрать, какие каталоги на дисках первичных серверов необходимо реплицировать и куда, а также какие каталоги вторичного сервера сделать доступными для сетевых клиентов в случае отказа основного сервера. Даже если сбой произошел во время репликации, SurviveIT гарантирует целостность данных и исключает повреждения файлов, записывая на диск только завершенные транзакции. После завершения синхронизации целостность поддерживается путем передачи по сети только фактических изменений, а не за счет копирования файлов целиком. Поэтому защищаемые и резервный сервера могут включаться непосредственно в локальную сеть или даже соединяться низкоскоростным каналом. Однако если предполагается реплицировать большие объемы данных, возможно, окажется целесообразным установить в каждый сервер по дополнительному сетевому адаптеру и организовать выделенный сегмент, изолированный от ЛВС, к которой подключены клиентские рабочие места. SurviveIT позволяет обеспечить корректное функционирование на вторичном сервере приложений, работающих на первичном сервере. Для этого, во-первых, необходимо установить их в те же каталоги и с теми же параметрами,
№7(20), июль 2004
что и на первичном. Затем в свойствах задания на репликацию указывается, какие сценарии будут выполняться на серверах в случае отказа. В состав пакета входят сценарии для следующих приложений: ! Unicenter TNG ! Lotus Notes Server ! Microsoft Exchange Server ! Microsoft SQL Server ! Microsoft Internet Information Server ! Jasmine II ! Microsoft Proxy Server ! Netscape SuiteSpot ! Oracle Enterprise Server ! Sybase Adaptive Server ! Ingres II Можно создавать и собственные сценарии. Для этого в состав пакета включен соответствующий шаблон. Скачать его можно на сайте журнала (www.samag.ru/source). Последнее, что нужно сделать, – настроить механизм определения отказа основного сервера. Для обеспечения дополнительной надежности рекомендуется соединить соответствующим кабелем последовательные порты основного и вторичного серверов (по нему будет посылаться специальный сигнал «heartbeat», свидетельствующий о «жизнеспособности» основного сервера), а также указать один или несколько IP-адресов каких-либо сетевых устройств, независимо «пингуя» которые серверы могут определить, произошел ли отказ и нужно меняться ролями, или это, к примеру, сбой в работе ЛВС. Интерфейс управляющей консоли удобен и понятен, и описываемый процесс много времени не занял. Можно было приступать непосредственно к тестированию. Для имитации отказа основного сервера его просто выключили кнопкой питания. На консоли вторичного сервера появилось предупреждение, и после небольшой паузы он перезагрузился. После этого клиентам вновь стали доступны веб-сервер и сетевые диски. Необходимо учесть, что переключение на резервный сервер происходит не мгновенно, время переключения складывается из времени, необходимого для определения самого факта отказа (в определенных пределах может задаваться администратором системы) плюс время на перезагрузку резервного компьютера. Поэтому если в вашей сети даже такая задержка недопустима, этот продукт вам не подойдет. Обратная задача – восстановление status quo – тоже решается легко. После устранения неисправностей основного сервера администратору достаточно просто инициализировать соответствующий процесс. SurviveIT ресинхронизирует сервера (в это время они оказываются недоступными для пользователей) и автоматически переключает пользователей обратно, на основной сервер, а подменявший его вновь становится резервным (вторичным). К сожалению, не удалось протестировать SurviveIT в сети с развернутой службой каталога Active Directory. Но и того, что было сделано, хватило, чтобы рекомендовать системным администраторам обратить внимание на этот продукт. Вполне возможно, что кому-то он окажется очень полезен.
51
администрирование
УПРАВЛЕНИЕ ФАЙЛОВЫМИ СЕРВЕРАМИ
В данной статье будет рассмотрен круг вопросов, связанный с управлением файловыми серверами в сетях Windows 2000. На всех серверах – контроллерах домена, файловых, почтовых серверах и прочих операционной системой является Microsoft Windows 2000. В любой крупной сети, построенной на основе домена Windows, существует один или несколько файловых серверов, предназначенных для хранения дистрибутивов различного программного обеспечения, технической документации, драйверов, архивов, аудио-, видео-записей, других ресурсов.
ИВАН КОРОБКО Размер массива одного файлового сервера, собранного из жестких дисков, может достигать от нескольких сотен гигабайт до нескольких терабайт. Количество файловых серверов может варьироваться в зависимости от потребностей. Поддержка серверов является трудоемкой задачей, требующей пристального внимания системного администратора, поскольку осуществляется постоянное обновление ресурсов. Эта статья посвящена автоматизации администрирования файловых серверов с помощью стандартных средств Microsoft Windows. Решением поставленной задачи является инструмент, созданный на основе VBScript1, позво-
52
ляющий абстрагироваться от файловой системы и автоматизировать администрирование файловых серверов.
Выбор варианта Создание сервера является нетривиальной задачей, состоящей из нескольких основных этапов. Первым этапом во время планирования сервера является определение списка услуг, которые будут предоставлены, и количество одновременно обслуживаемых пользователей, максимальное количество которых будет варьироваться в зависимости от типа ресурсов, хранимых на сервере. Например, нагрузка на файловый сервер, на котором хранится коллекция фильмов, и на файловый сер-
администрирование вер, на котором хранятся дистрибутивы программ, различна. Исходя из нагрузки на сервер будут определены его аппаратные характеристики. Вторым фактором, влияющим на аппаратные характеристики сервера, является язык программирования, на основе которого создан инструмент. В качестве такового выбран VBScript по следующим критериям: ! Язык программирования VBScript входит в поставку Microsoft Windows, поэтому перенос инструмента с одного Windows-сервера на другой не представляет труда. ! Синтаксис языка достаточно прост. ! VBScript является объектно-базированным языком и имеет встроенную поддержку объектов. Одним из недостатков VBScript является низкая скорость работы сценария, поскольку VBScript является интерпретируемым языком. Увеличить его скорость работы можно за счет наращивания аппаратных ресурсов, поскольку язык требователен к этому фактору. Вторым этапом создания файлового сервера является планирование файловой структуры сервера, необходимо дать ответы на следующие вопросы: ! По каким критериям должна осуществляться сортировка программного обеспечения: по названию продукта, по названию фирмы-производителя приложения (Microsoft, Adobe, Symantec и др), по назначению приложения (папка, содержащая архиваторы, такие как Zip, Rar, называется Arc; программы для тестирования аппаратных средств – SysInfo и т. д.) или предпочесть смешанный вариант. Каждый системный администратор решает самостоятельно, какой из вариантов сортировки ресурсов ему удобнее использовать. ! Как осуществлять сортировку ресурсов по языковому признаку (имеется в виду язык интерфейса)? ! Какое место в файловой структуре займет раздел драйверов, документации и т. д. ! По какому принципу будет осуществляться сортировка драйверов – по названию фирмы-производителя (HP, Canon, Intel, Asus, Maxtor) или по назначению устройств (Video, Audio, Chipset, Printer)? ! Как будут учитываться версия операционной системы, для которой создан драйвер устройства? Создавая файловый сервер, системный администратор должен дать ответ на огромное количество вопросов. Приведенные вопросы – лишь малая их часть. Создание файловой структуры (иерархии директорий, подчиняющихся некоторому принципу) на файловом сервере является сложной задачей даже для опытного системного администратора. Род деятельности фирмы вносит свои коррективы в окончательный вид структуры. Исправление ошибок, допущенных при ее создании, по прикладываемым усилиям эквивалентно созданию новой файловой структуры. Предлагаемый инструмент позволяет полностью абстрагироваться от файловой структуры, однако это не значит, что она может быть хаотичной. Поскольку объем файлового сервера огромен, то об-
№7(20), июль 2004
работка структуры каталогов при загрузке страницы занимает много времени, поэтому загрузка html-страницы будет длиться десятки минут. Для увеличения скорости работы приложения его разделяют на 2 части. Первая часть представляет собой сценарий на VBScript, который создает промежуточный файл с данными – кэшфайл. Сценарий запускается планировщиком задач через определенные промежутки времени. В том случае если размер файлового сервера велик, то нагрузку по созданию кэш-файла можно распределить между несколькими серверами. Вторая часть представляет собой сайт (см. рис. 1а, 1б), созданный на основе ASP-страниц (для функционирования ASP-страниц требуется установка Internet Information Server (IIS). IIS является приложением, входящим в комплект поставки Microsoft Windows). С помощью ASP осуществляется отображение информации, поиск по различным критериям.
Ðèñóíîê 1à
Ðèñóíîê 1á
VBScript: работа с массивами При создании данного комплекса активно использовались массивы. О некоторых особенностях работы с массивами необходимо рассказать отдельно, чтобы впоследствии не отвлекаться на подобные разъяснения. Немного теории: массивы бывают одномерные и многомерные. Могут быть статическими и динамическими. Для статического массива память выделяется один раз в пол-
53
администрирование ном объеме, а динамический растет или уменьшается в зависимости от надобности и записанных в него данных.
Объявление массивов Рассмотрим общий случай – многомерный массив. Массивы объявляются с помощью оператора Dim. После имени многомерного массива в круглых скобках через запятую указывают границы каждого измерения массива. Особенности объявления массивов: ! индексация элементов массивов начинается с нуля; ! в многомерном массиве может быть до 60 измерений; ! динамические одномерные и многомерные массивы объявляются одинаково. Приведем пример объявления многомерного и одномерного массивов. Одномерный массив является частным случаем многомерного (см. таблицу 1). Òàáëèöà 1
вия упорядочивания массива смежные элементы меняются местами. Затем происходит переход к следующему элементу. В результате элементы массива становятся друг за другом в заданном порядке – происходит упорядочивание. В том случае, если читатель хочет использовать более производительный метод упорядочивания массивов, то ему необходимо переписать часть кода, касающегося сортировки массива. Ïðèìåð 2: Dim Array(100) …………………… For j=0 to 100 For i=0 to 100 If StrComp(Array_sort(i),Array_sort(i+1),0)=1 Then temp=Array(i) Array (i)=Array(i+1) Array(i+1)=temp End if Next Next
Первая часть: создание кэш-файла Переопределение размеров массива Размер массива может быть переопределен только в том случае, если он является динамическим. Эта процедура осуществляется с помощью команды ReDim, имеющей следующий синтаксис: ReDim [Preserve] array_name(subscripts) [, varname(subscripts)] ...
! Preserve – переопределяет размер, сохраняя существу! !
ющие данные в массиве, если параметр не указан, то все ячейки массива обнуляются; Array_name – название массива; Subscripts – количество элементов в массиве.
При изменении размера многомерного массива корректировке поддается только размер последнего измерения.
Определение границ массива Определение размера выделенной памяти для массива осуществляется с помощью команд lbound(array_name) и ubound(array_name), где array_name – название массива, границы которого необходимо определить. С помощью команды lbound определяется нижняя граница массива, с помощью ubound – верхняя граница. Данный прием часто используется для чтения всех элементов динамического массива: Ïðèìåð1: For i=Lbound(Array_) to Ubound(Array_) Msgbox Array_(i) Next
Упорядочивание элементов массивов Наиболее простым, хотя и неэффективным, методом упорядочивания массивов является пузырьковый метод. Суть метода заключается в том, что при несоблюдении усло-
54
Создание кэш-файла осуществляется с помощью сценария, написанного на VBScript, который запускается через заданные промежутки времени стандартным планировщиком задач Microsoft Windows. По ходу выполнения сценария осуществляется чтение структуры подкаталогов в указанном родительском каталоге. Анализируется содержимое подкаталогов. Если в подкаталоге обнаружена метка – файл !meta.txt, – путь к этой папке записывается в массив. Файл !meta.txt является идентификационно-параметрическим. Для удобства файл будем называть файлом параметров, или параметрическим. Он содержит в себе три параметра, по одному в каждой строке. Первая строка включает в себя информацию о названии раздела, в котором будет размещена ссылка на дистрибутив данного приложения. Названием раздела может быть либо название фирмы-производителя продукта, либо жанр, к которому относится приложение. Вторая строка содержит название продукта. Третья – его описание. Создаваемый кэш-файл представляет собой текстовый файл. Каждая строка файла включает в себя четыре параметра, содержащие название фирмы производителя, путь к каталогу, название приложения и его версию, комментарий. Параметры отделены символом-разделителем, который описывается в конфигурационном файле. VBScript-сценарий состоит из нескольких частей: ! чтение исходных параметров из конфигурационного файла; ! чтение структуры подкаталогов; ! обработка считанной структуры подкаталогов; ! формирование кэш-файла.
Чтение исходных параметров из конфигурационного файла Все исходные параметры для скрипта содержатся в отдельном конфигурационном файле, например, cache.ini. Он представляет собой текстовый файл и содержит следующие параметры: ! Имя и путь создаваемого кэш-файла, например, cache.dat.
администрирование ! Путь к корневому каталогу, структура подкаталогов ко! !
торого подлежит обработке, например \\server\software. Определение флага, по которому определяется необходимая парапка. Флагом является имя параметрического файла, например, meta.txt. Метка предназначеная для разделения параметров при формировании кэш-файла.
Рассмотрим структуры файла cache.ini. Файл содержит следующие поля и соответствующие им характеристики:
value=left(cstr(array(i)), len1-1) msgbox "value: "& value Select Case value Case "Symbol" path1=right(cstr(array(i)), ↵ len(cstr(array(i)))-len1) Case "Path_To_Root" path2=right(cstr(array(i)), ↵ len(cstr(array(i)))-len1) Case "Label_FileName" path3=right(cstr(array(i)), ↵ len(cstr(array(i)))-len1) Case "Cache_FileName" path4=right(cstr(array(i)), ↵ len(cstr(array(i)))-len1) End Select End if Next MsgBox path1+chr(13)+ path2+chr(13)+ path3+chr(13)+ path4+chr(13)
Òàáëèöà 2
Чтение структуры подкаталогов Чтение данных из параметрического файла осуществляется с помощью Windows Scripting Host (WSH)2, поддерживаемого VBScript. Данные считываются в массив, элементами которого являются строки, содержащиеся в файле, по следующему шаблону:
Чтение структуры подкаталогов осуществляется с помощью объекта Scrpting.FileSystemObject WSH, который создается функцией CreateObject. После создания объекта необходимо получить доступ к подкаталогу, элементы которого (файлы и каталоги) должны быть обработаны. Последовательно перебирая все элементы массива, осуществляется считывание имен папок. Ïðèìåð 5:
Ïðèìåð 3: Text="" i=0 Dim Array() FileName="cache.ini" Set fso = CreateObject("Scripting.FileSystemObject") If fso.FileExists(filename) Then Set Fline = fso.OpenTextFile(FileName) Do While Not (Fline.atEndOfStream) redim preserve array(i) Array(i)= Fline.ReadLine i=i+1 Loop Else Text="File Not Found" End If For i=lbound(array) to ubound(array) Text=Text+cstr(array(i))+vbCrLf ‘ vbCrLf=chr(13)+chr(10)=ENTER Next MsgBox Text
После того как сформирован массив, содержащий строки параметрического файла, осуществляется анализ его элементов и распознавание значений параметров по следующему алгоритму: ! Чтение элементов массива. ! Поиск в строке символа «=» с помощью функции instr()3. ! Сравнение строк до знака равенства с названиями параметров, перечисленных в таблице 1. ! В случае совпадения названия параметра и части строки до знака равенства осуществляется чтение строки за знаком равенства и присвоение прочитанных значений параметрам. В программном коде реализация описанного алгоритма выглядит следующим образом: Ïðèìåð 4: For i=lbound(array) to ubound(array) len1=instr(cstr(array(i)),"=") msgbox "len1: "&len1 if len(cstr(array(i)))<>0 then
№7(20), июль 2004
Path=”C:\RootFolder” Temp=”” Set fso=Wscript.CreateObject("Scripting.FileSystemObject") Set oFolder=fso.GetFolder(path) Set oFolders=oFolder.SubFolders For Each oF In oFolders Temp=Temp+cstr(oF.Path)+chr(13)+chr(10) Next MsgBox Temp
Как уже говорилось, данный фрагмент считывает все подкаталоги в заданном каталоге. Для чтения всей структуры подкаталогов необходимо этот механизм поместить в функцию, а ее сделать рекурсивной4. Функция будет иметь два параметра – путь к абсолютному подкаталогу (PATH) и уровень вложенности подкаталогов (IDX). Данные, содержащие абсолютные пути к подкаталогам, разумно записывать в динамический одномерный массив. Приведем пример создания и заполнения элементов динамического массива на примере чтения структуры подкаталогов (основа – предыдущий пример), затем шаблон рекурсивной функции. Листинг рекурсивной функции см. в приложении5, листинг файла service.vbs. Ïðèìåð 6: Path=”C:\RootFolder” i=0 Dim Array() 'Îáúÿâëåíèå äèíàìè÷åñêîãî ìàññèâà Set fso=Wscript.CreateObject("Scripting.FileSystemObject") Set oFolder=fso.GetFolder(path) Set oFolders=oFolder.SubFolders ' Èçìåíåíèå ðàçìåðà äèíàìè÷åñêîãî ìàññèâà Redim Preserve Array(oFolders.count) For Each of In oFolders Array(i)=cstr(oF.Path) ' Çàïèñü ýëåìåíòîâ â ìàññèâ i=i+1 Next '×òåíèå ýëåìåíòîâ ìàññèâà Temp=”” For i=Lbound(Array) to Ubound(Array) Temp=Temp+cstr(Array(i))+chr(13)+chr(10) Next MsgBox Temp
55
администрирование Ïðèìåð 7: Øàáëîí ðåêóðñèâíîé ôóíêöèè ……….. ' Âûçîâ ôóíêöèè RecFolder â òåëå ñöåíàðèÿ RecFolder index, path ……….. Function Recfolder (idx, path) ……….. ……….. ……….. Call Recfolder (idx+1, path) ……….. End Function
Обработка структуры подкаталогов
Запись в файл ведется построчно: каждая строка файла содержит четыре параметра, разделенных спецсимволом. Параметры записываются в файл в следующем порядке: название фирмы-производителя продукта; путь к дистрибутиву приложения; название приложения; краткое описание приложения. Запись данных в текстовый файл осуществляется в соответствии со следующим шаблоном: Ïðèìåð 8: text= “____________” set fso = CreateObject("Scripting.FileSystemObject") set MyFile = fso.CreateTextFile(strFileName, True, TRUE) ‘  òåêñòîâûé ôàéë çàïèñûâàåòñÿ çíà÷åíèå ïåðåìåííîé «text» MyFile.WriteLine(text) MyFile.Close
После завершения процесса считывания подкаталогов в многомерный массив осуществляется упорядочивание элементов массива по первому полю (по названию фирмы). Впоследствии, во второй части инструмента, созданной на основе ASP, осуществляется упорядочивание названий продуктов каждой фирмы. Таким образом, список будет упорядочен и по разделам, и по названиям продуктов в каждом из разделов. Пример упорядочивания одномерного массива – см. пример 2; многомерных массивов см. в приложении, листинги файлов service.vbs и soft.asp.
Пример файла cache.dat см. в приложении. В случае необходимости нагрузка по считыванию и обработке структуры подкаталогов может быть распределена между несколькими серверами. На каждом сервере необходимо запустить самостоятельного агента, который будет генерировать кэш-файл с уникальным именем. Файлы, полученные от разных агентов, должны быть сохранены в одной папке на сервере.
Формирование кэш-файла
Внедрение агента
После завершения процесса сортировки многомерного массива по названию фирмы-производителя продукта осуществляется запись данных в кэш-файл, который представляет собой текстовый файл. Параметры в файле разделены спецсимволом, в качестве которого рекомендуется один из символов таблицы ASCII-II (см. рис. 2),выбор которого обусловлен тем, что ни один из них не может быть использован в описании параметров файла !meta.txt.
Внедрение агента не представляет собой никакой сложности: в файле-скрипте указывается путь к конфигурационному файлу. На основе данных, прочитанных из этого файла, будут определены параметры формируемого кэшфайла и его местоположение. Подключение к файловому серверу необходимо осуществлять с помощью сетевого диска, например, диска S (см. приложение файл cache.ini). Подключение к сетевому диску в данном примере осуществляется при помощи команды: net use s: \\server\software
Для обеспечения регулярного обновления кэш-файла можно использовать команду AT или стандартный планировщик задач. Синтаксис команды в данном случае будет следующим: AT \\Server 20:00 /interactive /every:M,T,W,Th,F,S,Su ↵ C:\InetPub\Soft\Data\Service.vbs
Данная команда запускает сценарий Service.vbs каждый день в 8 часов вечера на сервере под именем «Server».
Обработка кэш-файла Файловая структура проекта на основе ASP Инструмент представляет сайт, точкой входа в который является страница на языке HTML. Сайт создан на основе окон (Frame): файл default.htm делит окно браузера на два столбца. Ïðèìåð 9: Ëèñòèíã ôàéëà default.htm
Ðèñóíîê 2
56
<html> <head> <title>Ïðîãðàììíîå îáåñïå÷åíèå </title> <meta http-equiv="Content-Type" content="text/html; ↵ charset=windows-1251">
администрирование <FONT FACE="Arial" size="3"></FONT> </head> <!-- Ëåâûé ñòîëáåö ñîñòàâëÿåò 20% îò øèðèíû âñåãî îêíà. Ëåâîå îêíî èìååò èäåíòèôèêàòîð «SortSearch», âòîðîå «Product». --> <frameset cols="25%,*" FRAMEBORDER="5" BORDER="yes" ↵ FRAMESPACING="5" > <frame src="sortsearch.asp" name="sortsearch" scrolling="yes" ↵ ÌÀRGINHEIGHT=1 > <frame src="soft.asp?Search_Text=&radGrp=1" name="product"> </frameset> </html>
В разделе <HTML> описываются таблицы стилей; исходная кодировка документа; способ взаимодействия страниц, основанный на передаче форм; поиск по заданным критериям.
Таблица стилей и выбор исходной кодировки документа Обе ASP-страницы используют таблицу стилей. Текстовая информация изначально выводится в кодировке WIN1251. Стили описываются в подключаемом файле style.css (см. приложение). Файл стилей описывается в разделе <LINK>, необходимая кодировка – в разделе <META> (HTML): Ïðèìåð 10: <HEAD> <LINK HREF="style.css" TYPE=text/css REL=stylesheet> <META HTTP-EQUIV ="Content-Type" CONTENT="text/html; ↵ CHARSET=windows-1251"> </HEAD>
Значение параметра CHARSET определяет исходную кодировку документа. Клиент, загрузив страницу, может конвертировать ее для наилучшего отображения в любую другую удобную для него кодировку. Остальные страницы, отображающиеся в 2 фреймах, см. рис. 1, взаимосвязаны друг с другом и являются рекурсивными. Обе страницы создаются с помощью скриптов ASP.
Взаимодействие страниц Взаимодействие страниц основано на передаче данных, которая осуществляется с помощью двух методов: прямого и косвенного. Прямой метод: в исходной странице создается форма, содержащая поля для ввода информации, кнопку для отправки информации: Ïðèìåð 11: <FORM ACTION="printer_adsi.asp" TARGET="main" METHOD="get"> <INPUT TYPE="submit" VALUE="Ïîèñê"></INPUT> <INPUT NAME="Search_Text"></INPUT> </FORM>
В разделе FORM присутствуют следующие параметры: ACTION, TARGET, METHOD. В параметре ACTION указывается имя файла, который содержит сценарий. Сценарию будут передаваться данные после нажатия на кнопку. Подразделом, который обязательно присутствует в разделе <FORM>, является INPUT. INPUT имеет следующие параметры: TYPE, NAME, VALUE. Параметр TYPE опреде-
№7(20), июль 2004
ляет вид приемника информации: кнопка (TYPE=«submit» или «reset» ); окно для ввода текста (TYPE=«text»), значение по умолчанию; кнопка выбора одного параметра из группы (TYPE=«radio»). VALUE – название поля, отображаемое в объекте. NAME – имя, которое участвует в формировании запроса. Запрос, который формируется с помощью метода GET, в общем виде выглядит следующим образом: http://имя_ страница.asp(htm)?П1=З1&П2=З2&...Пn=Зn. В приведенной строке присутствуют обозначения, имеющие следующую расшифровку: П – параметр, З – значение. Опишем страницу-приемник, предназначенную для получения и обработки данных. Считывание запроса, опираясь на приведенный пример, осуществляется следующим образом: set search_t= Request.QueryString(«Search_Text»)
Прямой метод используется для осуществления поиска. Косвенный метод: также используется для передачи данных между сценариями на ASP. Метод основан на том, что при нажатии пользователем на картинку или текст, являющийся ссылкой, сразу формируется запрос, который передает данные. Ïðèìåð 12: <% <A HREF="http://èìÿ_ñòðàíèöà.asp(htm)?Ï1=Ç1&Ï2=Ç2&...Ïn=Çn" target=" & chr(34) &"var"& chr(34) & " > ↵ <IMG SRC =images\pr1.jpg BORDER=0> </A> %>
Поиск по заданным критериям: файл sortsearch.asp Поиск базируется на использовании фильтра. Перед осуществлением этой процедуры пользователь задает следующие параметры: критерий поиска, искомое словосочетание. В качестве критерия может быть выбран один из параметров: «Везде», «Фирма», «Название». По умолчанию принято значение «Везде». Выбор значения по умолчанию осуществляется меткой CHECKED в соответствующем разделе INPUT. Форма на странице-отправителе выглядит следующим образом (HTML): Ïðèìåð 13: <FORM ACTION="printer_adsi.asp" TARGET="main" METHOD="get"> <INPUT TYPE="submit" VALUE="Ïîèñê"></INPUT> <INPUT NAME="Search_Text"></INPUT> <BR> <INPUT CHECKED TYPE="radio" VALUE="1" NAME="radGrp">Íàçâàíèå < INPUT TYPE="radio" VALUE="2" NAME="radGrp">Îïèñàíèå < INPUT TYPE ="radio" VALUE="3" NAME="radGrp">Ðàçìåùåíèå </FORM>
Вторая часть поисковой системы – «приемник» информации находится в ASP-файле. Считывание значений параметров осуществляется следующим образом: Ïðèìåð 14: set radio_=Request.QueryString("radGrp") set search_= Request.QueryString("Search_Text")
57
администрирование после того как считаны параметры, указывающие, где искать (radio_) и что искать (Search_), выполняется процедура поиска: Ïðèìåð 15: Select case radio_ Case "1" If instr(ucase(cstr(array_(i,0))), ucase(search_)) End If
Внедрение визуальной части then
T=T+cstr(array_data(i,0)))+…
Case "2" If instr(ucase(cstr(array_data(i,1))), ucase(search_)) then End If …… End Select
рых следует осуществлять поиск. Затем выполняется процедура поиска и запись в переменную. Результат выводится на экран.
T=T+cstr(array_data(i,0)))+…
В качестве месторасположения инструмента можно выбрать любой из серверов, на котором должен быть установлен IIS. При установке IIS будет создана стандартная папка C:\InetPub, в которой предлагается сделать подпапку Soft. В ней расположить файлы – defalt.htm, style.css, softsearch.asp, soft.asp. В следующей статье будет рассказано о том, как сделать мастер, позволяющий пополнять коллекцию программ и вносить изменения в метафайлы. 1
Отображение результатов: файл soft.asp Файл soft.asp является по своей сути сердцем проекта. Все остальные файлы являются вспомогательными. Функция рождения страницы, содержащей всю необходимую информацию, возложена на сценарий, содержащийся в этом файле. Рассмотрим алгоритм работы данного сценария. На первом этапе осуществляется чтение информации, переданной прямым методом из HTML-файла. Из конфигурационного файла soft.ini считываются параметры, определяющие символ «разделить» и местоположение кэшфайла. На этом подготовительный этап завершен. В результате работы функции чтения кэш-файла временный массив заполняется прочитанными строками. В массив заносятся только те строки, в которых встречается символ «разделить». Это означает, что считанная строка записана в нужном формате. Затем каждая строка временного массива делится на параметры, разграниченные спецсимволом. Полученные данные заносятся в многомерный массив. Добавление в динамический массив данных, читаемых из другого массива, вызывает изменение первого измерения заполняемого массива. Но из-за ограничений, накладываемых VB на работу с массивами, такие изменения выполнить невозможно. Поэтому необходимо сначала сосчитать количество элементов массива и только после этого создавать массив и выделять память. Таким образом, мы видим, что методы работы с динамическими массивами в языке VBScript далеки от настоящей динамичности. Следующий шаг – сортировка многомерного массива. Сортировка осуществляется по следующему алгоритму. Данные читаются из кэш-файла в многомерный массив. Элементами первого измерения массива являются названия фирмы-производителя продукта. Эти данные уже отсортированы по алфавиту. Элементы второго измерения массива требуют сортировки. Ими являются названия приложений. Весь html-код, получаемый в результате обработки массива, записывается в текстовую переменную. Переменная формируется по следующему алгоритму: с помощью процедуры select case и полученного из другого файла параметра определяют поля массива, в кото-
58
2
3
4
5
Visual Basic Script Edition (VBScript) является интерпретируемым языком. Представляет собой сокращенную версию VB. Имеет встроенную поддержку объектов. Сценарии могут быть не только встроенными в код вебстраницы, но и могут быть автономными файлами с расширением VBS. В 1998 году Microsoft предложила в качестве инструмента разработки и выполнения сценариев для операционной системы Microsoft Windows сервер сценариев Windows Scripting Host (WSH). В комплект поставки Microsoft Windows 98 входит WSH версии 1.0, в Microsoft Windows 2000 – WSH 2.0; Сценарии WSH поддерживает два встроенных в Microsoft Windows языка программирования – Microsoft Visual Basic Script Edition (VBScript) и Miscrosoft Java Script Edition(Jscript). WSH предъявляет минимальные требования к объему ОП, является удобным инструментом для создания сценариев и автоматизации повседневных задач. Возможности WSH-сценариев: выводить информацию в виде диалоговых сообщений; cчитывать информацию из потока данных; управлять процессами Microsoft Windows; подключать сетевые диски и принтеры; работать со специальными папками; с системным реестром; управлять OLE-объектами и т. д. InStr([start, ]string1, string2[, compare]) – функция определяет наличие подстроки в строке. Ее возвращаемым значением является номер символа, начиная с которого подстрока совпадает со строкой. start – начальная позиция, с которой начинается поиск. Является обязательным параметром в случае использования параметра тип сравнения; string1 – строка, в которой ищется подстрока, указанная в параметре string2; compare – числовой параметр, определяющий тип поиска строки в подстроке. Параметр compare может принимать следующие значения:
Рекурсивная функция – это функция, вызывающая саму себя, при этом она обязательно передает параметры. Рекурсивные функции, как правило, используются для чтения иерархической структуры. Приложение со всеми листингами смотрите на сайте журнала: http://www.samag.ru/source.
администрирование
РУСИФИКАЦИЯ FreeDOS
ВАДИМ ДРУЖИН Разработка системы FreeDOS началась более 10 лет назад – файл HISTORY.TXT в исходных текстах ядра начинает отсчёт с 25 мая 1993 года. Несмотря на столь солидную историю, пригодной для практического использования в современных условиях система стала недавно – поддержка файловой системы FAT32 начала разрабатываться в 2001 году. Конечно, не все применения DOS требуют поддержки большой файловой системы. Задолго до появления FAT32 FreeDOS использовалась как встроенная операционная система в условиях малого дискового пространства и небольших объемов оперативной памяти, а также входила в состав дистрибутива программы DOSEMU – среды эмуляции DOS для Linux. Однако для меня DOS – это прежде всего система, которую можно загрузить с дискеты и использовать для восстановления работоспособности операционной системы на жестком диске, среда для запуска утилит аварийного восстановления данных и антивирусных программ. После появления версий Microsoft Windows с поддержкой файловой системы FAT32 использовать для аварийно-восстановительных работ DOS без поддержки этой файловой системы было проблематично. Поэтому достаточно длительное время FreeDOS меня не интересовала.
№7(20), июль 2004
Недавно, подготавливая загрузочную антивирусную дискету, у меня возникли трудности в ограничении по размеру – необходимые файлы не удавалось упаковать в объём 3,5-дюймовой дискеты, несмотря на все ухищрения. Тогда я решил поискать версию DOS с поддержкой FAT32, но с меньшим объёмом загрузочных файлов, чем у MS-DOS из комплекта Windows. Оказалось, что у FreeDOS уже имеются нужные возможности и при этом сохранился маленький объём загрузочного файла. Сравним размер ядра MSDOS из состава Windows 98 Second Edition RUS и ядра из дистрибутива FreeDOS Beta9RC5:
Разница почти в 5 раз! Так что для запуска с дискеты предпочтительно использовать именно FreeDOS. К сожалению, при работе с FreeDOS выяснился один неприятный момент – FreeDOS не поддерживает смену информации о кодовых страницах. Команда COUNTRY в файле CONFIG.SYS обрабатывается только частично, используется первый параметр (код страны) для установки национальных форматов даты, времени, валюты и разде-
59
администрирование лителей, а указанная кодовая страница (второй параметр) игнорируется, и используется страница, жёстко зашитая во время компиляции. Поэтому после установки из дистрибутива FreeDOS не может нормально обрабатывать файлы и каталоги с русскими именами. Но эту проблему можно решить. Так как FreeDOS распространяется с полными исходными текстами, никто не мешает нам собрать версию ядра с поддержкой нужной кодовой страницы. Описанию этой процедуры и посвящена статья. Для сборки ядра FreeDOS нам понадобится компилятор языка C. Выполнять компиляцию можно при помощи Turbo C 2.01, Turbo C++ 1.01, Turbo C 3.0, Borland C 4.51 и 5.01, по утверждению авторов, можно использовать другие компиляторы Borland, Microsoft C и (Open)Watcom C. Хотя некоторые версии компиляторов Borland сейчас доступны в Интернете для свободного скачивания, я (как и разработчики FreeDOS) рекомендую использовать OpenWatcom C. Я пробовал использовать для сборки компилятор Turbo C 2.01. Процесс прошёл успешно, но получившееся ядро не смогло нормально загрузиться. А после сборки компилятором OpenWatcom C 1.2 получился полностью рабочий исполняемый файл. Компилятор OpenWatcom можно свободно загрузить из Интернета по адресу: http://openwatcom.mirrors.pair.com/ watcom/open-watcom-win32-1.2.exe – это полный инсталятор для среды Win32, включая компиляторы C и Fortran77 (размер файла 63.1 Mб). Если вы не хотите качать весь файл, можно получить набор только необходимых для компиляции компонентов по адресу: http://openwatcom.mirrors.pair.com/watcom/zips-1.2. Компиляцию можно выполнять как из-под Windows, так и из-под DOS. Версию компилятора для DOS можно использовать, например, в среде DOSEMU под Linux. В зависимости от ОС, под которой будет проходить компиляция, нужно скачать соответствующий набор ZIP-архивов.
Все скачанные архивы нужно распаковать в какой-нибудь каталог, пусть для определенности это будет C:\WATCOM. Распаковывать архивы нужно с сохранением структуры каталогов. Кроме компилятора C для сборки понадобится также ассемблер NASM версии не ниже 0.98.36. Я использовал версию 0.98.38 (DOS 32-bit). Загрузить NASM можно по адресу: http://nasm.sourceforge.net. Будем считать, что NASM распакован в каталог C:\NASM. Маленький размер ядра FreeDOS достигается при помощи использования динамической упаковки. После компиляции файл дополнительно обрабатывается упаковщиком UPX, и при загрузке происходит его автоматическая распаковка в оперативную память. Похожую технологию использует ядро Linux, там для упаковки используется алгоритм GZIP или BZIP2. Скачать UPX можно по адресу: http://upx.sourceforge.net. Я пользовался UPX 1.24d (32-bit DOS version). По аналогии с NASM пусть UPX располагается в каталоге C:\UPX. Итак, все утилиты, необходимые для компиляции, у нас есть, теперь нужно получить сами исходные тексты FreeDOS. Вы можете скачать с сайта http://www.freedos.org/ дистрибутив FreeDOS в виде образа загрузочного компактдиска (ISO), в который входят исходные тексты, скомпилированные версии всех файлов и интерактивный инсталлятор, либо скачать с того же адреса по отдельности необходимые утилиты (команды и драйверы DOS) и по адресу http://sourceforge.net/projects/freedos/ исходные тексты ядра. Если скачивать утилиты по отдельности, вы получите самые последние версии, программы, входящие в установочный образ, могут быть устаревшими. Я использовал для сборки исходные тексты ядра версии 2.0.34 из дистрибутива FreeDOS Beta9RC5, они находятся в файле FREEDOS\ PACKAGES\SRC_BASE\DISK04\KERNELS.ZIP на компактдиске. После распаковки архива с сохранением структуры каталогов в корне диска C мы получим каталог C:\SOURCE\KE2034, в котором и располагаются исходные тексты, документация на английском языке (в подкаталоге DOCS) и командные файлы, выполняющие сборку. Сборка производится с помощью командного файла BUILD.BAT или утилиты make. При запуске make без параметров выполняется вызов того же самого BUILD.BAT, так что разницы фактически никакой. Перед началом компиляции нужно задать параметры, такие как тип и место нахождения используемого компилятора, архитектура процессора, для которого предназначено ядро (16 или 32 бита), и необходимость поддержки файловой системы FAT32. Задаются эти параметры в файле CONFIG.BAT. Чтобы получить исходный вариант файла (с настройками по умолчанию), нужно скопировать с этим именем файл CONFIG.B (copy CONFIG.B CONFIG.BAT). Теперь вносим в файл CONFIG.BAT необходимые изменения. Следующие строки нужно закомментировать: set set set set
60
COMPILER=TC2 TC2_BASE=c:\tc201 XCPU=86 XFAT=16
администрирование А взамен раскомментировать (убрать символы «:- » в начале строки): ::::::-
set COMPILER=WATCOM if not \%WATCOM% == \ goto watcom_defined set WATCOM=c:\watcom set PATH=%PATH%;%WATCOM%\binw set XCPU=386 set XFAT=32
Следующие строки изменить: set XNASM=c:\bin\nasm16 → set XNASM=c:\nasm\nasm set XUPX=upx --8086 –best → set XUPX=c:\upx\upx --8086 --best
Если вы используете версию компилятора Watcom C для среды Win32, то в строку set PATH=%PATH%;%WATCOM%\binw
Теперь вернемся в каталог KE2034 и, собравшись с духом, запустим команду BUILD.BAT. После окончания компиляции в каталоге BIN мы получим файл KERNEL.SYS – наше новое ядро FreeDOS. Кроме него, в каталоге BIN находятся дополнительные файлы и утилиты: ! AUTOEXEC.BAT – пример конфигурационного командного файла; ! CONFIG.SYS – пример конфигурационного файла DOS; ! INSTALL.BAT – командный файл для создания загрузочной дискеты с собранным ядром; ! KWC38632.map – карта памяти ядра (отладочная информация); ! KWC38632.sys – копия ядра, в имени файла которого указаны использованные опции сборки – тип компилятора, тип процессора, тип файловой системы; ! SYS.COM – команда для копирования системных файлов и записи на диск загрузочного сектора.
нужно добавить путь к каталогу binnt: set PATH=%PATH%;%WATCOM%\binw;%WATCOM%\binnt
Итак, для компиляции у нас всё готово, осталось только выполнить то, ради чего всё это затевалось – русификацию. Минимально необходимая модификация ядра, которая позволяет без проблем обрабатывать файлы с русскими символами в именах, – это задание правильной таблицы преобразования строчных/заглавных букв. Информация о кодовой странице (частью которой является таблица преобразования) хранится в файле KERNEL\ NLS_HC.ASM. Этот файл является копией одного из исходных файлов, находящихся в каталоге KERNEL\NLS. В стандартном ядре это KERNEL\NLS\001-437.HC. Насколько я понял из комментариев, для создания HC-файлов должна использоваться некая утилита, которая преобразует файл формата UNF (Universal NLS data Format) в ассемблерный листинг (HC). Никаких следов этой утилиты мне обнаружить не удалось, поэтому я решил взять в качестве исходного ассемблерный код файла NLS_HC.ASM и поменять таблицу непосредственно в нём. Структура файла вполне очевидна. Хотя, кроме заголовка, комментарии в файле отсутствуют, имеющиеся имена меток достаточно информативны. Таблица преобразования регистра символов имеет метку _nlsUpcaseHardcoded. Кроме этой таблицы, я также исправил таблицу порядка сортировки, которая помечена как _nlsCollHardcoded. Порядок сортировки в изменённом варианте используется не алфавитный, а по возрастанию расширенных ASCII-кодов символов. Для русской кодовой страницы 866 использование такого порядка даёт почти правильный результат, только в последовательность строчных русских букв вклинивается набор символов псевдографики (эти символы в именах файлов встречаются нечасто). Мой вариант файла NLS_HC.ASM можно скачать по ссылке http://vdruzhin.chat.ru/freedos/007-866.hc. После скачивания файл нужно переименовать и скопировать вместо оригинального NLS_HC.ASM.
№7(20), июль 2004
Я не разбирался, привязана ли жёстко команда SYS.COM к версии ядра, скорее всего нет, но на всякий случай я использую оба файла, собранные совместно. Чтобы проверить работоспособность скомпилированных файлов, можно использовать командный файл INSTALL.BAT. Вставьте в дисковод A: чистую дискету и, находясь в каталоге KE2034\BIN, выполните команду INSTALL. На дискету будет скопировано ядро, конфигурационные файлы и командный интерпретатор. Только при копировании файла COMMAND.COM возникает небольшая проблема. Дело в том, что команда SYS ищет этот файл в трёх местах – в исходном каталоге, в корневом каталоге исходного диска и в переменной среды «COMSPEC», при этом она не проверяет, принадлежит ли найденный файл именно FreeDOS. Так как в каталоге KE2034\BIN после завершения сборки никакого COMMAND.COM нет, найден и скопирован на дискету будет командный интерпретатор той операционной системы, под которой выполнялась компиляция. Не самое плохое решение, однако если окажется, что текущая операционная система – это не FreeDOS, а MS-DOS (или MS Windows), то загрузочная дискета будет неработоспособной, так как COMMAND.COM от Microsoft работает только в «своей» версии DOS. Чтобы исправить эту ситуацию, можно либо заранее скопировать в каталог KE2034\BIN командный интерпретатор от FreeDOS, либо после создания дискеты скопировать файл на неё, заменив вариант, записанный командой SYS. На инсталяционном компактдиске FreeDOS COMMAND.COM можно найти в каталоге isolinux\buildcd. Теперь осталось перезагрузить компьютер (не забыв в настройках BIOS Setup разрешить загрузку с дискеты) и убедиться в работоспособности скомпилированного ядра.
Ссылки: 1. 2. 3. 4.
FreeDOS – http://www.freedos.org OpenWatcom – http://www.openwatcom.org NASM (Netwide Assembler)- http://nasm.sourceforge.net UPX (Ultimate Packer for eXecutables) – http://upx.sourceforge.net
61
программирование
ТЕХНИКА ВНЕДРЕНИЯ КОДА В РЕ-ФАЙЛЫ И МЕТОДЫ ЕГО УДАЛЕНИЯ
Настоящая статья – попытка систематизировать и классифицировать существующие алгоритмы внедрения в PE32/PE64-файлы, способы визуальной идентификации потенциально опасного кода и методы его удаления. Публикация рассчитана на широкий круг читателей, знакомых с языком Си и имеющих опыт системного программирования на операционных системах семейства Windows 9x и Windows NT.
КРИС КАСПЕРСКИ Механизмы внедрения в PE-файлы весьма разнообразны, но довольно поверхностно описаны в доступной литературе. Имеющиеся источники либо катастрофически неполны, либо откровенно неточны, да к тому же рассеянны по сотням различных FAQ и tutorial. Приходится, выражаясь словами Маяковского, перелопачивать тонны словесной руды, прежде чем обнаружится нечто полезное. Данная работа представляет собой попытку систематизации и классификации всех известных способов внедрения. Статья будет интересна не только специалистам по информационной безопасности, занимающихся идентификацей и удаленем вирусов, но и разработчикам навесных защит конвертного типа и упаковщиков. Что касается вирусописателей. Если человек задался целью написать вирус, то он его напишет. Публикации подобного рода на это никак не влияют. Автор ни к чему не призывает и ни от чего не отговаривает. Это – прерогатива карательных органов власти, религиозных деятелей, ну и моралистов наконец. Моя же задача намного скромнее – показать, какие пути внедрения существуют, на что обращать внимание при поиске постороннего кода и как отремонтировать файл, угробленный некорректным внедрением.
Понятие X-кода и другие условные обозначения Код, внедряющийся в файл, мы будем называть X-кодом. Под это определение попадает любой код, внедряемый нами в файл-носитель (он же подопытный файл или файлхозяин от английского host-file), например, инструкция
62
NOP. О репродуктивных способностях X-кода в общем случае ничего не известно, и для успокоения будем считать X-код несаморазмножающимся кодом. Всю ответственность за внедрение берет на себя человек, запускающий программу-внедритель (нет, не «вредитель», а «внедритель» от слова «внедрить»), которая, предварительно убедившись в наличии прав записи в файл-носитель (а эти права опять-таки дает человек) и его совместимости с выбранной стратегией внедрения, записывает X-код внутрь файла и осуществляет высокоманевренный перехват управления, так что подопытная программа не замечает никаких изменений. Для экономии места в статье используется ряд общепринятых сокращений, перечисленных ниже: ! FA: File Alignment – физическое выравнивание секций; ! SA, OA: Section Alignment или Object Alignment – виртуальное выравнивание секций; ! RVA: Relative Virtual Address – относительный виртуальный адрес; ! FS: First Section – первая секция файла; ! LS: Last Section – последняя секция файла; ! CS: Current Section – текущая секция файла; ! NS: Next Section – следующая секция файла; ! v_a: Virtual Address – виртуальный адрес; ! v_sz: Virtual Size – виртуальный размер; ! r_off: raw offset – физический адрес начала секции; ! f_sz: raw size – физический размер секции; ! DDIR: DATA DIRECTORY – нет адекватного перевода; ! EP: Entry Point – точка входа.
программирование Под Windows NT, если не оговорено обратное, подразумевается вся линейка NT-подобных операционных систем: Windows NT 4.0/Windows 2000/Windows XP, а под Windows 9x – Windows 95, Windows 98 и Windows Me. Под системным загрузчиком понимается компонент операционной системы, ответственный за загрузку исполняемых файлов и динамических библиотек. Выше, левее, западнее – соответствует меньшим адресам, что совпадает с естественной схемой отображения дампа памяти отладчиком или дизассемблером.
! Поиском необходимых функций в таблице импорта
!
!
!
Ðèñóíîê 1. Óñëîâíûå ãðàôè÷åñêèå îáîçíà÷åíèÿ, ïðèíÿòûå â ñòàòüå
Цели и задачи Х-кода
!
Перед X-кодом стоят по меньшей мере три задачи: ! разместить свое тело внутри подопытного файла; ! перехватить управление до начала выполнения основной программы или в процессе оного; ! определить адреса API-функций, жизненно важных для собственного функционирования. Методология перехвата управления и определения адресов API-функций уже рассматривалась нами ранее в [1, 2, 3], поэтому здесь не описывается. Ограничимся тем, что напомним читателю основные моменты. Перехват управления обычно осуществляется следующими путями: ! переустановкой точки входа на тело X-кода; ! внедрением в окрестности оригинальной точки входа команды перехода на X-код (естественно, перед передачей управления X-код должен удалить команду, восстановив исходное содержимое EP); ! переустановкой произвольно взятой команды JMP/ CALL на тело X-кода с последующей передачей управления по оригинальному адресу (этот прием не гарантирует, что X-коду вообще удастся заполучить управление, но зато обеспечивает ему феноменальную скрытность и максимальную защищенность от антивирусов); ! модификацией одного или нескольких элементов таблицы импорта с целью подмены вызываемых функций своими собственными (этой технологией в основном пользуются stealth-вирусы, умело скрывающие свое присутствие в системе). Определение адресов API-функций обычно осуществляется следующими путями:
№7(20), июль 2004
!
файла-хозяина (будьте готовы к тому, что там их не окажется, либо отказывайтесь от внедрения, либо используйте другую стратегию поиска). Поиском LoadLibrary/GetProcAddress в таблице импорта файла-хозяина с последующим импортированием всех необходимых функций вручную (будьте готовы к тому, что и этих функций в таблице импорта также не окажется). Прямым вызовом API-функций по их абсолютным адресам, жестко прописанным внутри X-кода (адреса функций KERNEL32.DLL/NTDLL.DLL меняются от одной версии системы к другой, а адреса USER32.DLL и всех остальных пользовательских библиотек непостоянны даже в рамках одной конкретной системы и варьируются в зависимости от Image Base остальных загружаемых библиотек, поэтому при всей популярности данного способа пользоваться им допустимо только в образовательно-познавательных целях). Добавлением в таблицу импорта необходимых X-коду функций, которыми, как правило, являются LoadLibrary/ GetProcAddress, с их помощью можно вытащить из недр системы и все остальные (достаточно надежный, хотя и слишком заметный способ). Непосредственным поиском функций LoadLibrary/Get ProcAddress в памяти, поскольку KERNEL32.DLL проецируется на адресное пространство всех процессов, а ее базовый адрес всегда выровнен на границу в 64 Кб, от нас всего лишь требуется просканировать первую половину адресного пространства процесса на предмет поиска сигнатуры «MZ». Если таковая найдена – убеждаемся в наличии сигнатуры «PE», расположенной по смещению e_lfanew от начала базового адреса загрузки. Если она действительно присутствует, анализируем DATA DIRECTORY и определяем адрес таблицы экспорта, в которой требуется найти LoadLibraryA и GetProc Address. Если же хотя бы одно из этих условий не совпадает, уменьшаем указатель на 64 Кб и повторяем всю процедуру заново. Пара соображений в подмогу: прежде чем что-то читать из памяти, вызовите функцию IsBad ReadPtr, убедившись, что вы вправе это делать; помните, что Windows 2000 Advanced Server и Datacenter Server поддерживают загрузочный параметр /3GB, предоставляющий в распоряжение процесса 3 Гб оперативной памяти и сдвигающий границу сканирования на 1 Гб вверх; для упрощения отождествления KERNEL32.DLL можно использовать поле Name RVA, содержащееся в Export Directory Table и указывающее на имя динамической библиотеки, однако оно может быть и подложным (системный загрузчик его игнорирует). Определением адреса функции KERNEL32!_except_ handler3, на которую указывает обработчик структурных исключений по умолчанию. Эта функция не экспортируется ядром, однако присутствует в отладочной таблице символов, которую можно скачать со следующего сервера http://msdl.microsoft.com/download/symbols. (Внимание! Сервер не поддерживает просмотр браузером, и с ним работают только последние версии Microsoft Kernel Debugger и NuMega Soft-Ice). Это делается так:
63
программирование mov esi, fs:[0]/lodsd/lodsd
!
После выполнения кода регистр EAX содержит адрес, лежащий где-то в глубине KERNEL32. Выравниваем его по границе 64 Кб и ищем MZ/PE-сигнатуры, как показано в предыдущем пункте (это наиболее корректный и надежный способ поиска). Определением базового адреса загрузки KERNEL32.DLL через PEB: mov eax, fs:[30h]/mov eax, [eax + 0Ch]/mov esi, ↵ [eax + 1Ch]/lodsd/mov ebx, [eax + 08h]
!
Базовый код возвращается в регистре EBX (это очень простой, хотя и ненадежный прием, т.к. структура PEB в любой момент может измениться, и за все время существования Windows она уже менялась по меньшей мере три раза, к тому же PEB есть только в NT); Использованием native API операционной системы, взаимодействие с которым осуществляется либо через прерывание INT 2Fh (Windows 3.x, Windows 9x), либо через прерывание INT 2Eh (Windows NT, Windows 2000), либо через машинную команду syscall (Windows XP). Краткий перечень основных функций можно найти в Interrupt List Ральфа Брауна: http://www.pobox.com/~ralf/ files.html (это наиболее трудоемкий и наименее надежный способ из всех, мало того, что native API-функции не только недокументированы и подвержены постоянным изменениям, так они еще и до безобразия примитивны, т.е. реализуют простейшие низкоуровневые функции, непригодные к непосредственному использованию).
Принципы внедрения X-кода в PE-файлы с технической точки зрения практически ничем не отличаются от ELF, разве что именами служебных полей и стратегией их модификации. Однако детальный анализ спецификаций и дизассемблирование системного загрузчика выявляет целый пласт тонкостей, неизвестных даже профессионалам (во всяком случае ни один известный мне протектор/упаковщик не избежал грубых ошибок проектирования и реализации). Существуют следующие способы внедрения: ! размещение X-кода поверх оригинальной программы (так же называемое затиранием); ! размещение X-кода в свободном месте программы (интеграция); ! дописывание X-кода в начало, середину или конец файла с сохранением оригинального содержимого; ! размещение X-кода вне основного тела файла-носителя (например, в динамической библиотеке или NTFSпотоке), загружаемого «головой» X-кода, внедренной в файл одним из предыдущих способов. Поскольку первый способ приводит к необратимой потере работоспособности исходной программы и реально применяется только в вирусах, здесь он не рассматривается. Все остальные алгоритмы внедрения полностью или частично обратимы.
64
Требования, предъявляемые к X-коду Во-первых, X-код должен быть полностью перемещаем, т.е. сохранять свою работоспособность независимо от базового адреса загрузки. Это достигается использованием относительной адресации: определив свое текущее расположение вызовом команды: CALL $+5/POP EBP
X-код сможет преобразовать смещения внутри своего тела в эффективные адреса простым сложением их с EBP. Разумеется, это не единственная схема. Существуют и другие, однако мы не будем на них останавливаться, поскольку к PE-файлам они не имеют ни малейшего отношения. Во-вторых, грамотно сконструированный X-код никогда не модифицирует свои ячейки, поскольку не знает, имеются ли у него права на запись или нет. Стандартная секция кода лишена атрибута IMAGE_SCN_MEM_WRITE, и присваивать его крайне нежелательно, т.к. это не только демаскирует X-код, но и снижает иммунитет программыносителя. Разумеется, при внедрении в секцию данных это ограничение теряет свою актуальность, однако далеко не во всех случаях запись в секцию данных разрешена. Оптимизм – это прекрасно, но программист должен закладываться на наихудший вариант развития событий. Разумеется, это еще не означает, что X-код не может быть самомодифицирующимся или не должен модифицировать никакие ячейки памяти вообще! К его услугам и стек (автоматическая память), и динамическая память (куча), и кольцевой стек сопроцессора наконец! В третьих, X-код должен быть предельно компактным, поскольку объем пространства, пригодного для внедрения, подчас очень даже ограничен. Имеет смыл разбить X-код на две части: крошечный загрузчик и протяжный хвост. Загрузчик лучше всего разместить в PE-заголовке или регулярной последовательности внутри файла, а хвост сбросить в оверлей или NTFS-поток, комбинируя тем самым различные методы внедрения. Наконец, X-код не может позволить себе задерживать управление более чем на несколько сотых, ну от силы десятых долей секунд, в противном случае факт внедрения станет слишком заметным и будет сильно нервировать пользователя, чего допускать ни в коем случае нельзя.
Внедрение Перед внедрением в файл необходимо убедиться, что он не является драйвером, не содержит нестандартных таблиц в DATA DIRECTORY и доступен для модификации. Присутствие оверлеев крайне нежелательно, и без особой необходимости в оверлейный файл лучше ничего не внедрять, а если и внедрять, то придерживаться наиболее безболезненной стратегии внедрения – стратегии А (см. раздел «Классификация механизмов внедрения»). Ниже все эти требования разобраны подробнее: ! если файл расположен на носителе, защищенном от записи, или у нас недостаточно прав для его записи/ чтения (например, файл заблокирован другим процессом), отказываемся от внедрения;
программирование ! если файл имеет атрибут, запрещающий модифика! ! ! ! !
! !
цию, либо снимаем этот атрибут, либо отказываемся от внедрения; если поле Subsystem > 2h или Subsystem < 3h, отказываемся от внедрения; если FA < 200h или SA < 1000h – это, вероятнее всего, драйвер, и в него лучше ничего не внедрять; если файл импортирует одну или несколько функций из hal.dll и/или ntoskrnl.exe, отказываемся от внедрения; если файл содержит секцию INIT, он, возможно, является драйвером устройства, а возможно, и нет, но без особой нужды лучше сюда ничего не внедрять; если DATA DIRECTORY содержит ссылки на таблицы, использующие физическую адресацию, либо отказываемся от внедрения, либо принимаем на себя обязательства корректно «распотрошить» всю иерархию структур данных и скорректировать физические адреса; если ALIGN_UP(LS.r_off + LS.r_sz, A) > SizeOfFile, файл скорее всего содержит оверлей, и внедряться в него можно только по методу А; если физический размер одной или нескольких секций превышает виртуальный на величину большую или равную FA и при этом виртуальный размер не равен нулю, подопытный файл содержит оверлей, допуская тем самым использовать внедрения только типа А.
Следует помнить о необходимости восстановления атрибутов файла и времени его создания, модификации и последнего доступа (большинство разработчиков ограничивается одним лишь временем модификации, что демаскирует факт внедрения). Если поле контрольной суммы не равно нулю, следует либо оставить такой файл в покое, либо рассчитать новую контрольную сумму самостоятельно, например, путем вызова API-функции CheckSumMappedFile. Обнулять контрольную сумму, как это делают некоторые, категорически недопустимо, т.к. при активных сертификатах безопасности операционная система просто откажет файлу в загрузке! Еще несколько соображений общего типа. В последнее время все чаще и чаще приходится сталкиваться с исполняемыми файлами чудовищного объема, неуклонно приближающегося к отметке в несколько гигабайт. Обрабатывать таких монстров по кускам нудно и сложно. Загружать весь файл целиком – слишком медленно, да и позволит ли Windows выделить такое количество памяти! Поэтому имеет смысл воспользоваться файлами, проецируемыми в память (Memory Mapped File), управляемыми функциями CreateFileMapping и MapViewOfFile/UnmapView OfFile. Это не только увеличивает производительность, упрощает программирование, но и ликвидирует все ограничения на предельно допустимый объем, который теперь может достигать 18 экзобайтов, что соответствует 1 152 921 504 606 846 976 байтам. Как вариант можно ограничить размер обрабатываемых файлов несколькими мегабайтами, легко копируемыми в оперативный буфер и сводящими количество «обвязочного» кода к минимуму (кто работал с файлами от 4 Гб и выше, тот поймет).
№7(20), июль 2004
Предотвращение повторного внедрения В то время как средневековые алхимики пытались создать алмогест – универсальный растворитель, растворяющий все и вся, – их оппоненты язвительно замечали: задумайтесь, в чем вы его будете хранить? И хотя алмогест так и не был изобретен, его идея не умерла и до сих пор будоражит умы вирусописателей, вынашивающих идею принципиально недетектируемого вируса. Может ли существовать такой вирус хотя бы в принципе? И если да, то как он сможет отличать уже инфицированные файлы от еще не зараженных? В противном случае заражения одного и того же файла будут происходить многократно и навряд ли многочисленные копии вирусов смогут мирно соседствовать друг с другом. X-код, сохраняющий работоспособность даже при многократном внедрении, называют рентабельным. Рентабельность предъявляет жесткие требования как к алгоритмам внедрения в целом, так и к стратегии поведения X-кода в частности. Очевидно, что X-код, внедряющийся в MS-DOSзаглушку, рентабельным не является и каждая последующая копия затирает собой предыдущую. Протекторы, монополизирующие системные ресурсы с целью противостояния отладчикам (например, динамически расшифровывающие/ зашифровывающие защищаемую программу путем перевода страниц памяти в сторожевой режим с последующим перехватом прерываний), будут конфликтовать друг с другом, вызывая либо зависание, либо сбой программы. Классическим примером рентабельности является X-код, дописывающий себя в конец файла и после совершения всех запланированных операций возвращающий управление программеносителю. При многократном внедрении X-коды как бы «разматываются», передавая управление словно по эстафете, однако, если нить управления запутается, все немедленно рухнет. Допустим, X-код привязывается к своему физическому смещению, отсчитывая его относительно конца файла. Тогда при многократном внедрении по этим адресам будут расположены совсем другие ячейки, принадлежащие чужому X-коду, и поведение обоих станет неопределенным. Перед внедрением в файл нерентабельного X-кода необходимо предварительно убедиться, что в файл не был внедрен кто-то еще. К сожалению, универсальных путей решения не существует, и приходится прибегать к различным эвристическим приемам, распознающим присутствие инородного X-кода по косвенным признакам. Родственные X-коды всегда могут «договориться» друг с другом, отмечая свое присутствие уникальной сигнатурой. Например, если файл содержит строку «x-code ZANZIBAR here», отказываемся от внедрения на том основании, что здесь уже есть «свои». К сожалению, этот трюк очень ненадежен, и при обработке файла любым упаковщиком/протектором сигнатура неизбежно теряется. Ну разве что внедрить сигнатуру в ту часть секции ресурсов, которую упаковщики/протекторы предпочитают не трогать (иконка, информация о файле и т. д.). Еще надежнее внедрять сигнатуру в дату/время последней модификации файла (например, в десятые доли секунды). Упаковщики/ протекторы ее обычно восстанавливают, однако короткая длина сигнатуры вызывает большое количество ложных срабатываний, что тоже нехорошо.
65
программирование Неродственным X-кодам приходится намного хуже. Чужих сигнатур они не знают и потому не могут наверняка утверждать – возможно ли осуществить корректное внедрение в файл или нет? Поэтому X-код, претендующий на корректность, обязательно должен быть рентабельным, в противном случае сохранение работоспособности файлам уже не гарантировано. Упаковщики оказываются в довольно выигрышном положении: дважды один файл не сожмешь, и если коэффициент сжатия окажется исчезающе мал, упаковщик вправе отказаться обрабатывать такой файл. Протекторам – другое дело. Протектор, отказывающийся обрабатывать уже упакованные (зашифрованные) файлы, мало кому нужен. Если протектор монополизирует ресурсы, отказываясь их предоставлять кому-то еще, он должен обязательно контролировать целостность защищенного файла и, обнаружив внедрение посторонних, выводить соответствующее предупреждение на экран, возможно, прекращая при этом работу. В противном случае защищенный файл могут упаковать и попытаться защитить повторно. Последствия такой защиты не заставят себя ждать…
Классификация механизмов внедрения Механизмы внедрения можно классифицировать по-разному: по месту (начало, конец, середина), по «геополитике» (затирание исходных данных, внедрение в свободное пространство, переселение исходных данных на новое место обитания), по надежности (предельно корректное, вполне корректное и крайне некорректное внедрение), по рентабельности (рентабельное или нерентабельное) и т. д. Мы же будем отталкиваться от характера воздействия на физический и виртуальный образ подопытной программы, разделив все существующие механизмы внедрения на четыре категории, обозначенные латинскими буквами A, B, C и Z. ! К категории А относятся механизмы, не вызывающие изменения адресации ни физического, ни виртуального образов. После внедрения в файл ни его длина, ни количество выделенной при загрузке памяти не изменяется и все базовые структуры остаются на своих прежних адресах. Этому условию удовлетворяют внедрение в пустое место файла (PE-заголовок, хвосты секций, регулярные последовательности), внедрение путем сжатия части секции и создание нового NTFSпотока внутри файла1. ! К категории B относятся механизмы, вызывающие изменения адресации только физического образа. После внедрения в файл его длина увеличивается, однако количество выделенной при загрузке памяти не изменяется, и все базовые структуры проецируются по тем же самым адресам, однако их физические смещения изменяются, что требует полной или частичной перестройки структур, привязывающихся к своим физическим адресам. Если хотя бы одна из них останется нескорректированной (или будет скорректирована неправильно), файл-носитель с высокой степенью вероятности откажет в работе. Категории B соответствуют раздвижка заголовка, сброс части оригинального файла в оверлей и создание своего собственного оверлея.
66
! К категории C относятся механизмы, вызывающие из-
!
менения адресации как физического, так и виртуального образов. Длина файла и выделяемая при загрузке память увеличиваются. Базовые структуры могут либо оставаться на своих местах (т.е. изменяются лишь смещения, отсчитываемые от конца образа/файла), либо перемещаться по страничному имиджу произвольным образом, требуя обязательной коррекции. Этой категории соответствует расширение последней секции файла, создание своей собственной секции и расширение серединных секций. К «засекреченной» категории Z относятся механизмы, вообще не дотрагивающиеся до файла-носителя и внедряющиеся в его адресное пространство косвенным путем, например модификацией ключа реестра, ответственного за автоматическую загрузку динамических библиотек. Этой технологией интересуются в первую очередь сетевые черви и шпионы. Вирусы к ней равнодушны.
Категория A наименее конфликтна и приводит к отказу лишь тогда, когда файл контролирует свою целостность. Сфера применений категорий B и C гораздо более ограничена, в частности, она неспособна обрабатывать файлы с отладочной информацией, поскольку отладочная информация практически всегда содержит большое количество ссылок на абсолютные адреса. Ее формат не документирован, и к тому же различные компиляторы используют различные форматы отладочной информации, поэтому скорректировать ссылки на новые адреса нереально. Помимо отладочной информации еще существуют сертификаты безопасности и прочие структуры данных, нуждающиеся в неприкосновенности своих смещений. К сожалению, механизмы внедрения категории А налагают достаточно жесткие ограничения на предельно допустимый объем X-кода, определяемый количеством свободного пространства, имеющегося в программе, и достаточно часто здесь не находится места даже для крохотного загрузчика, поэтому приходится идти на вынужденный риск, используя другие категории внедрения. Кстати говоря, различные категории можно комбинировать друг с другом, осуществляя «гибридное» внедрение, наследующее худшие качества всех используемых механизмов, но и аккумулирующее их лучшие черты.
Категория A: внедрение в пустое место файла Внедрение в PE-заголовок Типичный PE-заголовок вместе с MS-DOS-заголовком и заглушкой занимает порядка 300h байт, а минимальная кратность выравнивания секций составляет 200h. Таким образом, между концом заголовка и началом первой секции практически всегда имеется 100h бесхозных байт, которые можно использовать для «производственных целей», размещая здесь либо всю внедряемую программу целиком, либо только загрузчик X-кода, считывающий свое продолжение из дискового файла или реестра.
программирование ! иначе:
Ðèñóíîê 2. Âíåäðåíèå X-êîäà â ñâîáîäíîå ïðîñòðàíñòâî õâîñòà PE-çàãîëîâêà
Внедрение. Перед внедрением в заголовок X-код должен убедиться, что хвостовая часть заголовка (ласково называемая «предхвостием») действительно свободна, т.е. SizeOfHeadres < FS.r_off. Если же SizeOfHeadres == FS.r_off вовсе не факт, что свободного места в конце заголовка нет. «Подтягивать» хвост заголовка к началу первой секции – обычная практика большинства линкеров, усматривающих в этом гармонию высшего смысла. Сканирование таких заголовков обычно выявляет длинную цепочку нулей, расположенных в его хвосте и, очевидно, никак и никем не используемых. Может ли X-код записать в них свое тело? Да, может, но только с предосторожностями. Необходимо отсчитать по меньшей мере 10h байт от последнего ненулевого символа, оставляя этот участок нетронутым (в конце некоторых структур присутствует до 10h нулей, искажение которых ни к чему хорошему не приведет). Некоторые программисты пытаются проникнуть в MSDOS-заголовок и заглушку. Действительно, загрузчик Windows NT реально использует всего лишь шесть байт: сигнатуру «MZ» и указатель e_lfanew. Остальные же его никак не интересует и могут быть использованы X-кодом. Разумеется, о последствиях запуска такого файла в голой MS-DOS лучше не говорить, но… MS-DOS уже давно труп. Правда, некоторые вполне современные PE-загрузчики дотошно проверяют все поля MS-DOS-заголовка (в особенности это касается win32-эмуляторов), поэтому без особой нужды лучше в них не лезть, а вот использовать для своих нужд MS-DOS-заглушку можно, пускай и не без ограничений. Многие системные загрузчики неспособны транслировать виртуальные адреса, лежащие к западу от PE-заголовка, что препятствует размещению в MS-DOSзаголовке/заглушке служебных структур PE-файла. Даже и не пытайтесь внедрять сюда таблицу импорта или таблицу перемещаемых элементов! А вот тело X-кода внедрять можно. Кстати говоря, при обработке файла популярным упаковщиком UPX X-код, внедренный в PE-заголовок, не выживает, поскольку UPX полностью перестраивает заголовок, выбрасывая оттуда все «ненужное» (MS-DOS-заглушку он, к счастью, не трогает). Упаковщики ASPack и tElock ведут себя более корректно, сохраняя и MS-DOS-заглушку, и оригинальный PE-заголовок, однако X-код должен исходить из худшего варианта развития событий. В общем случае внедрение в заголовок осуществляется так: ! считываем PE-заголовок и приступаем к его анализу; ! если SizeOfHeaders < FS.r_off и (SizeOfHeaders + sizeof(Xcode))< FS.r_off, то: ! увеличиваем SizeOfHeaders на sizeof(X-code) или же просто подтягиваем его к raw offset первой секции; ! записываем X-код на образовавшееся место;
№7(20), июль 2004
!
! сканируем PE-заголовок на предмет поиска непрерывной цепочки нулей и, если таковая будет действительно найдена, внедряем свое тело, начиная с 10h байта от ее начала; ! внедряем X-код в MS-DOS-заглушку, не сохраняя ее старого содержимого; если внедрение прошло успешно, перехватываем управление на X-код.
Идентификация пораженных объектов. Внедрение в PE-заголовок в большинстве случаев можно распознать и визуально. Рассмотрим, как выглядит в hex-редакторе типичный исполняемый файл (см. рис. 3): вслед за концом MS-DOS-заголовка, обычно содержащим в себе строку «This program cannot be run in DOS mode» (или что-то подобное), расположена «PE»-сигнатура, за которой следует немного мусора, щедро разбавленного нулями и плавно перетекающего в таблицу секций, содержащую легко узнаваемые имена .text, .rsrc и .data (если файл упакован, названия секций скорее всего будут другими). Иногда за таблицей секций присутствует таблица BOUND импорта с перечнем имен загружаемых динамических библиотек. Дальше, вплоть до начала первой секции, не должно быть ничего, кроме нулей, использующихся для выравнивания (отождествить начало первой секции легко, hiew ставит в этом месте точку). Если же это не так, то исследуемый файл содержит X-код (см. рис. 4). Восстановление пораженных объектов. Не все дизассемблеры позволяют дизассемблировать PE-заголовок. IDA PRO относится к числу тех, что позволяют, но делает это только в случаях крайней необходимости, когда точка входа указывает внутрь заголовка. Заставить же ее отобразить заголовок вручную, судя по всему, невозможно. HIEW в этом отношении более покладист, но RVAадреса и переходы внутри заголовка он не транслирует и их приходится вычислять самостоятельно. Дизассемблировав X-код и определив характер и стратегию перехвата управления, восстановите пораженный файл в исходный вид или потрассируйте X-код в отладчике, позволив ему сделать это самостоятельно, а в момент передачи управления оригинальной программе сбросьте дамп (разумеется, прогон активного X-кода под отладчиком всегда таит в себе угрозу и отлаживаемая программа в любой момент может вырваться из-под контроля, поэтому если вы хотя бы чуточку не уверены в себе, пользуйтесь дизассемблером, так будет безопаснее). Если X-код оказался утрачен, например, вследствие упаковки UPX, распакуйте файл и постарайтесь идентифицировать стартовый код оригинальной программы (в этом вам поможет IDA PRO), переустановив на него точку входа. Возможно, вам придется реконструировать окрестности точки входа, разрушенные командой перехода на X-код. Если исходный стартовый код начинался с пролога (а в большинстве случаев это так), то на ремонт файла уйдет совсем немного времени (первые 5 байт пролога стандартны и легко предсказуемы, обычно это 55 8B EC 83 EC, 55 8B EC 83 C4, 55 8B EC 81 EC или
67
программирование 55 8B EC 81 C4, правильный вариант определяется по правдоподобности размера стекового фрейма, отводимого под локальные переменные). При более серьезных разрушениях алгоритм восстановления становится неоднозначен, и вам, возможно, придется перебрать большое количество вариантов. Попробуйте отождествить компилятор и изучить поставляемый вместе с ним стартовый код – это существенно упрощает задачу. Хуже, если X-код внедрился в произвольное место программы, предварительно сохранив оригинальное содержимое в заголовке (которого теперь с нами нет). Возвратить испорченный файл из небытия скорее всего будет невозможно, во всяком случае, никаких универсальных рецептов его реанимации не существует. Некорректно внедренный X-код может затереть таблицу диапазонного импорта, обычно располагающуюся позади таблицы секций, и тогда система откажет файлу в загрузке. Это происходит, когда разработчик определяет актуальный конец заголовка по следующей формуле: e_lfanew + SizeOfOptionalHeader + 14h + NumberOfSections*40
которая, к сожалению, неверна. Как уже говорилось выше, любой компилятор/линкер вправе использовать все SizeOfHeaders байт заголовка. Если таблица диапазонного импорта дублирует стандартную таблицу импорта (а чаще всего это так), то простейший способ ремонта файла сводится к обнулению 0x11-го элемента DATA DIRECTORY, а точнее, ссылки на структуру IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT. Если же таблица диапазонного импорта содержит (точнее, содержала) уникальные динамические библиотеки, отсутствующие во всех остальных таблицах, то для восстановления достаточно знать базовый адрес их загрузки. При отключенном диапазонном импорте эффективные адреса импортируемых функций, жестко прописанные в программе, будут ссылаться на невыделенные страницы памяти и операционная система немедленно выбросит исключение, сообщая виртуальный адрес ячейки, к которой произошло обращение. Остается лишь найти динамическую библиотеку (и этой библиотекой скорее всего будет собственная библиотека восстанавливаемого приложения, входящая в комплект поставки), содержащую по
Ðèñóíîê 3. Òàê âûãëÿäèò òèïè÷íûé PE-çàãîëîâîê íåçàðàæåííîãî ôàéëà
68
программирование данному адресу более или менее осмысленный код, совпадающий с точкой входа в функцию. Зная имена импортируемых библиотек, восстановить таблицу диапазонного импорта не составит никакого труда. Для приличия (чтобы не ругались антивирусы) можно удалить неактивный X-код из файла, установив SizeOf Headers на последний байт таблицы секций (или таблицы диапазонного импорта, если она есть) и вплоть до FS.r_off, заполнив остальные байты нулями, символом «*» или любым другим символом по своему вкусу. Например, «посторонним вирусам вход воспрещен!». Ëèñòèíã 1. Äèçàññåìáëåðíûé ôðàãìåíò X-êîäà, âíåäðåííîãî â çàãîëîâîê (âñå êîììåíòàðèè ïðèíàäëåæàò Èäå) HEADER:01000300 HEADER:01000300 HEADER:01000300 HEADER:01000300
; ; ; ; ; ; ;
The code at 01000000-01000600 is hidden from normal disassembly and was loaded because the user ordered to load it explicitly <<<< IT MAY CONTAIN TROJAN HORSES, VIRUSES, AND DO HARMFUL THINGS >>>
HEADER:01000300 ; HEADER:01000300 HEADER:01000300 start: HEADER:01000300 HEADER:01000305 HEADER:01000306 HEADER:0100030C HEADER:0100030D HEADER:0100030E HEADER:0100030F
Внедрение в хвост секции Операционная система Windows 9x требует, чтобы физические адреса секций были выровнены по меньшей мере на 200h байт (Windows NT – на 002h), поэтому между секциями практически всегда есть некоторое количество свободного пространства, в котором легко затеряться. Рассмотрим структуру файла notepad.exe из поставки Windows 2000 (см. листинг 2). Физический размер секции .text превышает виртуальный на 6600h – 65CAh == 36h байт, а .rsec – аж на C00h! Вполне достаточный объем пространства для внедрения, не правда ли? Разумеется, такое везение выпадает далеко не всегда, но пару десятков свободных байт можно найти практически в любом файле. Ëèñòèíã 2. Òàê âûãëÿäèò òàáëèöà ñåêöèé ôàéëà notepad.exe Number 1 2 3
Name .text .data .rsrc
v_size 00065CA 0001944 0006000
RVA 0001000 0008000 000A000
r_size 0006600 0000600 0005400
r_offst 0000600 0006C00 0007200
flag 60000020 C0000040 40000040
public start call pop mov lodsd push lodsd push
$+5 ebp esi, fs:0 ebp eax
Ðèñóíîê 5. Âíåäðåíèå X-êîäà â õâîñò ñåêöèè, îñòàâøèéñÿ îò âûðàâíèâàíèÿ
Ðèñóíîê 4. Òàê âûãëÿäèò çàãîëîâîê ôàéëà ïîñëå âíåäðåíèÿ X-êîäà
№7(20), июль 2004
69
программирование Внедрение. Перед внедрением необходимо найти секцию с подходящими атрибутами и достаточным свободным пространством в конце или рассредоточить Xкод в нескольких секциях. При этом необходимо учитывать, что виртуальный размер секции зачастую равен физическому или даже превышает его. Это еще не значит, что свободное пространство отсутствует, – попробуйте просканировать хвостовую часть секции на предмет наличия непрерывной цепочки нулей – если таковая там действительно присутствует (а куда бы она делась?), ее можно безбоязненно использовать для внедрения. Правда тут есть одно «но», почему-то не учитываемое подавляющим большинством разработчиков: если виртуальный размер секции меньше физического, загрузчик игнорирует физический размер (хотя и не обязан это делать) и он может быть любым, в том числе и заведомо бессмысленным! Если виртуальный размер равен нулю, загрузчик использует в качестве него физический, округляя его на величину Section Alignment. Поэтому, если r_off + r_sz некоторой секции превышает r_off следующей секции, следует либо отказаться от обработки такого файла, либо самостоятельно вычислить физический размер на основе разницы raw offset двух соседних секций. Некоторые программы хранят оверлеи внутри файла (да, именно внутри, а не в конце!), при этом разница физического и виртуального размеров, как правило, оказывается больше кратности физического выравнивания. Такую секцию лучше не трогать, т.к. внедрение X-кода скорее всего приведет к неработоспособности файла. К сожалению, оверлеи меньшего размера данный алгоритм отловить не в состоянии, поэтому всегда проверяйте внедряемый участок на нули и отказывайтесь от внедрения, если здесь расположено что-то другое. Большинство разработчиков X-кода, проявляя преступную небрежность, пренебрегают проверкой атрибутов секции, что приводит к критических ошибкам и прочим серьезным проблемам. Внедряемая секция должна быть, во-первых, доступной (флаг IMAGE_SCN_MEM_READ установлен) и, во-вторых, невыгружаемой (флаг IMAGE_ SCN_MEM_DISCARDABLE сброшен). Желательно, но необязательно, чтобы по крайней мере один из флагов IMAGE_SCN_CNT_CODE, IMAGE_SCN_CNT_INITIALIZED_ DATA был установлен. Если же эти условия не соблюдаются и других подходящих секций нет, допустимо модифицировать флаги одной или нескольких секций вручную, однако работоспособность подопытного приложения в этом случае уже не гарантирована. Если флаги IMAGE_ SCN_MEM_SHARED и IMAGE_SCN_MEM_WRITE установлены, в такую секцию может писать кто угодно и что угодно, а во-вторых, адрес ее загрузки может очень сильно отличаться от v_a, поскольку та же Windows 9x позволяет выделять разделяемую память только во второй половине адресного пространства. Поскольку при внедрении в хвост секции невозможно отличить данные, инициализированные нулями, от неинициализированных данных, перед передачей управления основному коду программы X-код должен замести следы, аккуратно подчистив все за собой. Например, скопиро-
70
вать свое тело в стек или в буфер динамической памяти и вернуть нули на место. К сожалению, многие об этом забывают, в результате чего часть программ отказывает в работе. Код, внедренный в конец секции, как правило, выживает при упаковке или обработке файла протектором (т.к. внедренная область памяти теперь помечена как занятая). Исключение составляют служебные секции, такие как секция перемещаемых элементов или секция импорта, сохранять которые упаковщик не обязан и вполне может реконструировать их, выбрасывая оттуда все «ненужное». Обобщенный алгоритм внедрения выглядит приблизительно так: ! считываем PE-заголовок; ! анализируем Section Table, сравнивая физическую длину секций с виртуальной; ! ищем секции, у которых r_sz > v_sz, и записываем их в кандидаты на внедрение, предварительно убедившись, что в хвосте секции содержатся одни нули; ! если r_sz – v_sz >= FA, не трогаем такую секцию, т.к. скорее всего она содержит оверлей; ! если кворума набрать не удалось, ищем секции, у которых r_sz <= v_sz, и пытаемся найти непрерывную цепочку нулей в их конце; ! из всех кандидатов отбираем секции с наибольшим количеством свободного места; ! находим секцию, атрибуты которой располагают к внедрению (IMAGE_SCN_MEM_SHARED, IMAGE_SCN_MEM_ DISCARDABLE cброшены, IMAGE_SCN_MEM_READ или IMAGE_SCN_MEM_EXECUTE установлены, IMAGE_SCN_ CNT_CODE или IMAGE_SCN_CNT_INITIALIZED_DATA установлены), а если таких среди оставшихся кандидатов нет, либо корректируем атрибуты самостоятельно, либо отказываемся от внедрения; ! если v_sz != 0 и v_sz < r_sz, увеличиваем v_sz на sizeof(X-code) или подтягиваем к v_a следующей секции. Идентификация пораженных объектов. Распознать внедрения этого типа достаточно проблематично, особенно если X-код полностью помещается в первой кодовой секции файла, которой, как правило, является секция .text. Внедрение в секцию данных разоблачает себя наличием осмысленного дизассемблерного кода в ее хвосте, но если X-код перехватывает управление хитрым образом, дизассемблер может и не догадаться дизассемблировать этот код и нам придется сделать это вручную, самостоятельно отыскав точку входа. Правда, если X-код зашифрован и расшифровщик находится вне кодовой секции, этот прием уже не сработает. Внедрение во все служебные секции (например, секцию ресурсов или fixup) распознается по наличию в них чужеродных элементов, которые не принадлежат никакой подструктуре данных (см. рис. 6). Восстановление пораженных объектов. Чаще всего приходится сталкиваться с тем, что программист не предусмотрел специальной обработки для виртуального раз-
программирование мера, равного нулю, и вместо того чтобы внедриться в хвост секции, необратимо затер ее начало. Такие файлы восстановлению не подлежат и должны быть уничтожены. Реже встречается внедрение в секцию с «неудачными» атрибутами: секцию, недоступную для чтения, или DISCARDABLE-секцию. Для реанимации файла либо заберите у X-кода управление, либо отремонтируйте атрибуты секции. Могут также попасться файлы с неправильно «подтянутым» виртуальным размером. Обычно вирусописатели устанавливают виртуальный размер внедряемой секции равным физическому, забывая о том, что если r_sz < v_sz, то виртуальный размер следует вычислять исходя из разницы адресов виртуальных адресов текущей и последующей секции. К счастью, ошибки внедрения этого типа не деструктивны и исправить виртуальный размер можно в любой момент.
Внедрение в регулярную последовательность байт Цепочки нулей необязательно искать в хвостах секций. Дался нам этот хвост, когда остальные части файла ничуть не хуже, а зачастую даже лучше конца! Скажем больше, необязательно искать именно нули – для внедрения подходит любая регулярная последовательность (например, цепочка FF FF FF… или даже FF 00 FF 00…), которую мы сможем восстановить в исходный вид перед передачей управления. Если внедряемых цепочек больше одной, X-коду придется как бы «размазаться» по телу файла (а скорее всего так и будет). Соответственно стартовые адреса и длины этих цепочек при-
дется где-то хранить, иначе как потом прикажете их восстанавливать? Регулярные последовательности чаще всего обнаруживаются в ресурсах, а точнее – в bitmap и иконках. Технически внедриться сюда ничего не стоит, но пользователь тут же заметит искажение иконки, чего допускать ни в коем случае нельзя (даже если это и не главная иконка приложения, «Проводник» показывает остальные по нажатию кнопки «сменить значок» в меню свойств ярлыка). Существует и другая проблема: если регулярная последовательность относится к служебным структурам данных, анализируемых загрузчиком, то файл «упадает» еще до того, как X-код успеет восстановить эту регулярную последовательность в исходный вид. Соответственно если регулярная последовательность содержит какое-то количество перемещаемых элементов или элементов таблицы импорта, то в исходный вид ее восстанавливать ни в коем случае нельзя, т.к. это нарушит работу загрузчика. Поэтому поиск подходящей последовательности существенно усложняется, но отнюдь не становится принципиально невозможным! Правда, некоторые программисты исподтишка внедряются в таблицу перемещаемых элементов, необратимо затирая ее содержимое, поскольку, по их мнению, исполняемым файлам она не нужна. Варвары! Хоть бы удостоверились сначала, что 01.00.00.00h >= Image Base >= 40.00.00h, в противном случае таблица перемещаемых элементов реально нужна файлу! К тому же не все файлы с расширением EXE исполняемые. Под их личиной вполне может прятаться и динамическая библиотека, а динамическим
Ðèñóíîê 6. Îñìûñëåííûé ìàøèííûé êîä â õâîñòå ñåêöèè äàííûõ – ïðèçíàê âíåäðåíèÿ
№7(20), июль 2004
71
программирование библиотекам без перемещения – никуда. Кстати говоря, вопреки распространенному мнению, установка атрибута IMAGE_FILE_RELOCS_STRIPPED вовсе не запрещает системе перемещать файл, и для корректного отключения таблицы перемещаемых элементов необходимо обнулить поле IMAGE_DIRECTORY_ENTRY_BASERELOC в DATA DIRECTORY. Автор знаком с парой лабораторных вирусов, умело интегрирующих X-код в оригинальную программу и активно использующих строительный материал, найденный в теле файла-хозяина. Основной интерес представляют библиотечные функции, распознанные по их сигнатуре (например, sprintf, rand), а если таковых не обнаруживается, X-код либо ограничивает свою функциональность, либо реализует их самостоятельно. В дело идут и одиночные машинные команды, такие как CALL EBX или JMP EAX. Смысл этого трюка заключается в том, что подобное перемешивание команд X-кода с командами основной программы не позволяет антивирусам делить Xкод от файла. Однако данная техника еще не доведена до ума и все еще находится в стадии разработки…
Ðèñóíîê 7. Âíåäðåíèå X-êîäà â ðåãóëÿðíûå öåïî÷êè
Внедрение. Алгоритм внедрения выглядит приблизительно так: ! сканируем файл на предмет поиска регулярных последовательностей и отбираем среди них цепочки наибольшей длины, причем сумма их длин должна несколько превышать размеры X-кода, т.к. на каждую цепочку в среднем приходится 11 байт служебных данных: четыре байта – на стартовую позицию, один байт – на длину, один – на оригинальное содержимое и еще пять байт – на машинную команду перехода к другой цепочке; ! убеждаемся, что никакая часть цепочки не принадлежит ни одной из подструктур, перечисленных в DATA DIRECTORY (именно подструктур, а не структур, поскольку таблицы экспорта/импорта, ресурсов, перемещаемых элементов образуют многоуровневые древовидные иерархии, произвольным образом рассеянные по файлу, ограничиться одной лишь проверкой к принадлежности IMAGE_DATA_DIRECTORY.VirtualAddress и IMAGE_DATA_DIRECTORY.Size категорически недостаточно); ! проверяем атрибуты секции, которым принадлежит цепочка (IMAGE_SCN_MEM_SHARED, IMAGE_SCN_MEM_ DISCARDABLE cброшены, IMAGE_SCN_MEM_READ или IMAGE_SCN_MEM_EXECUTE установлены, IMAGE_ SCN_CNT_CODE или IMAGE_SCN_CNT_INITIALIZED_ DATA установлены); ! «нарезаем» X-код на дольки, добавляя в конец каждой из них команду перехода на начало следующей, не забывая о том, что тот jmp, который соответствует машинному коду EBh, работает с относительными адресами, и это те самые адреса, которые образуются
72
!
после загрузки программы в память. С «сырыми» смещениями внутри файла они вправе не совпадать. Как правильно вычислить относительный адрес перехода? Определяем смещение команды перехода от физического начала секции, добавляем к нему пять байт (длина команды вместе с операндом). Полученную величину складываем с виртуальным адресом секции и кладем полученный результат в переменную a1. Затем определяем смещение следующей цепочки, отсчитываемое от начала той секции, к которой она принадлежит, и складываем его с виртуальным адресом, записывая полученный результат в переменную a2. Разность a2 и a1 и представляет собой операнд инструкции jmp; запоминаем начальные адреса, длины и исходное содержимое всех цепочек в импровизированном хранилище, сооруженном либо внутри PE-заголовка, либо внутри одной из цепочек. Если этого не сделать, тогда X-код не сможет извлечь свое тело из файла-хозяина для внедрения во все последующие. Некоторые разработчики вместо команды jmp используют call, забрасывающий на вершину стека адрес возврата. Как нетрудно сообразить, совокупность адресов возврата представляет собой локализацию «хвостов» всех используемых цепочек, а адреса «голов» хранятся в операнде команды call! Извлекаем очередной адрес возврата, уменьшаем его на четыре и относительный стартовый адрес следующей цепочки перед нами!
Идентификация пораженных объектов. Внедрение в регулярную последовательность достаточно легко распознать по длинной цепочке jmp или call, протянувшихся через одну или несколько секций файла и зачастую располагающихся в совсем несвойственных исполняемому коду местах, например, секции данных (см. листинг 3). А если X-код внедрится внутрь иконки, она начинает характерно «шуметь» (см. рис. 8). Хуже, если одна регулярная цепочка, расположенная в кодовой секции, вмещает в себя весь X-код целиком – тогда для выявления внедренного кода приходится прибегать к его дизассемблированию и прочим хитроумным трюкам. К счастью, такие регулярные цепочки в живой природе практически не встречаются. Во всяком случае, просканировав содержимое папок WINNT и Program Files, я обнаружил лишь один такой файл, да и то деинсталлятор. Ëèñòèíã 3. Âíåäðåíèå X-êîäà â ðåãóëÿðíûå öåïî÷êè .0100A708: .0100A709: .0100A70A: .0100A70F: .0100A715: .0100A71B: .0100A721: .0100A726: .0100A727: .0100A72A:
9C 60 E80B000000 64678B260000 6467FF360000 646789260000 E800000000 5D 83ED23 EB2B
sub jmps
pushfd pushad call .00100A71A --- (1) esp,fs:[00000] d,fs:[00000] fs:[00000],esp call .00100A726 --- (2) pop ebp ebp,023 ;"#" .00100A757 -------- (3)
jmps
.00100A767
mov push mov
… .0100A757: EB0E …
-------- (1)
программирование .0100A767: 8BC5 .0100A769: EB2C
mov jmps
eax,ebp .00100A797
-------- (1)
jmps
.00100A7F7
-------- (1)
jmps
.00100A857
-------- (1)
jmps
.00100A897
-------- (1)
jmps
.00100A8D6
-------- (1)
jmps
.00100A8E5
-------- (1)
… .0100A797: EB5E … .0100A7F7: EB5E … .0100A857: EB3E … .0100A897: EB3D … .0100A8D6: EB0D … .0100A8E5: .0100A8EA: .0100A8F0: .0100A8F1: .0100A8F6: .0100A8FC: .0100A8FD: .0100A902:
2D00200000 89857E070000 50 0500100000 89857E070000 50 0500100000 EB31
mov mov jmps
sub eax,000002000 ;" " [ebp][00000077E],eax push eax add eax,000001000 ;" " [ebp][00000077E],eax push eax add eax,000001000 ;" " .00100A935 -------- (1)
Ðèñóíîê 8. Âíåäðåíèå X-êîäà â ãëàâíóþ èêîíêó ôàéëà
Восстановление пораженных объектов. Отличить и отделить фрагменты X-кода от фрагментов оригинального файла практически нереально. Да и нужно ли? Ведь достаточно отобрать у него управление… К счастью, таких изощренных X-кодов в дикой природе практически не встречается, и обычно они ограниваются внедрением в свободные с их точки зрения регулярные последовательности, которые вполне могут принадлежать буферам инициализированных данных, и если X-код перед передачей управления оригинальной программе не подчистит их за собой, ее поведение рискует стать совершенно непредсказуемым (она ожидала увидеть в инициализированной переменной ноль, а ей что подсунули?). Восстановление иконок и bitmap не представляет большой проблемы и осуществляется тривиальной правкой ресурсов в любом приличном редакторе (например, в Visual Studio). Задачу существенно упрощает тот факт, что все иконки обычно хранятся в нескольких экземплярах, выполненных с различной цветовой палитрой и разрешением. К тому же из всех регулярных последовательностей программисты обычно выбирают для внедрения нули, соответствующие прозрачному цвету в иконках и черному в bitmap. Сама картинка остается неповрежденной, но окруженной мусором, который легко удаляется ластиком. Если после удаления X-кода файл отказывается запускаться, просто смените редактор ресурсов либо воспользуйтесь hiew, при минимальных навыках работы с которым иконки можно править и в hex-режиме (считайте, что идете по стопам героев «Матрицы», рассматривающих окружающий мир через призму шестнадцатеричных кодов).
№7(20), июль 2004
Отдельный случай представляет восстановление таблицы перемещаемых элементов, необратимо разрушенных внедренным X-кодом. Если Image Base < 40.00.00h, такой файл не может быть загружен под Windows 9x, если в нем нет перемещаемых элементов. Причем поле IMAGE_ DIRECTORY_ENTRY_BASERELOC имеет приоритет над флагом IMAGE_FILE_RELOCS_STRIPPED, и если IMAGE_ DIRECTORY_ENTRY_BASERELOC != 0, а таблица перемещаемых элементов содержит мусор, то попытка перемещения файла приведет к непредсказуемым последствиям – от зависания до отказа в загрузке. Если это возможно, перенесите поврежденный файл на Windows NT, минимальный базовый адрес загрузки которой составляет 1.00.00h, что позволяет ей обходиться без перемещений даже там, где Windows 9x уже не справляется. X-код, не проверяющий флага IMAGE_FILE_DLL, может внедриться и в динамические библиотеки, имеющие расширение EXE. Вот это действительно проблема! В отличие от исполняемого файла, всегда загружающегося первым, динамическая библиотека вынуждена подстраиваться под конкретную среду самостоятельно, и без перемещаемых элементов ей приходится очень туго, поскольку на один и тот же адрес могут претендовать множество библиотек. Если разрешить конфликт тасованием библиотек в памяти не удастся (это можно сделать утилитой EDITBIN из SDK, запущенной с ключом /REBASE), придется восстанавливать перемещаемые элементы вручную. Для быстрого отождествления всех абсолютных адресов можно использовать следующий алгоритм: проецируем файл в память, извлекаем двойное слово, присваиваем ее переменной X. Нет, X не годится, возьмем Y. Если Y >= Image Base и Y <= (Image Base + Image Size), объявляем текущий адрес кандидатом в перемещаемые элементы. Смещаемся на байт, извлекаем следующее двойное слово и продолжаем действовать в том же духе, пока не достигнем конца образа. Теперь загружаем исследуемый файл в ИДУ и анализируем каждого кандидата на «правдоподобность» – он должен представлять собой смещение, а не константу (отличие констант от смещений подробно рассматривалось в «Фундаментальных основах хакерства» Криса Касперски). Остается лишь сформировать таблицу перемещаемых элементов и записать ее в файл. К сожалению, предлагаемый алгоритм чрезвычайно трудоемок и не слишком надежен, т.к. смещение легко спутать с константой. Но других путей, увы, не существует. Останется надеяться лишь на то, что X-код окажется мал и затрет не всю таблицу, а только ее часть.
Категория A: внедрение путем сжатия части файла Внедрение в регулярные последовательности фактически является разновидностью более общей техники внедрения в файл путем сжатия его части, в данном случае осуществляемое по алгоритму RLE. Если же использовать более совершенные алгоритмы (например, Хаффмана или Лемпеля-Зива), то стратегия выбора подходящих частей значительно упрощается. Давайте сожмем кодовую секцию, а на освободившееся место запишем свое тело. Легко в реализации, надежно в эксп-
73
программирование луатации! Исключение составляют, пожалуй, одни лишь упакованные файлы, которые уже не ужмешь, хотя… много ли X-коду нужно пространства? А секция кода упакованного файла по-любому должна содержать упаковщик, хорошо поддающийся сжатию. Собственно говоря, разрабатывать свой компрессор совершенно необязательно, т.к. соответствующий функционал реализован и в самой ОС (популярная библиотека lz32.dll для наших целей непригодна, поскольку работает исключительно на распаковку, однако в распоряжении X-кода имеются и другие упаковщики: аудио-/видео-кодеки, экспортеры графических форматов, сетевые функции сжатия и т. д.). Естественно, упаковка оригинального содержимого секции (или ее части) не обходится без проблем. Вопервых, следует убедиться, что секция вообще поддается сжатию. Во-вторых, предотвратить сжатие ресурсов, таблиц экспорта/импорта и другой служебной информации, которая может присутствовать в любой подходящей секции файла и кодовой секции в том числе. В-третьих, перестроить таблицу перемещаемых элементов (если, конечно, она вообще есть), исключая из нее элементы, принадлежащие сжимаемой секции и поручая настройку перемещаемых адресов непосредственно самому X-коду. Возникают проблемы и при распаковке. Она должна осуществляться на предельной скорости, иначе время загрузки файла значительно возрастет и пользователь тут же почует что-то неладное. Поэтому обычно сжимают не всю секцию целиком, а только ее часть, выбрав места с наибольшей степенью сжатия. Страницы кодовой секции от записи защищены, и попытка их непосредственной модификации вызывает исключение. Можно, конечно, при внедрении X-кода присвоить кодовой секции атрибут IMAGE_SCN_MEM_WRITE, но красивым это решение никак не назовешь: оно демаскирует X-код и снижает надежность программы. Это все равно что сорвать с котла аварийный клапан – так и до взрыва недалеко. Лучше (и правильнее!) динамически присвоить атрибут PAGE_ READWRITE вызовом VirtualProtect, а после завершения распаковки возвратить атрибуты на место.
Ðèñóíîê 9. Âíåäðåíèå X-êîäà ïóòåì ñæàòèÿ ñåêöèè
Внедрение. Обобщенный алгоритм внедрения выглядит так: ! открываем файл, считываем PE-заголовок; ! находим в таблице секций секцию с атрибутом IMAGE_ SCN_CNT_CODE (как правило, это первая секция файла); ! убеждаемся, что эта секция пригодна для внедрения (она сжимается, не содержит в себе никаких служебных таблиц, используемых загрузчиком, и не имеет атрибута IMAGE_SCN_MEM_DISCARDABLE);
74
! сжимаем секцию и размещаем X-код либо в начале секции, либо в ее конце;
! анализируем таблицу перемещаемых элементов и отбираем оттуда все элементы, относящиеся к сжатой части секции, и размещаем их внутри X-кода, а на освободившиеся места записываем IMAGE_REL_BASED_ ABSOLUTE – своеобразный аналог команды NOP для перемещаемых элементов. Идентификация пораженных объектов. Распознать факт внедрения в файл путем сжатия части секции трудно, но все-таки возможно. Дизассемблирование сжатой секции обнаруживает некоторое количество бессмысленного мусора, настораживающего опытного исследователя, но зачастую ускользающего от новичка. Разумеется, речь не идет о внедрении в секцию данных – присутствие постороннего кода в которой не заметит только слепой (однако если X-код перехватывает управление косвенным образом, он не будет дизассемблированной ИДОЙ и может прикинуться овечкой невинного массива данных). Обратите внимание на раскладку страничного имиджа. Если виртуальные размеры большинства секций много больше физических, файл, по всей видимости, сжат каким-либо упаковщиком. В несколько меньшей степени это характерно для протекторов, вирусы же практически никогда не уменьшают физического размера секций, т.к. для этого им пришлось бы перестраивать всю структуру заражаемого файла целиком, что не входит в их планы. Восстановление пораженных объектов. Типичная ошибка большинства разработчиков – отсутствие проверки на принадлежность сжимаемой секции служебным структурам (или некорректно выполненная проверка). В большинстве случаев ситуация обратима, достаточно обнулив все поля DATA DIRECTORY загрузить файл в дизассемблер, реконструировать алгоритм распаковщика и написать свой собственный, реализованный на любом симпатичном вам языке (например, ИДА-Си, тогда для восстановления файла даже не придется выходить из ИДЫ). Если же файл запускается вполне нормально, то для удаления X-кода достаточно немного потрассировать его в отладчике, дождавшись момента передачи управления оригинальной программе, и немедленно сбросить дамп.
Категория A: создание нового NTFS-потока внутри файла Файловая система NTFS поддерживает множество потоков в рамках одного файла, иначе называемых расширенными атрибутами (Extended Attributes) или именованными разделами. Безымянный атрибут соответствует основному телу файла, атрибут $DATE – времени создания файла и т. д. Вы также можете создавать и свои атрибуты практически неограниченной длины (точнее, до 64 Кб), размещая в них всякую всячину (например, Xкод). Аналогичную технику использует и Mac OS, только там потоки именуются труднопереводимым словом forks.
программирование Подробнее об этом можно прочитать в «Основах Windows NT и NTFS» Хелен Кастер, «Недокументированных возможностях Windows NT» А.В.Коберниченко и «Windows NT File System Internals» Rajeev Nagar. Сильной стороной этого алгоритма является высочайшая степень его скрытности, т.к. видимый объем файла при этом не увеличивается (под размером файла система понимает отнюдь не занимаемое им пространство, а размер основного потока), однако список достоинств на этом и заканчивается. Теперь поговорим о недостатках. При перемещении файла на не NTFS-раздел (например, дискету, zip или CD-R/RW) все рукотворные потоки бесследно исчезают. То же самое происходит при копировании файла из оболочки наподобие Total Commander (в девичестве Windows Commander) или обработке архиватором. К тому же полноценная поддержка NTFS есть только в Windows NT. Внедрение. Ввиду хрупкости расширенных атрибутов X-код необходимо проектировать так, чтобы пораженная программа сохраняла свою работоспособность даже при утрате всех дополнительных потоков. Для этого в свободное место подопытной программы (например, в PE-заголовок) внедряют крошечный загрузчик, который считывает свое продолжение из NTFS-потока, а если его там не окажется, передает управление программеносителю. Функции работы с потоками недокументированы и доступны только через Native-API. Это NtCreateFile, NtQuery EaFile и NtSetEaFile, описание которых можно найти, в частности, в книге «The Undocumented Functions Microsoft Windows NT/2000» Tomasz Nowak, электронная копия которой может быть бесплатно скачана с сервера NTinterlnals.net. Создание нового потока осуществляется вызовом функции NtCreateFile, среди прочих аргументов принимающий указатель на структуру FILE_FULL_EA_INFORMATION, передаваемый через EaBuffer. Вот она-то нам и нужна! Как вариант можно воспользоваться функцией NtSetEaFile, передав ей дескриптор, возвращенный NtCreateFile, открывающей файл обычным образом. Перечислением (и чтением) всех имеющихся потоков занимается функция NtQueryEaFile. Прототипы всех функций и определения структур содержатся в файле NTDDK.H, в котором присутствует достаточное количество комментариев, чтобы со всем этим хозяйством разобраться, однако до тех пор, пока Windows 9x не будет полностью вытеснена с рынка, подобная техника внедрения, судя по всему, останется невостребованной. Идентификация пораженных объектов. По непонятным маркетинговым соображениям штатные средства Windows не позволяют просматривать расширенные атрибуты файлов, мне также неизвестна ни одна утилита сторонних производителей, способная справиться с этой задачей, поэтому необходимый минимум программного обеспечения приходится разрабатывать самостоятельно. Наличие посторонних потоков внутри файла однозначно свидетельствует о его зараженности. Другой, не менее красноречивый признак внедрения, – обращение к функциям NtQueryEaFile/NtSetEaFile, кото-
№7(20), июль 2004
рое может осуществляться как непосредственным импортом из NTDLL.DLL, так и прямым вызовом: INT 2Eh.EAX=067h/INT 2Eh.EAX = 9Ch
а в Windows XP еще и машинной командой syscall. Возможен также вызов по прямым адресам NTDLL.DLL или динамический поиск экспортируемых функций в памяти. Восстановление пораженных объектов. Если после обработки упаковщиком/архиватором или иных внешне безобидных действий файл неожиданно отказал в работе, одним из возможных объяснений является гибель расширенных атрибутов. При условии, что потоки не использовались для хранения оригинального содержимого файла, у нас неплохие шансы на восстановление. Просто загрузите файл в дизассемблер и, проанализировав работу X-кода, примите необходимые меры противодействия. Более точной рекомендации дать, увы, не получается, поскольку такая тактика внедрения существует лишь теоретически и своего боевого крещения еще не получила. Для удаления ненужных потоков можно воспользоваться уже описанной функцией NtSetEaFile.
Категория B: раздвижка заголовка Никакой уважающий себя X-код не захочет зависеть от наличия свободного места в подопытном файле, поскольку это унизительно и вообще не по-хакерски. Когда пространства, имеющегося в PE-заголовке (или какой-либо другой части файла) оказывается недостаточно для размещения всего X-кода целиком, мы можем попробовать растянуть заголовок на величину, выбранную по своему усмотрению. До тех пор, пока SizeOf Headers не превышает физического смещения первой секции, такая операция осуществляется элементарно (см. «Внедрение в PE-заголовок»), но вот дальше начинаются проблемы, для решения которых приходится кардинально перестраивать структуру подопытного файла. Как минимум необходимо увеличить raw offset всех секций на величину, кратную принятой степени выравнивания, прописанной в поле File Alignment, и физически переместить хвост файла, записав X-код на освободившееся место. Максимальный размер заголовка равен виртуальному адресу первой секции, что и неудивительно, т.к. заголовок не может перекрываться с содержимым страничного имиджа. Учитывая, что минимальный виртуальный адрес составляет 1000h, а типичный размер заголовка – 300h, мы получаем в свое распоряжение порядка 3 Кб свободного пространства, достаточного для размещения практически любого X-кода. В крайнем случае можно поместить оставшуюся часть в оверлей. Хитрость заключается в том, что системный загрузчик загружает лишь первые SizeOfHeaders байт заголовка, а остальные (при условии, что они есть) оставляет в оверлее. Мы можем сдвинуть raw offset всех секций хоть на мегабайт, внедрив мегабайт X-кода в заголовок, но в память будет загружено только SizeOf Headers байт, а о загрузке остальных X-код должен позаботиться самостоятельно.
75
программирование К сожалению, одной лишь коррекции raw offset для сохранения файлу работоспособности может оказаться недостаточно, поскольку многие служебные структуры (например, таблица отладочной информации) привязываются к своему физическому местоположению, которое после раздвижки заголовка неизбежно отнесет в сторону. Правила этикета требуют либо скорректировать все ссылки на абсолютные физические адреса (а для этого мы должны знать формат всех корректируемых структур, среди которых есть полностью или частично недокументированные – взять хотя бы ту же отладочную информацию), либо отказаться от внедрения, если один или несколько элементов таблицы DATA DIRECTORY содержат нестандартные структуры (ресурсы, таблицы экспорта, импорта и перемещаемых элементов используют только виртуальную адресацию, поэтому ни в какой дополнительной корректировке не нуждаются). Следует также убедиться и в отсутствии оверлеев, поскольку многие из них адресуются относительно начала файла. Проблема в том, что мы не можем надежно отличить настоящий оверлей от мусора, оставленного линкером в конце файла, и потому приходится идти на неоправданно спекулятивные допущения, что все, что занимает меньше одного сектора, – не оверлей. Или же различимыми эвристическими методами пытаться идентифицировать мусор.
Ðèñóíîê 10. Ïîäîïûòíûé ôàéë è åãî ïðîåêöèÿ â ïàìÿòü äî è ïîñëå âíåäðåíèÿ X-êîäà ïóòåì ðàçäâèæêè çàãîëîâêà
Внедрение. Типичный алгоритм внедрения выглядит так:
! считываем PE-заголовок; ! проверяем DATA DIRECTORY на предмет присутствия
! !
! !
76
структур, привязывающихся к своему физическому смещению, и, если таковые обнаруживаются, либо отказываемся от внедрения, либо готовимся их скорректировать; если SizeOfHeaders = FS.v_a, отказываемся от внедрения, т.к. внедряться уже некуда; если SizeOfHeaders != FS.r_off первой секции, подопытный файл содержит оверлей и после внедрения может оказаться неработоспособным, однако, если от Size OfHeders до raw offset содержатся одни нули, внедряться сюда все-таки можно; если sizeof(X-code) <= FS.r_off, переходим к главе «внедрение в PE-заголовок»; если sizeof(X-code) <= FS.v_a, то: ! вставляем между концом заголовка и началом страничного имиджа ALIGN_UP((sizeof(X-code) + Size OfHeaders – FS.r_off), FA) байт, физически перемещая хвост файла; при загрузке файла весь X-код будет спроецирован в память;
!
! !
! увеличиваем поле SizeOfHeaders на заданную величину; иначе: ! вставляем между концом заголовка и началом страничного имиджа ALIGN_UP((sizeof(X-code) + SizeOf Headers – FS.r_off), FA) байт, физически перемещая хвост файла; при загрузке файла системный загрузчик спроецирует только первые FS.v_a – SizeOf Headers байт X-кода, а все последующие ему придется считывать самостоятельно; ! SizeOfHeaders := FS.v_a; увеличиваем raw offset всех секций на величину физической раздвижки файла; корректируем все структуры, привязывающие к физическим смещениям внутри файла, перечисленные в DATA DIRECTORY.
Идентификация пораженных объектов. Данный метод внедрения распознается аналогично обычному методу внедрения в PE-заголовок (см. «Внедрение в PE-заголовок») и по соображениям экономии места здесь не дублируется. Восстановление пораженных объектов. При растяжке заголовка с последующим перемещением физического содержимого всех секций и оверлеев вероятность нарушения работоспособности файла весьма велика, а причины ее следующие: неверная коррекция raw offset и привязка к физическим адресам. Ну против неверной коррекции, вызванной грубыми алгоритмическими ошибками, не пойдешь, и с испорченным файлом скорее всего придется расстаться (но все-таки попытайтесь, отталкиваясь от виртуальных размеров/адресов секций, определить их физические адреса или идентифицируйте границы секций визуальными методами, благо они достаточно характерны), а вот преодолеть привязку к физическим адресам можно! Проще всего это сделать, вернув содержимое секций/оверлеев на старое место. Последовательно сокращая размер заголовка на величину File Alignment и физически подтягивая секции на освободившиеся место, добейтесь его работоспособности. Ну а если не получится, значит, причина в чем-то еще.
Категория B: сброс части секции в оверлей Вместо того чтобы полностью или частично сжимать секцию, можно ограничиться переносом ее содержимого в оверлей, расположенный в конце, середине или начале файла. Дописаться в конец файла проще всего. Никакие поля PE-заголовка при этом корректировать не надо – просто копируем sizeof(X-code) байт любой части секции в конец файла, а на освободившееся место внедряем X-код, который перед передачей управления программе-носителю считывает его с диска, возвращая на исходное место. Сложнее разместить оверлей в середине файла, например, расположив его между секциями кода и данных, что обеспечит ему высокую степень скрытности. Для этого будет необходимо увеличить raw offset всех последующих секций на величину ALIGN_UP(sizeof(X-code), FA), физически сдвинув секции внутри файла. Аналогичным образом осуществляется и создание оверлея в заголов-
программирование ке, о котором мы уже говорили (см. «раздвижка заголовка»). При обработке файла упаковщиков оверлеи (особенно серединные) обычно гибнут, но даже если и выживают, то оказываются расположенными совершенно по другим физическим смещениям, поэтому X-код при поиске оверлея ни в коем случае не должен жестко привязываться к его адресам, вычисляя их на основе физического адреса следующей секции. Пусть длина оверлея составляет OX байт, тогда его смещение будет равно: NS.r_off – OX, а для последнего оверлея файла: SizeOfFile – OX. Оверлеи в заголовках намного более выносливы, но при упаковке UPX гибнут и они.
! при желании находим первый пролог и последний эпи-
!
!
!
Ðèñóíîê 11. Âíåäðåíèå X-êîäà â ôàéë ïóòåì ñáðîñà ÷àñòè ñåêöèè â îâåðëåé
Внедрение. Обобщенный алгоритм внедрения выглядит так: ! считываем PE-заголовок и приступаем к его анализу; ! если DATA DIRECTORY содержит ссылку на структуру, привязывающуюся к физическим смещениям, либо готовимся скорректировать ее надлежащим образом, либо отказываемся от внедрения; ! если LS.r_off + LS.r_sz > SizeOfFile, файл скорее всего содержит оверлей, и лучше отказаться от внедрения; ! если физический размер какой-либо секции превышает виртуальный на величину большую или равную File Alignment, файл скорее всего содержит серединный оверлей, и настоятельно рекомендуется отказаться от внедрения; ! выбираем секцию, подходящую для внедрения (IMAGE_SCN_MEM_SHARED, IMAGE_SCN_MEM_DIS CARDABLE cброшены, IMAGE_SCN_MEM_READ или IMAGE_SCN_MEM_EXECUTE установлены, IMAGE_SCN_ CNT_CODE или IMAGE_SCN_CNT_INITIALIZED_DATA установлены); которой, как правило, является первая секция файла; ! физическое смещение начала секции в файле равно ее raw offset (это надежное поле, и ему можно верить); ! физическое смещение конца секции в файле вычисляется более сложным образом: min(CS.raw offset + ALIGN_DOWN(CS.r_sz, FA), NS.raw_off); ! находим часть секции, не содержащую подструктур служебных таблиц PE-файла, таких, например, как таблицы импорта/экспорта; ! в выбранной части (частях) секции находим один или несколько регионов, свободных от перемещаемых элементов, а если это невозможно, выбираем эти элементы из fixup table для последующей обработки X-кодом вручную;
№7(20), июль 2004
лог внутри выбранных частей секции, чтобы линия «отреза» не разорвала функцию напополам (это не нарушит работоспособности файла, но сделает факт внедрения более заметным); если мы хотим создать оверлей внутри файла, то: ! увеличиваем raw offset всех последующих секций на величину ALIGN_UP(sizeof(X-code), FA); ! физически сдвигаем все последующие секции в файле на эту же величину; ! перемещаем выбранные части секции в оверлей, записывая их в произвольном формате, но так, чтобы сами потом смогли разобраться; иначе: ! дописываем выбранные части секции в конец файла, записывая их в произвольном формате, но так, чтобы сами потом смогли разобраться; на освободившееся место записываем X-код.
Идентификация пораженных объектов. Дизассемблирование таких файлов не выявляет ничего необычного: Xкод расположен в секции кода, там, где и положено всякому нормальному коду быть. Никакого подозрительного мусора также не наблюдается. Правда, обнаруживается некоторое количество перекрестных ссылок, ведущих в середину функций (и эти функции, как нетрудно догадаться, принадлежат X-коду, даже если он и обрежет выдираемые фрагменты секций по границам функций, смещения функций X-кода внутри каждого из фрагментов будут отличаться от оригинальных, вырезать каждую функцию по отдельности не предлагать – это слишком нудно), однако такое нередко случается и с заведомо не зараженными файлами, поэтому оснований для возбуждения уголовного дела как будто бы нет. Присутствие серединного оверлея легко распознать по несоответствию физических и виртуальных адресов, чего не наблюдается практически ни у одного нормального файла, однако наличие оверлея в конце файла – это нормально. Ничего другого не остается, как анализировать весь X-код целиком, и если манипуляции с восстановлением секции будут обнаружены – факт внедрения окажется разоблачен. X-код выдает себя вызовом функций Virtual Protect (присвоение атрибута записи) и GetCommandLine, GetModuleBaseName, GetModuleFullName или GetModule FullNameEx (определение имени файла-носителя). Убедитесь также, что кодовая секция доступна только лишь на чтение, в противном случае шансы на присутствие X-кода существенно возрастут (и ему уже будет не нужно вызвать VirtualProtect). Восстановление пораженных объектов. Обычно приходится сталкиваться с двумя алгоритмическими ошибками, допущенными разработчиками внедряемого кода: корректной проверкой на пересечение сбрасываемой части секции со служебными данными и внедрение в секцию с неподходящими атрибутами. Обе полностью обратимы. Реже встречаются ошибки определения длины сбрасываемой секции: если CS.v_sz < CS.r_sz и CS.r_off + CS.raw_sz > NS.raw_off, то системный загрузчик загружает лишь CS.v_sz байт секции, а внедряемый код сбрасывает CS.r_sz байт секции, захватывая кусочек следующей
77
программирование секции, не учитывая, что она может проецироваться совершенно по другим адресам и при восстановлении оригинального содержимого сбрасываемой секции, кусочек секции так и не будет восстановлен. Хуже того, X-код окажется как бы разорван двумя секциями напополам, и эти половинки могут находиться как угодно далеко друг от друга! Естественно, работать после этого он не сможет. Если же пораженный файл запускается нормально, для удаления X-кода просто немного потрассируйте его и, дождавшись момента передачи управления основной программе, снимите дамп.
Категория B: создание своего собственного оверлея Оверлей может хранить не только оригинальное содержимое секции, но и X-код! Правда, полностью вынести весь Xкод в оверлей не удастся – как ни крути, но хотя бы крохотный загрузчик в основное тело все-таки придется внедрить, расположив его в заголовке, предхвостии, регулярной последовательности или других свободных частях файла. Достоинства этого механизма в простоте его реализации, надежности, неконфликтности и т. д. Теперь уже не требуется анализировать служебные подструктуры на предмет их пересечения со сбрасываемой частью секции (мы ничего не сбрасываем!), и если случится так, что оверлей погибнет, загрузчик просто передаст управление основной программе без нарушения ее работоспособности. Располагать оверлей следует либо в конце файла, либо в его заголовке. Хоть это и будет более заметно, шансы выжить при упаковке у него значительно возрастут. Внедрение. Алгоритм внедрения полностью идентичен предыдущему за тем лишь исключением, что в оверлей сбрасывается не часть секции файла-хозяина, а непосредственно сам X-код, обрабатываемый специальным загрузчиком. Внедрение загрузчика обычно осуществляется по категории А (см. «Внедрение в пустое место файла»), хотя в принципе можно использовать и другие категории. Идентификация пораженных объектов. Внедрения этого типа легко распознаются визуально по наличию загрузчика, как правило, внедренного по категории А и присутствию сверления в начале, конце или середине файла. Восстановление пораженных объектов. Если X-код спроектирован корректно, для его удаления достаточно убить оверлей (например, упаковав программу ASPack со сброшенной галочкой «сохранять оверлеи»). Методика удаления загрузчика, внедренного по категории А, уже была описана выше, так что не будем повторяться.
Категория C: расширение последней секции файла Идея расширения последней секции файла не нова и своими корнями уходит глубоко в историю, возвращая нас во времена господства операционной системы MS-DOS и файлов типа OLD-EXE (помните историю с фальшивыми монетами, на которых было отчеканено 2000 г. до н. э.? Древние не знали, что они живут до нашей эры! OLD-EXE тогда еще не были OLD). Это наиболее очевидный и наиболее популярный алгоритм из всех алгоритмов внедрения вообще (часто даже
78
называемый «стандартным способом внедрения»), однако его тактико-технические характеристики оставляют желать лучшего: он чрезвычайно конфликтен, слишком заметен и реально применим лишь к некоторым PE-файлам, отвечающим всем предъявляемым к ним требованиям (зато он хорошо, безболезненно переносит упаковку и обработку протекторами). На первый взгляд идея не встречает никаких препятствий: дописываем X-код в хвост последней секции, увеличиваем размер страничного имиджа на соответствующую величину, не забывая о ее выравнивании и передаем на X-код управление. Никаких дополнительных передвижений одних секций относительно других осуществлять не нужно, а значит, не нужно корректировать и их raw offset. Проблема конфликтов со служебными структурами PE-файла также отпадает, и нам нечего опасаться, что X-код перезапишет данные, принадлежащие таблице импорта или, например, ресурсам. Но стоит только снять розовые очки и заглянуть в глаза реальности, как проблемы попрут изо всех щелей. А что, если конец последней секции не совпадает с концом файла? Может же там оказаться оверлей или просто мусор, оставленный линкером? А что, если последней секцией файла является секция неинициализированных данных или DISCARDABLE-секция, которая в любой момент может быть выгружена из файла? Внедряться в последнюю секцию файла не только технически неправильно, но и политически некорректно. Тем не менее и этот способ имеет право на существование, поэтому рассмотрим его поподробнее.
Ðèñóíîê 12. Âíåäðåíèå X-êîäà â ôàéë ïóòåì ðàñøèðåíèÿ ïîñëåäíåé ñåêöèè
Внедрение. Если физический размер последней секции, будучи выровненным на величину File Alignment, не «дотягивается» до физического конца файла, значит, X-код должен либо отказаться от внедрения, либо пристегивать свое тело не к концу секции, а к концу файла. Разница не принципиальна за исключением того, что оверлей теперь придется загружать в память, увеличивая как время загрузки, так и количество потребляемых ресурсов. Внедряться же между концом секции и началом оверлея категорически недопустимо, т.к. оверлеи чаще всего адресуются относительно начала файла (хотя могут адресоваться и относительно конца последней секции). Другая тонкость связана с пересчетом виртуального размера секции – если он больше физического (как чаще всего и случается), то он уже включает в себя какую-то часть оверлея, поэтому алгоритм вычисления нового размера существенно усложняется.
программирование С атрибутами секций дела обстоят еще хуже. Секции неинициализированных данных вообще не обязаны загружаться с диска (хотя 9x/NT их все-таки загружают), а служебные секции (например, секция перемещаемых элементов) реально востребованы системой лишь на этапе загрузки PE-файла, активны только на стадии загрузки, а дальнейшее их пребывание в памяти негарантированно, и X-код запросто может схлопотать исключение еще до того, как успеет передать управление основной программе. Конечно, X-код может скорректировать атрибуты последней секции по своему усмотрению, но это ухудшит производительность системы, и будет слишком заметно. Если физический размер последней секции равен нулю, что характерно для секций неинициализированных данных, лучше ее пропустить, внедрившись в предпоследнюю секцию. Типичный алгоритм внедрения выглядит так: ! загружаем PE-заголовок и анализируем атрибуты последней секции; ! если флаг IMAGE_SCN_MEM_SHARED установлен, отказываемся от внедрения; ! если флаг IMAGE_SCN_MEM_DISCARDABLE установлен, либо отказываемся от внедрения, либо самостоятельно сбрасываем его; ! если флаг IMAGE_SCN_CNT_UNINITIALIZED_DATA установлен, лучше всего отказаться от внедрения; ! если ALIGN_UP(LS.r_sz, FA) + LS.r_a > SizeOfFile, файл содержит оверлей, лучше отказаться от внедрения; ! если LS.v_sz > LS.r_rz, хвост секции содержит данные инициализированные нулями, и следует либо отказаться от внедрения, либо перед передачей управления подчистить все за собой; ! дописываем X-код к концу файла; ! устанавливаем LS.r_sz на SizeOfFile – LS.r_off; ! если LS.v_sz >= (LS.r_a + LS.r_sz + (SizeOfFile – (LS.r_a + ALIGN_UP(LS.r_sz, FA))), оставляем LS.v_sz без изменений; иначе LS.v_sz := 0; ! если LS.v_sz != 0 пересчитываем Image Size; ! при необходимости корректируем атрибуты внедряемой секции: сбрасываем атрибут IMAGE_SCN_MEM_ DISCARDABLE, и присваиваем атрибут IMAGE_SCN_ MEM_READ; ! пересчитываем Image Size. Идентификация пораженных объектов. Внедрения этого типа идентифицировать проще всего – они выдают себя присутствием кода в последней секции файла, которой обычно является либо секция неинициализированных данных, либо секция ресурсов, либо служебная секция, например, секция импорта/экспорта или перемещаемых элементов. Если исходный файл содержал оверлей (или мусор, оставленный линкером), он неизбежно перекрывается последней секцией. Восстановление пораженных объектов. Любовь начинающих программистов к расширению последней секции файла вполне объяснима (более или менее подробное описание этого способа внедрения можно найти практически в любом вирусном журнале), но вот алгоритми-
№7(20), июль 2004
ческие ошибки, совершенные ими непростительны и требуют сурового наказания, ну или по крайней мере общественного порицания. Чаще всего встречаются ошибки трех типов: неверное определение позиции конца файла, отсутствие выравнивания и неподходящие атрибуты секции, причем большая часть из них необратима, и пораженные файлы восстановлению не подлежат. Начнем с того, что LS.r_off + LS.r_sz не всегда совпадает с концом файла, и если файл содержит оверлей, он будет безжалостно уничтожен. Если LS.v_sz < LS.r_sz, то r_sz может беспрепятственно вылетать за пределы файла, и разработчик X-кода должен это учитывать, в противном случае в конце последней секции образуется каша. Очень часто встречается и такая ошибка: вместо того чтобы подтянуть LS.r_sz к концу X-кода, программист увеличивает LS.r_sz на размер X-кода, и, если конец последней секции не совпадал с концом оригинального файла, X-код неожиданно для себя окажется в оверлее! К счастью, этой беде легко помочь – просто скорректируйте поле LS.r_sz, установив его на действительный конец файла. Нередко приходится сталкиваться и с ошибками коррекции виртуальных размеров. Как уже говорилось, увеличивать LS.v_sz на размер X-кода нужно лишь тогда, когда LS.v_sz <= LS.r_sz, в противном случае виртуальный образ уже содержит часть кода или даже весь X-код целиком. Если LS.v_sz != 0, такая ошибка практически никак не проявляет себя, всего лишь увеличивая количество памяти, выделенной процессору, но если LS.v_sz == 0, после внедрения он окажется равным… размеру X-кода, который много меньше размера всей секции, в результате чего ее продолжение не будет загружено, и файл откажет в работе. Для возращения его в строй просто обнулите поле LS.v_sz или вычислите его истинное значение. После изменения виртуальных размеров секции требуется пересчитать Image Size, что многие программисты делают неправильно, либо просто суммируя виртуальные размеры всех секций, либо увеличивая его на размер внедряемого кода, либо забывая округлить полученный результат на границу 64 Кб, либо допуская другие ошибки. Правильный алгоритм вычисления Image Size выглядит так: LS.v_a + ALIGN_UP((LS.v_s)? LS.v_s:LS.r_sz, OA). Самый безобидный баг – неудачные атрибуты расширяемой секции, например, внедрение в DISCARDABLEсекцию, которой, в частности, является секция перемещаемых элементов, обычно располагающася в конце файла. Коррекция атрибутов должна решить эту проблему. Для удаления X-кода из файла просто отберите у него управление, отрежьте sizeof(X-code) байт от конца последней секции и пересчитайте значения полей: Image Base, LS.r_sz и LS.r_off.
Категория C: создание своей собственной секции Альтернативой расширению последней секции файла стало создание своей собственной секции, что не только «модно», но и технически более грамотно. Теперь, по крайней мере, ни оверлеи, ни таблицы перемещаемых элементов не будут понапрасну болтаться в памяти.
79
программирование
Ðèñóíîê 13. Âíåäðåíèå X-êîäà â ôàéë ïóòåì ñîçäàíèÿ ñîáñòâåííîé ñåêöèè
Внедрение. Обобщенный алгоритм выглядит так:
! загружаем PE-заголовок и смотрим, что расположено за таблицей секций;
! если здесь не нули, отказываемся от внедрения; ! если (e_lfanew + SizeOfOptionalHeader + 14h + (Number
! ! ! !
!
OfSections + 1)*40) > SizeOfHeaders, раздвигаем заголовок, как показано в разделе «Внедрение в PE-заголовок», а если это невозможно, отказываемся от заражения; дописываем X-код к концу файла; увеличиваем NumberOfSections на единицу; выравниваем LS.r_sz на величину FA; дописываем к таблице секций еще один элемент, заполняя поля следующим образом: ! имя: не имеет значения; ! v_a: LS.v_a + ALIGN_UP( (LS.v_sz)? LS.v_sz: LS.r_sz), Section Alignment); ! r_offset: SizeOfFile; ! v_sz: sizeof(X-code) или 0x0; ! r_sz: sizeof(X-code); ! Charic.: IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_ EXECUTE; ! остальные: 0x0; пересчитываем Image Size.
Идентификация пораженных объектов. Внедрения этого типа легко распознаются по наличию кодовой секции в конце файла (стандартно кодовая секция всегда идет первой). Восстановление пораженных объектов. Ошибочное определение смещения внедряемой секции обычно приводит к полной неработоспособности файла без малейших надежд на его восстановление (подробнее об этом мы уже говорили в предыдущем разделе). Ошибки остальных типов менее коварны. Живая классика – невыровненный физический размер предпоследней секции файла. Как уже говорилось выше, выравнивать физический размер последней секции необязательно, но при внедрении новой секции в файл последняя секция становится предпоследней со всеми отсюда вытекающими последствиями.
Категория C: расширение серединных секций файла Внедрение в середину файла относится к высшему пилотажу и обеспечивает X-коду наибольшую скрытность. Предпочтительнее всего внедряться либо в начало, либо
80
в конец кодовой секции, которой в подавляющем большинстве случаев является первая секция файла. Этот алгоритм наследует все лучшие черты создания оверлея в середине, многократно усиливая их: внедренный X-код принадлежит страничному имиджу, оверлея нет, поэтому нет и конфликтов с протекторами/упаковщиками. Внедрение в начало. Внедрение в начало кодовой секции можно осуществить двояко: либо сдвинуть кодовую секцию вместе со всеми последующими за ней секциями вправо, физически переместив ее в файле, скорректировав все ссылки на абсолютные адреса в страничном имидже, либо уменьшить v_a и r_off кодовой секции на одну и ту же величину, заполняя освободившееся место X-кодом, тогда ни физические, ни виртуальные ссылки корректировать не придется, т.к. секция будет спроецирована в память по прежним адресам. Легко показать, что перемещение кодовой секции при внедрении X-кода в ее начало осуществляется аналогично перемещению секции данных при внедрении кода в конец и поэтому во избежание никому не нужного дублирования описывается в одноименном разделе (см. «Внедрение в конец»), здесь же мы сосредоточимся на западной границе кодовой секции и технических аспектах ее отодвижения вглубь заголовка. Собственно говоря, вся проблема в том, что подавляющее большинство кодовых секций начинается с адреса 1000h, – минимально допустимого адреса, диктуемого выбранной кратностью выравнивания OA, так что отступать уже некуда – заголовок за нами. Здесь можно поступить двояко: либо уменьшить базовый адрес загрузки на величину, кратную 64 Кб, и скорректировать все ссылки на RVA-адреса (что утомительно, да и базовый адрес загрузки подавляющего большинства файлов – это минимальный адрес, поддерживаемый Windows 9x), либо отключить выравнивание в файле, отодвинув границу на любое количество байт, кратное двум (но тогда файл не будет запускаться под Windows 9x). Типовой алгоритм внедрения путем уменьшения базового адреса загрузки выглядит так: ! считываем PE-заголовок; ! если Image Base < 1.00.00h и перемещаемых элементов нет, отказываемся от внедрения; ! если Image Base <= 40.00.00h и перемещаемых элементов нет, лучше отказаться от внедрения, т.к. файл не сможет запускаться в Windows 9x; ! внедряем 1.00.00h байт в заголовок по методу, описанному в разделе «Раздвижка заголовка», оформляя все 1.00.00h байта как оверлей (т.е. оставляя SizeOf Headers неизменным), а если это невозможно, отказываемся от внедрения; ! уменьшаем FS.v_a и FS.r_off на 1.00.00h; ! увеличиваем FS.r_sz на 1.00.00h; ! если FS.v_sz не равен нулю, увеличиваем его на 1.00.00h; ! увеличиваем виртуальные адреса всех секций, кроме первой, на 1.00.00h; ! анализируем все служебные структуры, перечисленные в DATA DIRECTORY (таблицы экспорта, импорта, перемещаемых элементов и т. д.), увеличивая все RVAссылки на 1.00.00h;
программирование ! внедряем X-код в начало кодовой секции от FS.r_off !
! если таблица перемещаемых элементов присутству-
до FS.r_off + 1.00.00; пересчитываем Image Size.
Типовой алгоритм внедрения путем переноса западной границы первой секции, выглядит так: ! считываем PE-заголовок; ! если OA < 2000h лучше отказаться от внедрения, т.к. файл будет неработоспособен на Windows 9x, но если мы все-таки хотим внедриться, то: ! устанавливаем FA и OA равными 20h; ! для каждой секции: если NS.v_a – CS.v_a – CS.v_sz > 20h, подтягиваем CS.v_sz к NS.v_a – CS.v_a; ! для каждой секции: если v_sz > r_sz, увеличиваем длину секции на v_sz – r_sz байт, перемещая все остальные в физическом образе и страничном имидже; ! для каждой секции: если v_sz < r_sz, подтягиваем v_sz к NS.v_a – CS.v_a, добиваясь равенства физических и виртуальных размеров; ! внедряем в заголовок ALIGN_UP(sizeof(X-code), OA) байт, оформляя их как оверлей; ! уменьшаем FS.v_a и FS.r_off на ALIGN_UP(sizeof(Xcode), OA); ! внедряем X-код в начало первой секции файла; ! пересчитываем Image Size. Внедрение в конец. Чтобы внедриться в конец кодовой секции, необходимо раздвинуть нестраничный имидж, заново пересчитав ссылки на все адреса, т.к. старых данных на прежнем месте уже не будет. Задача кажется невыполнимой (встраивать в X-код полноценный дизассемблер с интеллектом ИДЫ не предлагать), но решение лежит буквально на поверхности. В подавляющем большинстве случаев для ссылок между секциями кода и данных используются не относительные, а абсолютные адреса, перечисленные в таблице перемещаемых элементов (при условии, что она есть). В крайнем случае абсолютные ссылки можно распознать эвристическими приемами: если (Image Base + Image Size) >= Z >= Image Size, то Z – эффективный адрес, требующий коррекции (разумеется, предложенный прием не слишком надежен, но все же он работает). Типовой алгоритм внедрения выглядит так: ! считываем PE-заголовок; ! если нет перемещаемых элементов, лучше отказаться от внедрения, т.к. файл может потерять работоспособность; ! находим кодовую секцию файла; ! если CS.v_sz ==0 или CS.v_sz >= CS.r_sz, увеличиваем r_sz кодовой секции файла; ! если CS.v_sz < CS.r_sz, CS.r_sz := NS.r_off + ALIGN_UP(sizeof(X-code), FA); ! если CS.v_sz < CS.r_sz, CS.v_sz := CS.r_sz; ! физически сдвигаем все последующие секции на ALIGN_UP(sizeof(X-code), FA) байт, увеличивая их r_off на ту же самую величину; ! сдвигаем все последующие секции в страничном имидже, увеличивая их v_a на ALIGN_UP(sizeof(X-code), OA) байт;
№7(20), июль 2004
!
ет, увеличиваем все абсолютные ссылки на перемещенные секции на ALIGN_UP(sizeof(X-code), OA) байт, если же таблицы перемещаемых элементов нет, используем различные эвристические алгоритмы; пересчитываем ImageSize.
Идентификация пораженных объектов. Данный тип внедрения до сих пор не выловлен в живой природе, поэтому говорить о его идентификации преждевременно. Восстановление пораженных объектов. Ни одного пораженного объекта пока не зафиксировано.
Категория Z: внедрение через автозагружаемые DLL Внедриться в файл можно, даже не прикасаясь к нему. Не верите? А зря! Windows NT поддерживает специальный ключ реестра, в котором перечислены DLL, автоматически загружающиеся при каждом создании нового процесса. Если Entry Point динамической библиотеки не равна нулю, она получит управление еще до того, как начнется выполнение процесса, что позволяет ей контролировать все происходящие в системе события (такие, например, как запуск антивирусных программ). Естественно, борьба с вирусами под их руководством ни к чему хорошему не приводит, и система должна быть обеззаражена. Убедитесь, что в HKLM\SOFTWARE\Microsoft\Windows NT\ CurrentVersion\Windows\AppInit_DLLs перечислены только легальные динамические библиотеки и нет ничего лишнего!
Заключение Хотя собранная автором коллекция методов внедрения претендует на полноту (если вам встречались программы, внедряющиеся в файл другим способом, пожалуйста, дайте об этом знать), технический прогресс не стоит на месте, и каждый день приносит новые технологии и идеи. Поэтому не воспринимайте эту статью как догму. Это всего лишь путеводитель по кибернетической стране виртуального мира.
Дополнительная литература: 1. Касперски К. Борьба с вирусами: опыт контртеррористических операций, журнал «Системый администратор», №10(11), октябрь, 2003 г. – 68-76 с. 2. Касперски К. Вирусы в UNIX, или Гибель Титаника II, журнал «Системый администратор», №1(14), январь, 2004 г. – 6-23 с. 3. Касперски К. Ошибки переполнения буфера извне и изнутри как обобщенный опыт реальных атак, журнал «Системый администратор», №3(16), март, 2004 г. – 64-72 с. 1
Как вариант, X-код может внедриться в хвост кластера, оккупируя один или несколько незанятых секторов (если они там есть), однако, здесь этот вариант не рассматривается, т.к. никакого отношения к PE-файлов он имеет.
81
на правах рекламы
ПЕРЕДОВАЯ ТЕХНОЛОГИЯ СТЕКИРОВАНИЯ КОММУТАТОРОВ – SINGLE IP MANAGEMENT ЯКОВ ЮНИЦКИЙ ЗАО «ТАЙЛЕ» Стекируемые коммутаторы существуют уже довольно давно и применяются сегодня во многих сетях. Технология стекирования пришла на смену технологии каскадирования. Сразу определим эти термины, чтобы увидеть разницу между технологиями. Каскадирование – последовательное объединение между собой двух и более устройств, с целью увеличения количества портов с использованием базовых стандартных интерфейсов, при этом устройства, входящие в каскад, управляются независимо друг от друга. Стекирование – объединение между собой двух или более устройств с целью увеличения количества портов с использованием базовых стандартных или специальных интерфейсов, при этом все компоненты стека являются одним логическим устройством, функционирующим как единое целое. Схем организации стека по сути две: ! Звезда – схема, в основе которой находится мастеркоммутатор, имеющий несколько интерфейсов для подключения к нему ведомых устройств. ! Кольцо – схема, в которой все устройства равнозначные и соединены последовательно друг с другом, а мастер-коммутатор определяется либо подключением кабеля для стекирования, либо в процессе «выборов». Другие возможные варианты сводятся к описанным выше. Обе схемы имеют свои достоинства и недостатки, но общим узким местом является длина стекирующего кабеля. Для преодоления этого ограничения разработчики внедряют технологии стекирования на основе стандартных базовых портов. Примером могут служить коммутаторы D-Link серии DES-3500 и DES-6500, поддерживающие технологию Single IP Management. Single IP Management (SIM) – это концепция стекирования коммутаторов друг с другом посредством стандартных Ethernet-портов вместо использования специальных портов или модулей. Устройства, поддерживающие эту технологию, формируют стек, предоставляя порты 10/100/ 1000 Мбит/с и возможность организации гигабитного подключения к магистрали. Вот несколько достоинств, реализованных в этой технологии: ! SIM уменьшает количество IP-адресов, использующихся в сети. ! Для объединения в стек SIM позволяет обойтись без специальных соединительных кабелей. Трафик, передаваемый между устройствами стека, проходит через интерфейсы Gigabit Ethernet с поддержкой полного дуплекса по обычным кабелям UTP или оптике. Отказ от использования специальных стекирующих кабелей позволяет устранить барьеры, связанные с их длиной.
82
!
В стек могут быть объединены устройства, расположенные в любом месте сети. SIM – это дополнительная функция коммутатора. Она может быть включена через управляющий интерфейс и не влияет на его базовые функции.
В коммутаторах с поддержкой Single IP Management (SIM) термин «стек» равнозначен термину «SIM-группа». (SIM-группа – это группа коммутаторов, которая управляется, как единое устройство.) Их общие характеристики выглядят так: ! Коммутаторы, входящие в стек, могут выполнять одну из трех функций: ! Commander Swith (CS) – мастер-коммутатор в группе. Ему присваивается IP-адрес. Один CS не может входить в разные SIM-группы. ! Member Swith (MS) – коммутатор, который распознается CS, как член SIM-группы. Один MS может входить только в одну SIM-группу. ! Candidate Switch (CaS) – коммутатор, подключенный физически к SIM-группе, но не определенный CS как ее член. Любой коммутатор в режиме CaS можно сконфигурировать на ручное или автоматическое подключение к SIM-группе. ! В SIM-группу может входить только один Commander Swith. ! Все коммутаторы SIM-группы должны входить в один широковещательный домен. ! В SIM-группу может входить до 32 коммутаторов (от 0 до 31), включая Commander Switch (под номером 0). ! Нет ограничений на количество SIM-групп в одном широковещательном домене, однако один коммутатор может входить только в одну группу. ! SIM позволяет включать промежуточные устройства, которые не поддерживают эту технологию. Это позволяет управлять коммутатором, удаленным более чем на один транзитный участок от CS. С помощью встроенного веб-менеджера можно получить информацию, представленную в виде дерева (Tree View) о членах стека и топологии сети, с указанием месторасположения устройств стека и связей между ними. Это простое и достаточно эффективное веб-управление исключает необходимость установки дорогого ПО для SNMP-управления. Виртуальный стек поддерживает любые модели коммутаторов с функцией Single IP Management. Это означает, что стек может быть расширен коммутаторами, включая коммутаторы 3-го уровня для ядра сети, коммутаторы на основе шасси или любые другие.
сети
METRO ETHERNET
ДЕНИС ЕЛАНСКИЙ Концепция Metro Ethernet По меркам современных систем передачи данных идеология Metropolitan Ethernet еще очень молода. В настоящее время происходит активное становление этого подхода к построению распределенных систем передачи данных. Этот процесс идет при поддержке производителей сетевого оборудования и международных комитетов в области связи. Базовую модель сети можно представить как объединение пользовательского оборудования (CE – Customer Equipment). Для данной процедуры используются сетевые интерфейсы пользователя (UNI – User Network Interface) и непосредственно Metro Ethernet Network (MEN).
Ðèñóíîê 1. Áàçîâàÿ ìîäåëü MEN
Причем в роли UNI используются стандартные получившие широчайшее распространение во всем мире платы Ethernet (10 Мб/с, 100 Мб/с, 1 Гб/с, 10 Гб/с). Учитывая пространственную разнесенность абонентов и потенциальное отсутствие непосредственных связей между ними, вводится такое понятие, как Ethernet Virtual Connection – EVC (виртуальное соединение Ethernet). При этом разделяют три типа виртуальных соединений: ! Point to point (точка-точка), ! Point to multipoint (точка-многоточка), ! Multipoint to multipoint (многоточка-многоточка). Данная терминология и сам подход к воплощению заимствованы из таких транспортных технологий, как Frame Relay и ATM.
Ethernet. История стандарта Реализация концепции MetroEthernet базируется на технологии Ethernet, которая известна уже более двадцати лет, но наиболее бурное развитие получила в поледние 8 лет.
84
За эти годы из низкоскоростной технологии локальных сетей, технологии, не обладающей возможностью обеспечить надежность и качество обслуживания, предназначенной для объединения небольшого числа компьютеров, она превратилась в технологию, обеспечивающую построение магистральных линий связи, работающих на сверхвысоких скоростях, поддерживающую параметры обслуживания, управляемую и снабженную множеством инструментов интеллектуального управления.
Ðèñóíîê 2. Àíàëîãèÿ MEN è Frame Relay à) Âèðòóàëüíûå ñîåäèíåíèÿ Frame Relay á) Âèðòóàëüíûå ÷àñòíûå ëèíèè íà áàçå ñåðâèñà E-Line
Несмотря на то что технология Ethernet (низкоскоростные сети 10Base) используются только для объединения периферийных пользователей, базовые принципы все равно наследуются более современными последователями этого популярного стандарта. Поэтому автор позволит себе обратиться к истории. Изначально Ethernet работал как единая шина и только один электрический сигнал мог распространяться по сети в каждый момент времени. Если же случалось, что в сеть было послано несколько сигналов, то они накладывались друг на друга и переставали быть распознаваемыми. Неудивительно, что был разработан способ передачи данных, учитывающий эту неприятную особенность – в противном случае пользы от таких сетей не было бы вовсе. Алгоритм, известный как CSMA/CD (множественный доступ с контролем несущей и с определением столкновений), определял порядок доступа к среде передачи. В упрощенном виде алгоритм доступа к среде выглядит примерно так:
сети ! Устройство, имеющее кадры для передачи, прослуши! ! !
вает сеть на предмет ее занятости. Если сеть свободна, отправитель начинает передачу. Во время процесса передачи отправитель продолжает прослушивать сеть, чтобы удостовериться в отсутствии столкновений. Если во время передачи произошла коллизия, передающая сторона случайным образом генерирует интервал ожидания, по истечении которого повторяет первую операцию.
Протокол CSMA/CD решает значительное число проблем, однако его использование делает сеть малоэффективной при большом количестве пользователей. Приведу две наиболее негативные особенности протокола: ! Все столкнувшиеся кадры уничтожаются, и их приходится пересылать повторно. Это приводит к росту задержек в сети. ! Задержка растет также и для станций, ожидающих права на передачу. Так как при возрастании количества участников обмена данными по сети трафик сети насыщается, то и время ожидания для каждой станции растет. Часть этих проблем решается применением коммутаторов, а именно: ! Не происходит коллизий, а соответственно нет необходимости в повторной трансляции утерянных по этой причине кадров. ! Сетевые устройства не тратят времени на ожидание освобождения среды передачи. ! Фактически можно достичь двойной номинальной скорости, учитывая возможность одновременной двунаправленной передачи данных.
Благодаря этим позитивным тенденциям технология Ethernet перестает быть технологией построения ЛВС, вторгаясь на рынок сетей масштаба города (Metropolitan Area Networks).
Привлекательность MetroEthernet Так что же дает концепция Metro Ethernet провайдерам? Почему все большее число производителей оборудования начинает поддерживать эту концепцию? Почему специалисты прогнозируют бурный рост сетей Metro Ethernet? По мнению экспертов компании In-Start/MDR, рост прибыльности сетей Metro Ethernet составит колоссальные значения и к 2008 году данная технология сравняется по распространенности с SDH-сетями. Подобные оценки основаны на том, что пропускная способность каналов Ethernet постоянно растет (достигая на данном этапе величины в 10 Гб/с), вместе с этим улучшаются надежностные показатели технологии, ее управляемость и масштабируемость. При этом с распространением новых сред передачи (например, оптического волокна) возросли перекрываемые дистанции.
Ðèñóíîê 3. Ðîñò äîõîäîâ îò óñëóã ãîðîäñêèõ ñåòåé Ethernet â ÑØÀ
Современные реализации Ethernet Gigabit Ethernet сохраняет черты своих более медленных предшественников, разве что использование концентраторов для подобных сетей практически нонсенс, несмотря на наличие оных. Отличительными особенностями этой технологии являются также используемые кабели и методы кодирования передаваемых сигналов. Стоит отметить, что развитие семейства стандартов Ethernet идет такими темпами, что уже сейчас широким спросом пользуются решения 10 Gigabit Ethernet, а производители оборудования ведут работы над созданием еще более продуктивной технологии. Помимо увеличения пропускной способности каналов связи в сетях Ethernet наблюдаются и другие, менее очевидные, но не менее значимые тенденции. Во-первых, диаметр сети более не ограничивается, во-вторых, длина сегмента сети также перестала быть определяющей величиной: используя волоконно-оптические кабели, можно перекрывать дистанции до 100 км. В-третьих, и это также нельзя не отнести к положительным сторонам технологии, оборудование становится все более интеллектуальным, при этом не дорожая, а наоборот, снижаясь в цене. Более того, интеллектуальность оборудования приводит к повышению надежности сети, равно как и к расширению спектра предоставляемых услуг.
№7(20), июль 2004
Ðèñóíîê 4. Ãèáêîñòü ðîñòà ïðîïóñêíîé ñïîñîáíîñòè â ñåòÿõ Ethernet
Помимо этого, технология Ethernet не просто доминирует на рынке ЛВС, а осталась практически единственной, сохранившейся до настоящего времени (более 90 % рынка ЛВС приходится на сети Ethernet). Все это лежит в основе перспективности технологии Metro Ethernet как для провайдеров, так и для абонентов. Что получают провайдеры от сетей Metro Ethernet? Высокоскоростную опорную сеть, сегменты которой могут достигать 100 км, а пропускная способность одного канала может достигать 10 Гб/с. Чем же привлекательна эта технология для
85
сети абонентов? Во-первых, абоненту не нужно заботиться о сопряжении своей внутренней ЛВС с сетью провайдера. Вовторых, возможно оплачивать только то количество услуг, которое необходимо в настоящее время, не заботясь об изменении интенсивности трафика в дальнейшем.
кетинговая политика плюс неудачи конкурентов сделали Cisco законодателем мод в сфере систем передачи данных. Однако превосходство над конкурентами перестает быть безграничным, о чем свидетельствуют экономические показатели за прошедший год.
Производители и оборудование
Extreme Networks
Кто же является основным игроком на рынке Ethernet-решений? Какие компании производят оборудование для MEN? Какие плюсы и минусы имеет каждый производитель друг относительно друга? На эти вопросы мы постараемся дать ответ в следующих частях данной статьи. Сейчас на рынке телекоммуникаций существует огромное количество фирм, занимающихся производством сетевого оборудования. Естественно, что их продукция различается по качеству, по надежности, по уровню технической поддержки, по перечню обеспечиваемых сервисов и по многим другим показателям. Мы не станем рассматривать продукцию фирм, занимающихся производством дешевых решений, предназначенных для построения небольших локальных сетей. Мы рассмотрим тех вендоров, которые работают для рынка Metro Ethernet, производя высокотехнологичное оборудование с выдающимися рабочими характеристиками. После введения такого строгого фильтра в рассмотрении останется не так уж много фирм, не больше десятка. К ним смело можно отнести таких гигантов, как: ! Cisco Systems (www.cisco.com) ! Extreme Networks (www.extremenetworks.com) ! Nortel Networks (www.nortelnetworks.com) ! Riverstone Networks (www.riverstonenet.com)
Созданная в 1995 году компания Extreme Networks с первых дней своего существования задумывалась как конкурент Cisco Systems в области магистральных решений. Стоит отметить, что за восемь лет своего существования инженеры и менеджеры компании справились с этой задачей, и получилось это у них весьма неплохо. Об этом говорит тот факт, что на традиционно сильном американском рынке, дающем большую часть дохода, позиции основных конкурентов были значительно ослаблены, и около 15% поставляемого в настоящее время оборудования производится Extreme Networks. Такое развитие событий имеет под собой две основные причины. С одной стороны, компания-производитель четко определила круг своих интересов, а именно – магистральные сети и интеграция ЛВС-пользователей. С другой стороны, в кратчайшие сроки были созданы три семейства оборудования, полностью отвечающего этим требованиям. С точки зрения функционала оборудование Extreme поддерживает все протоколы и обеспечивает все интерфейсы, использующиеся в настоящее время как операторами, так и потребителями телекоммуникационных услуг. Для унификации программной компоненты все оборудование работает под управлением единой операционной системы ExtremeWare, которая в свою очередь может администрироваться средствами системы Epicenter. Дополнительным бонусом этой системы является интеграция с HP-OpenView. В продуктовой линейке Extreme в настоящее время представлены три семейства оборудования: Summit, Alpine и Black Diamond. Такое разделение позволяет обеспечить весь спектр решений как для конечного пользователя, так и для крупного регионального оператора связи. При этом капиталовложения будут соответствовать решаемой задаче. Как говорилось ранее, основной приоритет Extreme – высокопроизводительные магистральные сети, построенные на базе стандарта Ethernet. Для этой технологии было создано оборудование, являющееся на сегодняшний день наиболее производительным (с точки зрения битовой коммутации) и оптимально расширяемым (одно шасси поддерживает до 48/480/2304 физических портов 10 Гб/с, 1 Гб/с, 100 Мб/с соответственно, а при использовании технологии CWDM число логических портов может превышать число физических портов в 8 раз). Помимо высокоскоростных портов Ethernet оборудование Extreme имеет возможность для интеграции с опорными сетями SDH. В наличии имеются интерфейсы STM-1, STM-4, а также интерфейс АТМ 3,2 Гб/с. Наличие SDH-интерфейсов позволят использовать устройства Black Diamond как для сопряжения с сетями SDH, так и для предоставления сервисов IPoSDH и SDHoIP. Помимо распространенных протоколов обмена цифровой информацией коммутаторы Extreme позволяют строить «пакетные кольца».
Cisco Systems Являясь одним из признанных лидеров на рынке телекоммуникаций, Cisco Systems предлагает для магистральных Ethernet-сетей два класса устройств. Это в первую очередь магистральные маршрутизаторы (такие как Cisco 7600OSR, 10000 и 12600) и высокопроизводительные коммутаторы уровня ядра (семейство Cisco Catalyst 6500). Оборудование обоих классов может использоваться в любом из узлов сети (даже в таких требовательных системах, как опорные сети операторов) с той лишь разницей, что каждое шасси поддерживает различные типы плат расширения. Òàáëèöà 1. Ñòàòèñòè÷åñêèå ïîêàçàòåëè Cisco
Характерными особенностями оборудования Cisco являются следующие черты: поддержка технологии MPLS, протоколов маршрутизации (RIP, OSPF, BGP, IGRP, EIGRP, ISIS), высокая масштабируемость, поддержка высокопроизводительных протоколов резервирования каналов передачи данных (протокол STP и его многочисленные модификации), наиболее высокая (до недавнего времени) плотность портов, единая операционная система IOS, высокая производительность и надежность. Все это, а также агрессивная мар-
86
сети В отличие от тех случаев, когда резервирование обеспечивается средствами протоколов из семейства STP (STP, RSTP, STP+), время восстановления маршрута для технологии «paket ring» не превышает 50 мс. Это позволяет говорить о гарантированном качестве, сравнимом с качеством в сетях SDH.
Nortel Networks Компания Nortel, уже давно известная в мире как один из основных производителей оборудования телефонии, после слияния с Bay Networks стала активно осваивать новый для себя сегмент рынка. Наибольшим вниманием у Nortel Networks пользуется направление Metro Ethernet. В рамках развития этой концепции было создано новое семейство магистральных коммутаторов OPTERA Metro. По технологичности производимого оборудования Nortel во многом превосходит своих конкурентов, однако несмотря на это существует ряд объективных факторов, снижающих популярность и распространенность оборудования OPTERA у операторов. Как и все крупные производители, Nortel обеспечивает своих потенциальных заказчиков всем необходимым. Оборудование этого производителя предоставляет инструменты для интеграции в высокоскоростные Ethernet-магистрали (1 Гб/с и 10 Гб/с), а также для сопряжения с магистралями SDH, равно как и для подключения абонентов. Поддерживается широчайший спектр существующих протоколов обмена, реализованы «proprietary»-протоколы, примером которых может служить протокол «пакетного кольца», аналогичный реализованному в оборудовании Extreme и обеспечивающий восстановление канала в пределах 50 мс. Создана единая операционная система, конфигурируемая как в консольном, так и в графических режимах. Оборудование добротно и перспективно. Но каждый параметр, по которому его можно оценить, имеет одно «но». Унификация оборудования произведена, однако заставить работать его в связке с оборудованием стороннего производителя не всегда просто. Графическая система управления хороша, но в гетерогенных сетях возникают затруднения в комбинированном управлении. Спектр предлагаемых интерфейсов велик, но плотность портов оставляет желать лучшего (по этому показателю коммутаторы Nortel Networks уступают не только аналогам Extreme, но и большинству других значимых производителей). Единственное, пожалуй, бесспорное преимущество – это реализация таких сервисов Metro Ethernet, как агрегация пользователей и обеспечение параметров трафика.
Riverstone Networks Руководство Riverstone позиционирует свое оборудование как капиталосберегающее решение для перехода от сетей, чувствительных к задержкам (приложения VoIP), к сетям, требующим сверхвысокой пропускной способности (приложения CRM и ERP). При этом компания не отказывается от поддержки приложений реального времени. Для воплощения этой стратегии было создано семейство магистральных коммутаторов XGS. В общих чертах коммутаторы XGS – это модульное оборудование, расширяемое по мере необходимости и оптимизированное для работы с потоками ин-
№7(20), июль 2004
формации 10 Гб/с. Программное обеспечение коммутаторов обеспечивает как потоковую коммутацию (line-rate switching), так и маршрутизацию трафика. Естественно, поддерживаются протоколы Q-in-Q и MPLS. Для всех устройств (как для коммутаторов серии XGS, так и для маршрутизаторов серии RS) существует единая графическая система управления, работающая под управлением унифицированной операционной системы Riverstone. К недостаткам оборудования Riverstone нельзя не отнести следующее: высокопроизводительная серия только анонсирована – даже на официальном сайте нет подробного описания данного оборудования. А маршрутизаторы серии RS значительно уступают (по некоторым показателям на порядок) оборудованию других производителей. Так, например, битовая коммутация происходит на скорости всего лишь 170 Гб/с, в то время как Black Diamond 10800 (Extreme Networks) выполняет аналогичную процедуру на скорости 1600 Гб/с. Помимо этого, маршрутизаторы серии RS поддерживают стандарт SDH только до 16-го уровня, что не позволяет встраивать их в кольца магистралей STM64. Кроме этого, анонсированное семейство XGS вообще осталось без модулей SDH. Возможно, по мере развития спроса и популярности Riverstone пойдет на расширение своего модельного ряда оборудованием для интеграции с сетями SDH. На сегодняшний же день доступное оборудование серии RS не может в полной мере считаться аппаратурой операторского класса. Для формирования центров обработки данных такие коммутаторы подойдут практически идеально (при высокой надежности, достаточной производительности и масштабируемости они имеют относительно низкую стоимость) либо для построения узлов доступа к сетям MAN/MEN, но для использования в качестве магистрального это оборудование еще не готово.
Сравнение производителей Любое оборудование, любая компания, его производящая, имеют свои преимущества и недостатки. Если в разделе «Производители и оборудование» я постарался рассказать об оборудовании вообще, то в этой главе акценты будут поставлены несколько иначе. А именно: в чем преимущества не оборудования (здесь не может быть однозначного мнения), а производителей, где их сильные и слабые стороны?
Cisco. Pro et Contra Несомненным преимуществом компании является гарантированное качество и сверхширокий ассортимент. На данном этапе не существует такой задачи, которую было бы нельзя решить на оборудовании Cisco. Вторым существенным плюсом, позволяющим компании удерживать ведущие позиции на рынке, является стратегия работы с партнерами. Работая с крупными партнерами, Cisco обращает внимание как на руководящий (как правило, нетехнический) персонал, так и на инженеров, призванных работать непосредственно с оборудованием. Для руководителей компаний Cisco Systems представляется как крупнейшее наукоемкое производство, заботящееся о своих партнерах. В то же время для людей, обеспечивающих работоспособность сетей, построенных на оборудовании
87
сети Cisco, разработана система оценки уровня подготовленности. Вот почему инженеры, прошедшие обучение и сертификацию в Cisco, предпочитают консольные решения Cisco, а не легко управляемое в среде Web оборудование, выполненное в духе открытых стандартов. Третьим сильным местом является широчайший ассортимент, однако уже не с точки зрения покупателя, а с точки зрения конкурентов. Ведь если рынок насыщен (порой до 50%) оборудованием одного производителя, то возникает вопрос совместимости вновь предлагаемого с уже работающим оборудованием. Фактически это целенаправленное выдавливание конкурентов с рынка. Четвертым плюсом является производительность магистрального оборудования. Политика компании нацелена на то, чтобы средневзвешенная производительность оборудования Cisco была бы максимальной в отрасли. Но не только плюсами обладает Cisco. К недостаткам компании нельзя не отнести высокие (а порой и сверхвысокие) цены на оборудование. Это превышение колеблется в среднем от 15 до 50 % и распространяется как на оборудование, так и на сервис. В условиях партнерских соглашений цены естественно снижаются, но стоимость владения по-прежнему остается на очень высоком уровне. Как уже упоминалось, оборудование Cisco очень дорого в эксплуатации, т.к. требуются дорогостоящее обучение персонала, высокие затраты на сервисные работы и на техническую поддержку. Иерархическая модель Cisco не только упрощает построение сетей, но и требует более широкого спектра оборудования. При этом от уровня к уровню возникают сложности с совместимостью с третьими производителями. Это приводит к тому, что, став однажды клиентом Cisco Systems, потребитель становится заложником этой компании. Многие технологии Cisco не являются стандартными. Говоря о лидировании в области стандартов, часто забывают о том, что многие из этих стандартов не являются признанными. Если подходить к вопросу более формально, стоит говорить о лидерстве в применении конкретной технологии, ее частной реализации.
Помимо означенных выше преимуществ Extreme взяла на вооружение идеологию Cisco в общении с потенциальными партнерами. Этот производитель развернул широкомасштабную программу подготовки инженеров с последующей сертификацией. С разработкой нового оборудования компания развивает не только аппаратные возможности, но и стремится к усовершенствованию существующих протокольных возможностей. Более того, ведущие инженеры компании участвуют в технологических форумах. Это позволяет незамедлительно реализовывать все новейшие достижения. В отличие от большинства производителей Extreme Networks не стремится к получению сверхприбылей за счет сверхвысоких цен. Возможно, это временное явление, связанное с необходимостью расширения рынков сбыта. Еще одним положительным моментом является наличие мощного графического средства управления сетью. Этот инструмент весьма универсален (во всяком случае сравним по эффективности с Cisco Works), при этом значительно менее ресурсоемок. К слабым сторонам нельзя не отнести отсутствие инфраструктуры в Европе вообще и в России в частности. Фирма только начинает делать первые шаги в этом направлении. Поэтому нет ни представительства, ни сертифицированных партнеров, ни технической поддержки. Помимо отсутствия развитой структуры представительств и партнерских отношений настораживает сравнительная молодость производства вообще и продукции в частности. Если фирмы, созданные 20, 30 лет назад, уже определили концепцию развития и обкатали стратегию совершенствования аппаратно-программного обеспечения, то относительно молодые производства еще не до конца отладили программную компоненту своей продукции, следствием чего является необходимость частого обновления программного обеспечения. Хотя в случае с Extreme Networks эта закономерность прослеживается не очень ярко: обновления ПО выходят только при появлении нового аппаратного обеспечения.
Nortel Networks. Альтернативный выбор Extreme Networks. За и против Extreme Networks, основанная в 1995 году уступает по масштабам и именитости трем остальным компаниям. Однако в последние годы наметился бурный рост качества, а вместе с ним и распространенности оборудования этого производителя. В пользу компании говорит уже тот факт, что за 9 лет существования ее продукция отвоевала у мировых гигантов 9 % рынка, доведя этот показатель по некоторым отраслям до 18 %. Характерен и тот факт, что оборудование Extreme Networks (а именно магистральные коммутаторы семейства Black Diamond) было использовано при построении вычислительной сети министерства обороны США. Также в пользу этого вендора говорит и то, что в программном обеспечении коммутаторов реализованы все стандартные протоколы. Более того, многие получившие распространение, но не принятые в качестве стандарта технологии нашли отражение в программном обеспечении Extreme Networks.
88
Унаследовав всю мощь этого гиганта, Nortel стал одним из лидеров в сфере производства и разработки систем передачи данных. Касательно оборудования Nortel можно констатировать следующее: у него нет ярко выраженных недостатков, но и с преимуществами тоже не все очевидно. Если бы это были автомобили, можно было бы сказать – «добротный автомобиль». Это оборудование имеет весь необходимый перечень функций, которые могут понадобиться. Nortel ведет собственные разработки, реализует сторонние достижения, но не более того. Что касается архитектуры шасси, то и здесь отношение к производителю можно определить как «не хуже, чем у других». Но и не лучше. Это иллюстрируется ситуацией с плотностью и качеством портов: при производстве задействованы самые современные технологии, управление отдельными узлами совершенно, но плотность портов из расчета на шасси хоть и велика, но уступает показателям Extreme и Cisco, а с точки зрения управляемости композиция добротных элемен-
сети тов управления в результате все же не дотягивают до высококлассной системы управления. Что касается отношений с потенциальными заказчиками, то сертификация хотя и существует (сертификационные центры Nortel представлены не только за рубежом, но и на территории России), но ведется недостаточно агрессивно. Если обратить внимание на экономические показатели, то заметно, что при отсутствии деградации показателей развития какого-либо существенного прироста не наблюдается. Большая часть роста компании обусловлена расширением рынка оборудования телефонии.
Riverstone Networks. Преимущества и недостатки Как говорилось ранее, оборудование Riverstone предназначено для миграции к более ресурсоемким приложениям. При этом во главу угла ставится экономичность предлагаемых решений. Этот факт имеет двойственную оценку. С одной стороны, компания заботится о своих потребителях, но, с другой стороны, – она нацелена не на рынок операторов, а на рынок центров обработки данных, крупных корпоративных пользователей и позиционируется как средство для построения узлов доступа к магистральным сетям. Исходя из официальных материалов, предлагаемых на сайте производителя, можно понять, что Riverstone находится на расстоянии трех лет (что по меркам сверхбыстро развивающегося производства очень большой срок) от конкурентов. Это предположение может быть обосновано хотя бы тем, что серия маршрутизаторов Cisco 12000 была заявлена еще в 2001 году, а в дальнейшем происходило только ее совершенствование, в то время как появление конкурентоспособной серии XGS от Riverstone анонсировано только сейчас. При этом стоит помнить, что лидирует по техническим показателям Black Diamond 10800 (Extreme Networks) – магистральный коммутатор, которому уже больше полугода. Возможно, в случае успешного продвижения на рынке семейства XGS Riverstone сменит акценты и перейдёт к производству магистрального оборудования для высокопроизводительных сетей с поддержкой широкого спектра протоколов физического и логического уровней. Но сейчас это не оборудование операторского класса. Слишком много вопросов, связанных с политикой компании, возникает у потенциальных потребителей. Практически не существует программы сертификации инженеров. Партнерские программы развиты слабо: большая часть стратегических партнеров Riverstone – американские компании. Закрытость от потенциального потребителя, отсутствие оборудования уровня доступа приводит к изолированности и безвестности. Что в свою очередь приводит к подсознательному недоверию.
Эпилог Подводя итог сравнению оборудования разных фирм, можно сделать несколько выводов. Совершенно очевидно, что
№7(20), июль 2004
оборудование разных производителей выравнивается по качеству и по рабочим параметрам. Другим характерным умозаключением является то, что, выходя на новые рынки (в нашем случае – на рынок сетей Metro Ethernet), они оказываются практически в равных условиях – всем приходится начинать с нуля. Об этом стоит помнить при выборе оборудования и поставщика. Третьим интересным выводом является то, что в новых сферах производства признанные лидеры теряют свои, казалось бы незыблемые, позиции. Что может служить критерием оценки оборудования? По нашим представлениям – это, прежде всего, производительность и стандартность, латентность коммутации, совместимость с гетерогенными сетями, возможность работы по принципиально иным протоколам (в случае Ethernet таким протоколом является SDH), перспективная поддержка стандартов будущего (таких как 100Gig Ethernet), насыщенность портами, маршрутная емкость, отказоустойчивость и надежность (время восстановления канала передачи), обеспечение параметров качества обслуживания, стоимость внедрения, стоимость владения и простота обслуживания (т.е. наличие сертификационных центров обучения). Также при выборе оборудования полезной может оказаться и следующая информация: спектр производимого оборудования, распространенность оборудования в мире, стоимость оборудования и стоимость владения. Оценки этих, и ряда других, менее значимых параметров, приведены в таблице. Òàáëèöà 2. Îáîáù¸ííàÿ òàáëèöà õàðàêòåðèñòèê îáîðóäîâàíèÿ MEN
Все условные оценки являются позитивными, т.е. показатель тем лучше, чем выше его оценка. Условные оценки выставляются по пятибалльной шкале, если не оговорено иного. Как можно понять из результатов сравнения, имеется некоторый паритет. Хотя, на наш взгляд, оборудование Extreme (как самое производительное и отказоустойчивое) и Cisco (как самое распространенное и сопровождаемое) имеют некоторое преимущество над конкурентами.
89
образование
ВТОРОЕ НАЧАЛО ТЕРМОДИНАМИКИ – ГАРАНТ УСПЕХА СИСТЕМ С ОТКРЫТЫМ ИСХОДНЫМ КОДОМ Мы вышли на развилку, нам некуда вперёд; Идти назад нам не позволит наша честь. Непонятно, что такие, как мы, До сих пор делаем в таком отсталом месте, как здесь. Борис Гребенщиков
Мы живём в очень интересное время. Информационные технологии настолько прочно вошли в жизнь человечества, что без них уже невозможно представить не только развитие, но и существование нашей цивилизации. Жизненная необходимость разработки и совершенствования ПО уже не вызывает никаких сомнений, и нет оснований полагать, что ситуация изменится в ближайшее время. Поэтому особую остроту моменту придаёт то, что человечество фактически стоит на распутье. Стало очевидно, что есть две принципиально разные доктрины разработки и распространения ПО – с открытым исходным кодом и с закрытым кодом, оба убедительно демонстрируют свою успешность. Единого мнения, какое ПО успешней, нет. Множество людей ведут горячие споры о преимуществах и недостатках того или иного пути. Ситуация стремительно меняется. Человечество пытается принять решение.
АЛЕКСЕЙ МИЧУРИН №7(20), июль 2004
91
образование Почему термодинамика? Обычно при обсуждении вопросов выбора между ПО с закрытым и с открытым кодом в расчёт идут соображения экономического, этического характера, другие доводы. Но в этой статье я хотел бы взглянуть на проблему выбора доктрины разработки ПО с необычной стороны: не с точки зрения человека (производителя, потребителя...), а с точки зрения Природы (или правильнее: с точки зрения современных представлений о Природе). Давайте посмотрим на проблему с точки зрения термодинамики (ТД) – физической теории, описывающей поведение систем, состоящих из большого числа элементов. А именно, второго начала ТД – закона возрастания энтропии. На мой взгляд, термодинамический подход здесь более чем уместен. Взгляните сами: ТД рассматривает системы большого числа частиц. Частицы могут быть нескольких сортов, но каждый сорт представлен большим числом частиц. Очень сходная ситуация наблюдается и в мире ПО. Существует некоторое количество программных продуктов, каждый из которых проинсталлирован множество раз. Невольно напрашивается мысль продолжить аналогии и посмотреть, какие результаты дадут фундаментальные законы ТД, если их применить к миру ПО. Я обращусь ко второму началу ТД – фундаментальному закону, определяющему направления развития (эволюции) подобных систем1. Давайте сперва разберёмся, что такое энтропия, как применить это понятие к нашему вопросу и почему она возрастает.
Энтропия – мера возможностей Все знают магические слова: «энтропия – мера беспорядка», но мало кто задумывается о том, что за беспорядок имеется в виду. Строго говоря (по определению), энтропия данного макросостояния системы – величина, пропорциональная логарифму числа микросостояний, которыми реализуется данное макросостояние. Понятнее не стало? Давайте разбираться. Простой пример: у нас есть два спичечных коробка и четыре спички. С нашей макроскопической точки зрения спички неразличимы, одна ничем не хуже другой. Для нас важно только, сколько в каком коробке находится спичек. Это макроописание системы. Однако, на микроуровне (на уровне спичек) они (спички) различимы и их можно, например, пронумеровать и раскладывать в коробки разными способами, получая одну и ту же макроконфигурацию. Сколькими способами мы можем реализовать макросостояние: в первой коробке – одна спичка, во второй – три (обозначим это состояние 1 + 3)? Или иначе, сколькими способами мы можем разложить четыре неодинаковые спички по двум коробкам, получая заданное макросостояние 1 + 3? Очевидно, таких способов четыре (см. рисунок). А сколькими способами можно реализовать состояние 2 + 2? Шестью (тот же рисунок). Энтропия последнего состояния больше. Энтропия же состояния 0 + 4 равна нулю, так как это состояние реализуется только одним способом (логарифм единицы – ноль).
92
В этом смысле действительно уместна аналогия с порядком и беспорядком. Если все спички лежат в одном месте, это полный порядок и нулевая энтропия. Если всё разбросано хаотично, в беспорядке, то энтропия максимальна. Но это только одна из точек зрения на смысл энтропии. Я хочу подвести читателя к мысли, что энтропия показывает не столько беспорядок в бытовом смысле этого слова2, сколько число возможностей для реализации заданного макроскопического состояния (как это и говорилось в определении). Если рассмотреть больше коробков, то драматизм ситуации ещё более возрастает. Например, для четырёх коробков и восьми спичек конфигурация 1 + 1 + 1 + 5 реализуется 336 способами, а ситуация 2 + 2 + 2 + 2 может реализоваться 2520 способами. Энтропия последнего состояния, как вы понимаете, гораздо больше. Даже минимальное отклонение от этого состояния в состояние типа 2 + 2 + 3 + 1 уменьшит количество возможных реализаций на треть (до значения 1680 шт.). Надеюсь, теперь определение энтропии, данное в начале этого пункта, уже никому не кажется странным или пугающим. Мы уже обжились на микро- и макроуровнях рассмотрения систем и можем смело двигаться дальше.
Второе начало ТД Второе начало ТД, как известно, утверждает, что энтропия изолированной системы не убывает, то есть возрастает или остаётся постоянной. Теперь это утверждение становится более понятным. Если мы предоставим спички из нашего примера самим себе (по условию система должна быть изолированной, т.е. наше вмешательство в систему категорически исключено), то они, скорее всего, придут к состоянию, отвечающему равному заполнению коробков и обладающему большей энтропией. Именно поэтому воздух в вашей комнате заполняет каждый её уголок равномерно: он просто распределился таким образом, чтобы энтропия стала максимальной.
Эволюция Живого Я хотел бы немного отвлечься и взглянуть на эволюцию Живого в Природе (слово «Жизнь» я буду писать с большой буквы, подчёркивая тем самым, что я имею в виду не мою и не вашу жизнь, и даже не жизнь человечества, а Жизнь как минимум на планете Земля). На мой взгляд, здесь это будет вполне уместно по двум причинам. Вопервых, мир программ и вычислительных машин, усложняясь и организуясь, приобретает всё больше черт, род-
образование нящих его с живыми существами. Это видно даже из терминологии, достаточно вспомнить термин «virus» или оператор «die» (язык Perl), или команду «kill» (ОС UNIX). Вовторых, любая эволюция подчиняется одним и тем же глобальным законам, но эволюция Живого гораздо старше эволюции ПО и может преподать много поучительных уроков. Что же демонстрирует эволюция Живого? Она красноречиво свидетельствует, что Живое всё время стремится к разнообразию. ДНК – основной носитель наследственной информации – наращивается3 в ходе эволюции (эквивалентно увеличению числа коробков в наших захватывающих опытах со спичками). Таким образом, увеличивается число возможностей реализовать Жизнь. То есть Природа остаётся верна себе, постоянно увеличивая энтропию. При этом увеличивается не беспорядок в бытовом смысле, а скорее информационная ёмкость. Возвращаясь к нашему определению энтропии: возрастает количество микросостояний, доступных системе. Больше того, Жизнь «изобретает» способы, ускоряющие процесс создания различных модификаций, ярчайший пример – появление двух полов. Благодаря постоянной рекомбинации (перекомбинированию) генетического материала каждый индивид даже одного вида становится уникален. Представьте на минуту горе-человечество, размножающееся почкованием, где каждый человек несёт один и тот же набор генов и обладает одним и тем же набором форм иммунного ответа (защитных реакций). Такое человечество крайне нестабильно. Довольно быстро появится вирус (например), который истребит всех. К счастью, все люди разные, и человечество в целом продолжает здравствовать и по сей день. Подобная ситуация наблюдается и в любой другой эволюционирующей системе большого числа элементов. Эволюция общественного строя, эволюция инструментов труда, эволюция языков (в том числе и языков программирования), эволюция системы частиц газа в вашей комнате – все они ведут к увеличению числа возможностей реализации системы, то есть к увеличению энтропии. (В обществе всё больше различных «вакансий» для человека, инструменты труда предоставляют всё больше возможностей для достижения целей, языки предлагают всё больше вариантов выражения мыслей...) Но мы уже рискуем увлечься, вернёмся теперь в мир ПО.
Акт заимствования фрагмента кода из другой системы в чём-то сродни актам создания новых комбинаций генетического материала и тоже повышает устойчивость всего информационного сообщества (конечно, не обходится и без ошибок, но «мутанты» не живут долго). Представьте себе Интернет, на всех серверах которого стоит один набор программ, скомпилированных одним компилятором с одним набором параметров компиляции, установленных с одного диска одним касанием клавиши Enter... Клоны, клоны, клоны... ни чем не отличающиеся друг от друга. В конце концов, появится вирус, который воспользуется общей уязвимостью и разрушит весь этот мир. Если же ядро ОС каждого сервера и программное обеспечение хотя бы просто перекомпилированы, то атаки, направленные, скажем, на переполнение буферов (наверное, наиболее опасные и эффективные атаки4), затронут только тот soft, который имеет строго определённый двоичный код. Остальной soft будет неуязвим. Это простой пример, только один из многих. Тут читатели меня спросят: «Как же всё произойдёт? Что будет переломным моментом? Когда ждать предсказываемой мною победы OpenSource?» К сожалению (или к счастью?), я не могу ответить на эти вопросы, как не могу ответить, почему именно эта молекула воздуха оказалась именно здесь и когда здесь окажется другая. Возможно, должны измениться экономика и мораль, прежде чем ПО с открытым исходным кодом займёт то место, которое предназначено ему общеэволюционными законами. Возможно, что и сами способы разработки, лицензирования и распространения ПО изменятся до неузнаваемости. Трудно представить, что человечество не найдёт каких-то компромиссов и более совершенных решений. Я не знаю, когда это случится. Как не знаю, почему именно сочетание букв с-т-а-т-ь-я выражает то, что я сейчас пишу. Я только знаю, что языки становятся всё выразительнее и богаче, и в них найдутся слова для моих мыслей; что воздух по-прежнему равномерно заполняет мою комнату и я не задохнусь; Жизнь ищет всё новые пути разнообразить себя; ПО с открытым исходным кодом непременно восторжествует, второе начало ТД останется непоколебимым и Природа остаётся верна своим законам.
Эволюция в мире ПО
Я уже слышу возмущённые голоса читателей: «Уважаемый автор совершенно верно сказал, что второе начало ТД справедливо только для изолированных систем, а сам применяет его к чему попало! Ведь всем ясно, что наша планета не является термодинамически изолированной системой, получая тепло от Солнца. Тем более не являются изолированными подсистемы, упомянутые автором: компьютеры подключены в сеть электропитания, программисты потребляют еду и напитки... Как же можно применять второе начало ТД ко всем этим системам?».
В мире ПО царят те же самые законы, и эволюция неминуемо пойдёт по единственному возможному пути. ПО с закрытым исходным кодом, не допускающее модификацию, обречено, как и гипотетическое почкующееся человечество. Напротив, ПО с открытым кодом неминуемо восторжествует, так как каждая «пересобранная» из исходных кодов система уже является новой модификацией (не клоном, но новым индивидом того же вида), отличающейся от других возможно даже частью функциональных возможностей.
№7(20), июль 2004
Дополнение 1: Несколько слов об изолированности системы
93
образование Читатель абсолютно прав. Упомянутые системы не изолированы в термодинамическом смысле, но это не должно нас смущать, ведь и энтропию мы рассматриваем не в термодинамическом смысле. Мы не рассматриваем атомы и молекулы, как это делает термодинамика, мы выбрали другие микро- и макроуровни рассмотрения. Давайте вернёмся к примеру со спичками. Если мы нагреем нашу систему коробков-спичек, то термодинамическая энтропия, конечно, возрастёт, но спички останутся на своих местах и наша энтропия не изменится. Т.е. система вполне сохранила изолированность в нашем понимании. Точно так же появление новой операционной системы на компьютере практически не связано с количеством потреблённой им электроэнергии и ещё меньше связано с количеством попавших на него солнечных лучей. Термодинамическая изолированность или неизолированность здесь до известной степени ни при чём. Однако все законы продолжают выполняться и в нашем случае. Так, чтобы уменьшить энтропию, необходимо совершить работу. В нашем случае со спичками – продумать, как их разместить, и выполнить необходимые перестановки, переведя систему, скажем, из состояния 2+2 в состояние 0+4. Это сродни той работе, которую вы затрачиваете на уборку, складывая равномерно разбросанные вещи (два карандаша на столе, два – под столом) в одно место (все четыре – на столе, ноль – под столом). Причём я имею в виду не просто механическую работу (тем более не теплоту в термодинамическом смысле), а усилия в самом общем смысле: сюда включены и поиски карандашей, и подбор наиболее подходящего для них места... Опять же, неизолированность вашего стола и карандашей в термодинамическом смысле (их возможность обмениваться теплом с другими телами) нисколько не повлияет на ход вашей уборки, потому что ваши усилия иные, нежели теплообмен с системой. Конечно, последовательный анализ изолированности указанных систем (в указанном, не термодинамическом, смысле) требует отдельной статьи, но я думаю, что приведённых доводов достаточно, чтобы развеять многие опасения и сомнения.
Дополнение 2: Несколько уточнений об эволюции Живого Искушённый читатель, конечно, знает, что клоны тоже не эквивалентны. Позвольте сказать об этом несколько слов, чтобы отвести от себя возможные упрёки читателей в несправедливом проведении аналогий между миром живых существ и миром ПО. А заодно подкрепить выводы статьи дополнительными соображениями. Наивно было бы думать, что в течение жизни организма его генетический материал остаётся неизменным. Под воздействием неблагоприятных условий окружающей среды (различные излучения, химически активные вещества...), по вине случайных флуктуаций (нарушений хода деления клетки) могут произойти мутации как на уровне одного гена, так и на уровне целого генома5.
94
Вы видите, что невозможно в живом мире существование двух одинаковых организмов, даже если один из них является клоном другого. Конечно, генетический материал гораздо более подвержен изменениям, чем, скажем, код программ. И, конечно, нельзя получить копию живого организма так же, как копию файла. То есть наша аналогия между эволюцией живого и эволюцией ПО может показаться не вполне справедливой. Но при внимательном рассмотрении легко заметить, что указанные здесь несоответствия только добавляют весомости сделанным в статье выводам. Действительно, даже несмотря на то, что гены меняются самопроизвольно, а точное копирование живого невозможно, Природа была вынуждена изобрести способы дополнительного ускорения создания новых и новых комбинаций генов. И принудил её к этому всё тот же закон возрастания энтропии. Сможет ли эволюция ПО устоять перед этим фундаментальным эволюционным законом? Сама Природа, выбирая между неизменным и изменчивым, всегда отдаёт предпочтение последнему. 1
2
3
4
5
Первое начало ТД для нас будет малоинформативно. Оно является фактически законом сохранения энергии, справедливым для любых систем, безотносительно числа их элементов. Второе же начало ТД отражает статистические свойства системы. Так и хочется закричать: «Как вообще можно говорить о беспорядке в Природе или в физике! Бога побойтесь!» Биологи, ознакомившись с черновиком этой статьи, очень не одобрили это «дилетантское» слово, прошу меня извинить, если кого-то оно заденет. Детальному рассмотрению этого вопроса посвящены, в частности, статьи Криса Касперского, опубликованные в недавних номерах «Системного администратора». Кстати, с накоплением таких ошибок и сбоев связан и процесс старения. И это ещё одно отличие, программа не стареет (устаревает, но не стареет физически).
подписка на II полугодие 2004 Российская Федерация ! Подписной индекс: 81655 !
!
!
Каталог агентства «Роспечать» Подписной индекс: 87836 Объединенный каталог «Пресса России» Адресный каталог «Подписка за рабочим столом» Адресный каталог «Библиотечный каталог» Альтернативные подписные агентства: Агентство «Интер-Почта» (095) 500-00-60, курьерская доставка по Москве Агентство «Вся Пресса» (095) 787-34-47 Агентство «Курьер-Прессервис» Подписка On-line http://www.arzy.ru http://www.gazety.ru http://www.presscafe.ru
! Узбекистан – по каталогу «Davriy nashrlar» российские
! !
!
!
СНГ В странах СНГ подписка принимается в почтовых отделениях по национальным каталогам или по списку номенклатуры АРЗИ: ! Казахстан – по каталогу «Российская Пресса» через ОАО «Казпочта» и ЗАО «Евразия пресс» ! Беларусь – по каталогу изданий стран СНГ через РГО «Белпочта» (220050, г.Минск, пр-т Ф.Скорины, 10)
!
издания через агентство по распространению печати «Davriy nashrlar» (7000029, Ташкент, пл.Мустакиллик, 5/3, офис 33) Азербайджан – по объединенному каталогу российских изданий через предприятие по распространению печати «Гасид» (370102, г. Баку, ул. Джавадхана, 21) Армения – по списку номенклатуры «АРЗИ» через ГЗАО «Армпечать» (375005, г.Ереван, пл.Сасунци Давида, д.2) и ЗАО «Контакт-Мамул» (375002, г. Ереван, ул.Сарьяна, 22) Грузия – по списку номенклатуры «АРЗИ» через АО «Сакпресса» ( 380019, г.Тбилиси, ул.Хошараульская, 29 ) и АО «Мацне» (380060, г.Тбилиси, пр-т Гамсахурдия, 42) Молдавия – по каталогу через ГП «Пошта Молдавей» (МД-2012, г.Кишинев, бул.Штефан чел Маре, 134) по списку через ГУП «Почта Приднестровья» (МD-3300, г.Тирасполь, ул.Ленина, 17) по прайслисту через ООО Агентство «Editil Periodice» (2012, г.Кишинев, бул. Штефан чел Маре, 134) Подписка для Украины: Киевский главпочтамп Подписное агентство «KSS» Телефон/факс (044)464-0220
Подписные индексы:
81655 по каталогу агентства «Роспечать»
87836 по каталогу агентства «Пресса России»
№7(20), июль 2004
95
СИСТЕМНЫЙ АДМИНИСТРАТОР №7(20), Июль, 2004 год РЕДАКЦИЯ Исполнительный директор Владимир Положевец Ответственный секретарь Наталья Хвостова sekretar@samag.ru Технический редактор Владимир Лукин Редактор Андрей Бешков РЕКЛАМНАЯ СЛУЖБА тел./факс: (095) 928-8253 Константин Меделян reсlama@samag.ru Верстка и оформление imposer@samag.ru maker_up@samag.ru Дизайн обложки Николай Петрочук 103045, г. Москва, Ананьевский переулок, дом 4/2 стр. 1 тел./факс: (095) 928-8253 Е-mail: info@samag.ru Internet: www.samag.ru РУКОВОДИТЕЛЬ ПРОЕКТА Петр Положевец УЧРЕДИТЕЛИ Владимир Положевец Александр Михалев ИЗДАТЕЛЬ ЗАО «Издательский дом «Учительская газета» Отпечатано типографией ГП «Московская Типография №13» Тираж 7000 экз. Журнал зарегистрирован в Министерстве РФ по делам печати, телерадиовещания и средств массовых коммуникаций (свидетельство ПИ № 77-12542 от 24 апреля 2002г.) За содержание статьи ответственность несет автор. За содержание рекламного обьявления ответственность несет рекламодатель. Все права на опубликованные материалы защищены. Редакция оставляет за собой право изменять содержание следующих номеров.
96
ЧИТАЙТЕ В СЛЕДУЮЩЕМ НОМЕРЕ: Запуск в VMWare гостевой оси, установленной на физическом диске В этой статье будет описана возможность запуска гостевой системы, установленной на компьютере при помощи VMWare самым обычным способом. Для более рационального использования времени и ресурсов необходимо одну из этих систем (в данном случае Windows) запускать либо на виртуальной машине, либо на реальном компьютере, чтобы при этом сохранялись все изменения, сделанные в системе во время работы. Это необходимо на случай отсутствия администратора на работе. Про установку и запуск этой программы можно прочитать на http:// www.onix.opennet.ru, где выложены статьи Андрея Бешкова. Но там не описан вопрос запуска уже установленной системы, находящийся на физическом диске, который мы сегодня рассмотрим.
Linux и NTFS До недавнего времени острой необходимости в доступе к разделам с файловой системой NTFS во общем-то и не было. Ругать разработчиков ядра Linux не за что, необходимые работы ведутся уже давно и отнюдь не безуспешно. Уже в 1995 году для ядер серии 2.0 был доступен патч для работы с этой файловой системой, а с версии 2.2 (если быть точнее 2.1.74) подержка NTFS в ядро была включенна стандартно. Но все равно разработки велись несколько вяло. Причин несколько. Семейство пользовательских операционных систем Windows до Me включительно поддерживают только FAT, а NTFS использовалась на ОС корпоративно-серверного уровня, где вряд ли кто-то додумается поставить одновременно две операционные системы на одном компьютере. Появление же Windows 2000 практически ничего не изменило, на домашних компьютерах ее устанавливали довольно неохотно, и если посмотреть на форумах тех времен, то можно
отметить относительное спокойствие. А вот Windows XP действительно смог привлечь пользователя, этот момент и стоит считать началом действительного интереса разработчиков к проблеме. Многие (да почти все) производители включили поддержку NTFS в ядрах своих дистрибутивов. Проблем с доступом к разделам NTFS не обнаружат пользователи Mandrake, SUSE, ALTLinux, ASPLinux, Slackware, Debian и прочих популярных дистрибутивов. Только компания RedHat, очевидно руководствуясь лицензионной чистотой своего дистрибутива, не включила поддержку данной ФС. Поэтому пользователи RedHat и Fedora увидят при попытке монтирования раздела сообщение, выведенное в заголовок. На данный момент имеются два свободных проекта, по-своему решающие вопрос работы с разделами NTFS. Первый – проект Linux-NTFS – предлагает традиционный подход, т.е. написание драйвера, который позволит нормально работать с этой файловой системой. Второй, проект Captive NTFS – пробует решить все эмуляцией системы NT.
В яблочко! Данная статья – краткий обзор ОС Darwin 7.0 на платформе x86 (она же Jaguar, она же Mac OS X 10.3). Не прошло и полгода, как взоры автора вернулись к упомянутому в заголовке продукту. Что же может привлечь помимо легендарного названия фирмы Apple Computer? Наверное, самое главное – ориентирование данной операционной системы на открытость. Правда, можно долго дискутировать относительно открытости продуктов самой фирмы Apple, а нас в первую очередь должно волновать несколько моментов: списки совместимости с оборудованием (так называемые hardware compatibility list [1]); знание структуры *BSD-систем; потребность в использовании данной ОС в обучающем процессе/на производстве.