СЕРГЕЙ СУПРУНОВ
Прокси-сервер oops: первые впечатления
Что такое прокси-сервер, пояснять, думаю, не требуется. Для UNIX-машин стандартом де факто является Squid. Проверенный временем, отлично документированный, мощный, он способен удовлетворить большинство запросов системных администраторов. Однако для моей сети, в которой доступ в Интернет имеют не более десятка пользователей и особого контроля ни за трафиком, ни за правами доступа не требуется, он выглядел несколько избыточно. И вот после нескольких лет безупречной службы Squid был безжалостно деинсталлирован, и его место занял oops.
Установку на свой сервер (FreeBSD 5.3) я выполнял из портов с помощью утилиты portupgrade:
# portupgrade oops
На этапе конфигурирования порта (см. рис. 1) нужно включить поддержку DB4 – она понадобится для работы с кэшем.
Рисунок 1
Нужный порт будет установлен при этом автоматически. Также можно включить поддержку MySQL или PostgreSQL для хранения базы авторизации. На вопрос о том, нужно ли создать пользователя oops, с правами которого будет работать прокси-сервер, был дан положительный ответ. В принципе можно использовать и существующего пользователя (например, nobody или оставшегося от Squid одноименного пользователя), главное – не забыть подправить конфигурацию.
Скудный man oops слегка опечалил. Справедливости ради нужно заметить, что и squid в этом смысле далек от совершенства. Конфигурационный файл, по умолчанию /usr/local/etc/oops/oops.cfg, документирован достаточно неплохо. Может быть, не так пространно и красиво, как Squid, но зато ориентироваться в нем оказалось проще.
Для первого знакомства я решил настроить oops в простейшем варианте – кэширующий прокси-сервер без авторизации с возможностью ограничивать максимальную скорость соединения для клиента в зависимости от его IP-адреса. Переименовав oops.cfg.sample в oops.cfg, начинаем подстраивать его под свои нужды.
Прежде всего нужно задать удобные для вас DNS-серверы в параметрах nameserver. Обычно вполне достаточно двух – основного, с которым будет идти работа, и резервного на всякий случай. Я просто продублировал то, что значится у меня в /etc/resolv.conf.
Далее при желании можете изменить номер порта, на котором oops будет ожидать соединение – параметр http_port. По умолчанию используется 3128. Если у вас запущен Squid, то для тестирования oops можно поставить другой порт и тренироваться на нем.
C помощью параметра bind, находящегося в этой же секции, можно заставить oops ожидать запросы на интерфейсе с указанным IP-адресом, а не на всех имеющихся интерфейсах.
Следующий полезный параметр – userid. Здесь нужно задать имя или UID пользователя, с правами которого будет работать oops. По умолчанию это пользователь oops. Если вы используете другого – не забудьте внести соответствующие изменения до инициализации базы кэша.
Далее следуют параметры logfile, accesslog и pidfile. Установите те пути, которые у вас используются. Для лог-файлов попутно можно задать параметры ротации.
В файл statistics (по умолчанию /var/run/oops/oops_ statfile) раз в минуту сбрасывается текущая статистика вида:
clients : 7
uptime : 181 sec.
http_requests: 287
http_hits : 82
icp_requests : 0
req_rate : 2/s
hits_rate : 28%
free_space : 99%
|
Назначение указанных параметров перечислено в таблице:
Clients
|
Количество обслуживаемых в данный момент клиентов
|
Uptime
|
Время работы сервера с момента последнего перезапуска
|
http_requests
|
Количество HTTP-запросов с момента последнего перезапуска
|
http_hits
|
Количество запрошенных страниц
|
icp_requests
|
Количество ICP-запросов с момента последнего перезапуска
|
req_rate
|
Средняя скорость HTTP-запросов в секунду
|
hits_rate
|
Отношение http_hits к http_requests (в процентах)
|
free_space
|
Свободное место в кэш-хранилище (в процентах)
|
Вернемся к конфигурации. Параметры mem_max и lo_mark позволяют ограничивать количество оперативной памяти, потребляемой прокси-сервером. С помощью следующих далее опций start_red и refuse_at можно управлять количеством соединений, обслуживаемых одновременно. Первая из них указывает число соединений, при котором oops будет случайным образом «прореживать» очередь запросов, сбрасывая некоторые из них (так называемый алгоритм Early Drop). Если количество соединений все же достигнет определенного второй опцией, то все последующие запросы на соединение будут получать отказ.
Остальное можно оставить как есть. Разве что может возникнуть желание поэкспериментировать с опцией force_ completion. По умолчанию она имеет значение 75, что означает продолжение загрузки объекта, загруженного более чем на 75%, даже если соединение с клиентом, запросившим объект, разорвется или клиент откажется от продолжения закачки.
Далее следуют описания различных acl, которые можно рассматривать как переменные, используемые в дальнейшем. Для своей сети я ничего добавлять не стал – практически все, что мне нужно, оказалось удобнее описать непосредственно в секциях групп, о которых и пойдет речь ниже.
Группу «paco», включенную по умолчанию (по крайней мере во FreeBSD), вполне можно рассматривать как пример – она содержит практически все параметры, которые могут быть использованы в настройках групп, и достаточно подробно прокомментирована. По умолчанию она обслуживает подсети 195.114.128/19, 127/8 и 195.5.40.93/32 (см. параметр networks). Если случайно диапазон ваших адресов попадет в описанный для этой группы, то поменяйте этот параметр, чтобы данная группа оставалась исключительно демонстрационной. Хотя никто, конечно, не запрещает вам использовать ее (в конце концов, в файле oops.cfg.sample она останется неизменной) или, наоборот, вообще удалить. Но мне удобнее, когда подсказки всегда под рукой. Еще обратите внимание, что в эту группу попадает localhost. Как правило, обращений к прокси-серверу с локальной машины не происходит, но если вы решите проверить его работу, например, с помощью телнета «telnet localhost 3128», то должны понимать, что обслуживание запроса будет выполняться согласно правилам этой группы (если, конечно, вы не определите для localhost другую группу).
Рассмотрим подробнее пару моих групп:
group ourlan {
networks 192.168.0.0/24;
badports [0:79],110,138,139,513,[6000:6010] ;
bandwidth 16k;
per_ip_bw 4k;
http {
allow dstdomain * ;
}
}
group ouradmin {
networks 192.168.0.25/32;
badports [0:79],110,138,139,513,[6000:6010] ;
http {
allow dstdomain * ;
}
}
В группу ourlan попадут все IP-адреса из указанных в параметре networks, если они не попали в более узкие группы. Принадлежность адреса к группе определяется в порядке уменьшения маски. То есть адрес 192.168.0.25, формально попадающий и в группу ourlan, и в группу ouradmin, будет обслуживаться в соответствии с правилами группы ouradmin, несмотря на то что она описана позже.
Помимо определения допустимых адресов, для всех машин локальной сети, кроме машины администратора, задано ограничение скорости: для всей группы доступная полоса ограничивается значением 16 Кб/с, и дополнительно для каждого клиента доступно не более 4 Кб/с. То есть в данном случае одновременно только 4 клиента смогут работать на максимально доступной им скорости. Если число клиентов возрастет, то доступные для всей группы 16 Кб/с будут делиться между ними поровну. Например, при одновременной работе 8 пользователей из этой группы каждому достанется по 2 Кб/с.
При желании в секции http-группы можно более жестко установить доступ к конкретным доменам – либо перечислить разрешенные в параметре allow, либо разрешить все, но запретить доступ к некоторым с помощью параметра deny. Запрет или разрешение распространяется на сам указанный домен и все его поддомены. Например, параметр «deny narod.ru» запретит доступ ко всем «народным» файлам, а «allow ru» разрешит посещать только сайты в зоне «ru». Также поддерживается одиночный символ «*», означающий любые домены. Если домен попадает как в правило allow, так и в deny, применяется то, которое точнее описывает этот домен.
Например:
http {
deny dstdomain ru ;
allow dstdomain narod.ru ;
}
Эти правила запретят доступ ко всем доменам, кроме «народных».
Если доменов, для которых вводятся те или иные ограничения, достаточно много, то удобно их вынести в отдельный файл (по одному в строке) и подключать его следующим образом:
deny dstdomain include:baddomains.lst ;
Более гибкое регулирование доступа обеспечивает механизм перенаправлений, который будет рассмотрен ниже.
После секций групп следует секция storage, описывающая параметры файла, который будет использоваться для хранения кэша. В отличие от Squid, хранение кэшированных объектов в oops осуществляется в одном файле-хранилище, а не в отдельных файлах для каждого объекта. Это несколько разгружает систему, поскольку заботу о записи в кэш oops берет на себя, и по идее должно способствовать увеличению скорости работы, но на моих минимальных нагрузках это совершенно незаметно. С другой стороны, подобная схема ограничивает максимальный размер файла значением 2 Гб. Если вам требуются большие хранилища, соберите oops с поддержкой GigaBase.
В принципе можно создать несколько хранилищ, используя нужное количество секций storage. Это может быть полезно для распределения базы по разным разделам жесткого диска, если размеры каждого из них не позволяют выделить желаемое место на одном разделе, а также для преодоления вышеупомянутого ограничения на размер файла. Поскольку при этом скорость работы с кэшем должна ненамного, но снижаться, старайтесь по возможности использовать один файл.
Нужно заметить, что файлы-хранилища создаются во время инициализации, которой мы займемся немного позже, когда разберемся с конфигурацией. Процент занятости кэша можно контролировать в приведенном выше файле /var/run/oops/oops_statfile или с помощью управляющей утилиты oopsctl, которую мы скоро рассмотрим.
Далее следуют секции модулей. Модуль lang позволяет задать параметры перекодировки и кодировку по умолчанию. Шаблон HTML-файла сообщения об ошибках можно описать в секции err, если хотите изменить вид, принятый по умолчанию. Файл-шаблон задает вид страницы с сообщением об ошибке, текст самого сообщения в процессе работы подставляется вместо переменных %M (на языке, указанном в параметре lang этой же секции) и %m (на английском языке).
Последующие модули в большинстве своем отвечают за авторизацию. Oops поддерживает хранение базы авторизации в текстовом файле (формировать его следует с помощью утилиты htpasswd, входящей в состав Apache), в базах MySQL или PostgreSQL.
Модуль redir позволяет задавать более гибкие условия фильтрации трафика, чем правила deny и allow в группах. Помимо разрешения и запрета, запрос можно перенаправить на другой ресурс либо выполнить «внутреннюю» подстановку. В данной секции указывается полное имя файла, содержащего правила «редиректа» (параметр file) и шаблон сообщения о запрете доступа (параметр template). Файл правил (по умолчанию /usr/local/etc/oops/redir_rules) содержит правила в следующем формате:
Регулярное_выражение [Действие]
Регулярное выражение сопоставляется с адресом запрошенной страницы, и в случае соответствия по отношению к данному запросу может быть выполнено одно из следующих действий:
Действие
|
Описание
|
Allow
|
Разрешить доступ к запрошенному ресурсу
|
<URL>
|
Перейти на указанный ресурс
|
Internal:<object>
|
Возвратить «внутренний» объект (см. ниже)
|
Если действие не указано, то пользователю возвращается страница «Просмотр страницы запрещен», созданная на основе шаблона, указанного в параметре template.
«Внутренние» объекты компилируются вместе с oops. В версии 1.5.23 файл redir.c содержит следующее определение «внутренних» объектов:
static internal_doc_t redir_internals[] = {
{"nospam1x1", "image/gif", sizeof(nospam1x1gif), 3600, nospam1x1gif},
{"nospam468x60", "image/gif", sizeof(nospam468x60gif), 3600, nospam468x60gif},
{"nospam_close", "text/html", sizeof(nospam_close)-1, 3600, nospam_close},
{"nospam_empty", "text/html", sizeof(nospam_empty)-1, 3600, nospam_empty},
{"nospam_js", "application/x-javascript", sizeof(nospam_js)-1, 3600, nospam_js},
{""}
};
В этом же файле определяются и сами объекты. Например, перенаправление «internal:nospam_close» вернет клиенту такую HTML-страницу вместо запрошенной:
<html></html><SCRIPT LANGUAGE="JavaScript"><!--window.close()//--></SCRIPT>
Регулярные выражения строятся по стандартным для UNIX правилам. Ниже дан пример файла redir_rules, демонстрирующий некоторые особенности:
# Если запрос соответствует нескольким правилам, применяется первое (на microsoft.com мы попасть не сможем)
.*soft.*
.*micro.* allow
# Запрещаем доступ на все домены третьего уровня, имя которых содержит строку amsand, за исключением amsand.narod.ru
http://.*amsand.narod.ru allow
http://.*amsand\..+.ru
# Проверка работы «внутренних» перенаправлений
http://.*/.*test1x1.* internal:nospam1x1
http://.*/.*test468x60.* internal:nospam468x60
http://.*/.*test_close.* internal:nospam_close
http://.*/.*test_empty.* internal:nospam_empty
http://.*/.*test_js.* internal:nospam_js
# Любой запрос, содержащий в имени ресурса упоминание Альтависты, будет перенаправлен на стартовую страницу Яндекса
# (кстати, вы даже не сможете выполнить поиск в том же Яндексе по запросу «altavista.com», поскольку в результате будет
# отправлен запрос: «http://www.yandex.ru/yandsearch?rpt=rad&text=altavista.com», который так же будет соответствовать
# данному правилу)
altavista.com http://yandex.ru
# Перенаправляет на ya.ru все запросы, имеющие цифры в части имени, соответвтующей домену 3-го уровня, например:
# http://www.max2.dom.ru/
# http://www3.w3c.org/index.html
http://.*[[:digit:]]+.*\.[[:alnum:]]+\.[a-z]{2,4}/.* ya.ru
# В ответ на любой запрос, завершающийся символами shtml, будет возвращена пустая страница (содержит только тег <br>)
shtml$ internal:nospam_empty
# Будет запрещен доступ к любому ресурсу, адрес которого заканчивается именем файла page с одним из перечисленных
# расширений
http://.*/page.(html|htm|phtml)$
# Запрещаем доступ ко всем сайтам в зоне com, доменное имя которых имеет длину 7-9 символов (3-5 плюс ".com") и для
# которых не указано имя конкретного файла или папки, например: http://qwer.com/
http://.{3,5}\.com/?$
За пояснениями синтаксиса регулярных выражений можно обратиться, например, к странице справочного руководства man grep(1), к разделу «REGULAR EXPRESSIONS». Приведенные выше правила на практике бесполезны, зато хорошо демонстрируют различные возможности. Ниже приводится более полезный файл:
# Запрос к Яндексу будет перенаправлен на «аскетичный поиск», что можно использовать для снижения трафика, создаваемого
# пользователями
http://(www.)?yandex\.ru/?$ http://ya.ru
# Блокируем все запросы со словом porno, в том числе и формируемые поисковиками
porno
# Вместо любых изображений возвращаем «пустой» рисунок
(gif|jpg|jpe|jpeg)$ internal:nospam1x1
Любые изменения в файлах правил «подхватываются на лету», не требуя перезапуска сервера. Ну и чтобы указанные правила распространялись на конкретную группу, в секции этой группы нужно указать опцию redir_mods со значением «redir». Oops позволяет создавать до 15 дополнительных наборов правил перенаправления, которые описываются в модулях с redir/1 по redir/15 с таким же синтаксисом, что и redir, и могут быть назначены той или иной группе указанием имени модуля в строке redir_mods.
Чтобы oops работал как прозрачный прокси-сервер, дополнительно нужно раскомментировать секцию «module transparent», и в секциях тех групп, которым будет позволено пользоваться его благами, нужно в параметр redir_mods добавить значение «transparent». Ну и не забыть выполнить перенаправление трафика в правилах firewall, например, так:
# ipfw add 1000 fwd localhost,3128 ip from 192.168.0.0/24 to any dst-port 80
Работа в режиме прозрачного прокси-сервера позволяет клиентам вообще ничего не знать о посреднике на пути к запрошенному сайту. Браузер просто формирует стандартный запрос на 80-й порт и получает ответ. Естественно, что в таком режиме речь об авторизации уже не идет – браузер просто не подозревает, что в этом может возникнуть необходимость. Подробнее вопросы авторизации и работы сервера в прозрачном режиме были освещены в статье Дмитрия Репина «Transparent proxy. Быть или не быть?», журнал «Системный администратор», №4, апрель 2004 г.
Настало время выполнить инициализацию кэш-хранилища:
# oops –c /usr/local/etc/oops/oops.cfg –z
После непродолжительной работы появятся файлы, описанные в секциях storage. Осталось запустить oops и убедиться, что все работает как нужно. Для управления лучше всего использовать утилиту oopsctl:
# oopsctl start
Прописываем адрес и порт нашего proxy в настройках браузера (если прокси-сервер непрозрачный) и проверяем работоспособность. Во время работы будут меняться параметры статистики (см. /var/run/oops/oops_statfile). Более полную информацию можно просмотреть, выполнив следующую команду:
# oopsctl stat
## -- General info --
Version : 1.5.23
Uptime : 5646sec, (0day(s), 1hour(s), 34min(s))
Last update : Fri Dec 17 16:03:54 2004
Clients : 1 (max: 8)
HTTP requests: 415
ICP requests: 0
Total hits : 96
Curr.req.rate: 0,00 req/sec (max: 2,68)
Tot.req.rate : 0,07 req/sec
Curr.hit.rate: 0,00 %
Tot.hit.rate : 23,13 %
Curr.icp.rate: 0,00 req/sec (max: 0,00)
## -- CPU --
Total usage : 22607ms
Delta usage : 195ms
Delta time : 61000ms
|
Delta usage показывает использование процессора за последний Delta time. То есть в данном случае за последнюю 61 секунду было использовано 195 мс процессорного времени. Если говорить точнее, то эти цифры показывают, что предыдущий замер делался 61 секунду назад, и с тех пор общее время использования процессора (total usage) выросло на 195 мс. Собственно, на основе этих данных и считается Curr. CPU: 195/61000*100 = 0.32%.
Curr. CPU : 0,32 % (0,03s+0,29u)
Aver. CPU : 0,40 % (0,11s+0,29u)
## -- storages --
Disks msg : Cleanup finished
Storage : /var/oops/storage/oops_storage0
Size : 200,00 MB
Free blks : 51197 blks (199,99Mb) 99,99 %
State : READY
Fileno : 7
Storage : /var/oops/storage/oops_storage1
Size : 200,00 MB
Free blks : 51197 blks (199,99Mb) 99,99 %
State : READY
Fileno : 9
## -- end of storages --
## -- modules --
. . . пропущено . . .
## -- end of modules --
## -- icp peers --
## -- end of icp peers--
|
Остановка сервера выполняется командой:
# oopsctl stop
Чтобы применить изменения, внесенные в конфигурационный файл, используется следующая команда:
# oopsctl reconfigure
Все ключи запуска можно посмотреть в man oopsctl или по команде:
# oopsctl help
Вот вроде бы и все. Еще осталось позволить oops загружаться при старте системы, для чего файл /usr/local/etc/rc.d/oops.sh.sample нужно переименовать в oops.sh.
В заключение отмечу, что формат access-файла oops совместим с форматом Squid, и для его анализа подойдет любой анализатор, разработанный для Squid.
Мои запросы и в этом плане минимальны, поэтому я использую простой сценарий на Python:
#!/usr/local/bin/python
import sys
try:
logfile = sys.argv[1]
except:
logfile = 'access.log'
logs = open(logfile, 'r').readlines()
traffic = {}
counter = {}
for log in logs:
splitted = log.split()
ip = splitted[2]
if traffic.has_key(ip):
traffic[ip] = traffic[ip] + int(splitted[4])
counter[ip] = counter[ip] + 1
else:
traffic[ip] = int(splitted[4])
counter[ip] = 1
keys = traffic.keys()
keys.sort()
for ip in keys:
print '%s | %7d | %15d (%7.2f) |' % (ip.ljust(18), counter[ip], traffic[ip], traffic[ip] / 1048576.0)
Результат выглядит примерно так (для каждого IP возвращается количество сессий и суммарный трафик в байтах и мегабайтах):
# ./getstat
10.0.0.203 | 749 | 4523870 (4.31) |
10.0.0.207 | 1 | 1123 (0.00) |
127.0.0.1 | 1 | 1298 (0.00) |
192.168.0.8 | 234 | 2871338 (2.74) |
66.32.79.217 | 2 | 0 (0.00) |
|
На данный момент я вполне доволен как возможностями oops, так и гибкостью и понятностью его конфигурационных файлов. Как он будет вести себя в дальнейшем, когда потребуется что-то экзотическое – посмотрим.
Дополнительную информацию и сам oops можно получить на сайте проекта: www.oops-cache.org.