System administration
8 December 2011

Отказоусточивый прокси-сервер на базе Squid в домене Windows

UPD: различия в потреблении памяти между моей конфигурацией и той, которая используется у авторов негативных комментариев, вызваны тем, что со Squid'ом помимо стандартного скрипта wbinfo_group.pl, с помощью которого определяется принадлежность доменного пользователя доменной группе, используется скрипт, который определяет сам логин пользователя для того, чтобы squid умел предоставлять пользователю права, отличающиеся от тех прав, которые предоставлены его группе.

imageОднажды дождливым серым вечером у меня появилась потребность внедрить прокси-сервер, но не простой, а такой, который бы обладал следующим функционалом:
  • предоставление/ограничение доступа в зависимости от членства учетной записи в определенной группе Active Directory;
  • предоставление/ограничение доступа в зависимости от прав, предоставленных учетной записи (например, чтобы можно было всей группе разрешить пользоваться поисковиками, а одному самому хитрому пользователю этой группы отключить строго определенный поисковик);
  • предоставление доступа без всяких ограничений для «VIP» MAC-адресов;
  • предоставление доступа к минимальному набору ресурсов всем пользователям (и недоменным в том числе);
  • количество движений, которые должен выполнить пользователь для работы с прокси-сервером, должно быть сведено к минимуму;
  • администрирование прокси-сервера производится посредством веб-интерфейса;
  • совокупная стоимость владения данным прокси-сервером должна быть минимальной.


Естественно, взгляд мой упал на Squid с авторизацией по протоколу ntlmssp. Нашлась утилита конфигурирования Squid'а через веб-интерфейс — SAMS. Но как-то не срослось у меня с ним: то версию php приходилось откатывать с 5.3 на 5.2, то к домену он не цеплялся, то пользователей не видел. Я ни в коем случае не хочу сказать, что это плохой продукт, мой знакомый его разворачивал и настраивал (так что источник достоверный), просто расположение звезд не позволило мне стать адептом SAMS'а.
Изрядно намучившись с гуглом, различными продуктами и решениями, я подумал: «суть конфигурирования состоит в генерации файла squid.conf, на php мне как-то доводилось писать, а почему бы не реализовать веб-интерфейс самому?» Сказано — сделано. В скором времени забегали пакетики, заругались пользователи, понесли мне служебные записки на открытие доступа к ресурсам сети интернет.
Казалось бы, чего еще от жизни надо? Ан нет, по прошествии некоторого времени вылезло на поверхность несколько недочетов в инфраструктуре решения.
  1. При нарушении функционирования контроллера домена, при потере связи с ним, резервный канал связи с внешним миром, коим является интернет (а основной — корпоративная почта), переставал функционировать, ибо пользователи не могли авторизоваться.
  2. Пользователь авторизуется на прокси по протоколу ntlmssp с помощью helper’а (масло масляное) ntlm_auth, один экземпляр которого занимает в оперативной памяти около 60 мегабайт.

    Как видно, в авторизации участвует также и процесс winbindd, отъедающий столько же памяти. Итого, 120 мегабайт на запрос. Экспериментально выяснено, что параметр auth_param ntlm children, указывающий количество потоков для авторизации, должен превышать количество пользователей примерно на 10%. При auth_param ntlm children = 200 на прокси-сервере занято 600 мегабайт памяти.

    Экспериментально выяснено, что если при этом на прокси будут работать 150 пользователей с майл-агентами и прочей шалупонью, то сервер может вести себя непредсказуемо, например, подвисать, терять связь с контроллером домена и т.п. Снова замечу, что, возможно, это моя криворукость, но именно она подтолкнула меня к осмыслению темы данного опуса.
  3. Ресурсы процессора практически не задействуются, при этом происходит резервирование огромного количества оперативной памяти.

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

Снижение нагрузки на прокси-сервер + возможность масштабирования

Отправной точкой на пути к решению является возможность балансировки нагрузки в iptables.
# iptables -t nat -A PREROUTING --dst $GW_IP -p tcp --dport 3128 -m state --state NEW -m statistic --mode nth --every 3 --packet 0 -j DNAT --to-destination $PROXY1
#iptables -t nat -A PREROUTING --dst $GW_IP -p tcp --dport 3128 -m state --state NEW -m statistic --mode nth --every 3 --packet 1 -j DNAT --to-destination $PROXY2
#iptables -t nat -A PREROUTING --dst $GW_IP -p tcp --dport 3128 -m state --state NEW -m statistic --mode nth --every 3 --packet 2 -j DNAT --to-destination $PROXY3

Код данного листинга позволяет равномерно распределить нагрузку между тремя прокси-серверами. Теперь нам нужно три прокси-сервера, которые будут скрываться за одним IP адресом. Мое решение представлено на рисунке ниже.

Обозначения:
  • LAN 192.168.0.0/24 — локальная сеть;
  • PROXY — прокси-сервер, который видит пользователь;
  • LAN 192.168.1.0/24 — локальная сеть для прокси-серверов;
  • PROXY1..PROXYN — виртуальные машины на базе ОС Linux, на которых развернуты Squid, и Samba, интегрированная с Active Directory;
  • PROXY_CONFIG — виртуальная машина, на которой развернут интерфейс управления прокси-серверами.

В данном случае «прокси-сервер», который видит пользователь, на самом деле является гипервизором виртуальных машин, на котором развернуто необходимое количество прокси-серверов и интерфейс управления ими.

При такой структуре можно легко обеспечить:
  • прохождение пакетов от «VIP» MAC-адресов в обход прокси;
  • масштабируемость прокси-серверов;
  • быстрое восстановление после сбоя;
  • поле для экспериментов с конфигурацией Squid'а (для это достаточно развернуть новую виртуальную машину и тестировать все на ней);
  • быстрое выведение из пула прокси-серверов проблемной виртуальной машины;
  • более или менее оптимальное использование аппаратных ресурсов.

Конфигурация Squid'а одинакова для всех прокси-серверов, поэтому файл squid.conf хранится на компьютере PROXY_CONF в nfs-шаре.

Для воплощения данной схемы в жизнь использовались следующие программно-аппаратные компоненты:
  • компьютер CPU Core i3/RAM 4 Gb/HDD 500 Gb;
  • ОС Ubuntu-Server 11.04;
  • VirtualBox + phpvirtualbox.

Для автоматизированного запуска и остановки виртуальных машин при включении и выключении компьютера были написаны скрипты vmstart и vmstop, которые поочередно включают и выключают все виртуальные машины. Приводить их содержание не стану, так как это просто использование команд VBoxHeadless и VBoxManage в совокупности с пингом для проверки статуса машины.
Таким образом, решено несколько задач:
  • снижение нагрузки на прокси-сервер — пакеты с запросом нового соединения разбрасываются на N виртуальных прокси-серверов в порядке очередности, один прокси-сервер с RAM 650 Мб вполне комфортно себя чувствует при auth_param ntlm children = 200, при трех прокси-серверах количество потоков авторизации уже равно 600;
  • масштабируемость — при необходимости разгрузить сервера можно просто скопировать жесткий диск существующего прокси и за 5-10 минут создать новую машину в полной боевой готовности;
  • рациональное использование аппаратных ресурсов — две предыдущие задачи решены за счет повысившейся нагрузки на процессор;
  • отказоустойчивость — достаточно иметь копии машин PROXY_CONFIG и PROXY1 и после падения можно восстановить всю схему, неторопливо попивая кофе, а для полного спокойствия можно периодически бэкапить жесткий диск гипервизора, благо Linux не будет выпадать в синие экраны при попытке вставить жесткий диск в другой системный блок.

Автоматическое переключение между доменной и недоменной конфигурациями

Время показало, что в случае сбоев хорошо бы было, если бы прокси-сервер умел определять, все ли нормально с подключением к контроллеру домена, и в противном случае применял не зависящую от контроллера конфигурацию, а при восстановлении контроллера возвращался к тому функционалу, ради которого все это задумано.
Меньше слов, больше дела!

#!/bin/bash
# Проверяем, локальный или сетевой конфиг сейчас в работе
/bin/ls -la /etc/squid/squid.conf | /bin/grep 'local'
LOCAL=$?
echo Local configuration - $LOCAL
# Проверяем, авторизует ли контроллер домена
/usr/bin/ntlm_auth --username=ad_user --password=ad_pass 2>/dev/null 1>/dev/null
if [ $? != 0 ]; then
       # Если контроллер не авторизует
       echo No connection with domain
       if [ $LOCAL -eq 1 ]; then
		# Если при этом включена доменная конфигурация
		# то меняем ее на локальную
		echo Reconfigure to local
        	/bin/rm /etc/squid/squid.conf
        	/bin/ln -s /etc/squid/squid.conf.local /etc/squid/squid.conf
        	/usr/sbin/squid -k reconfigure
		# уведомляем админа почтой о том, что случилось
                /usr/bin/php5 /home/squiduser/mail/mail.php 'from' 'Proxy XX - Gone to non-domain mode' '=('
       fi
       # Пробуем автризоваться в домене 
       /etc/init.d/joindomain
else
       # Если связь с доменом в порядке
       echo OK connection to domain
       if [ $LOCAL -eq 0 ]; then
	       # Если при этом включена локальная конфигурация
	       # то меняем ее на сетевую
               echo Reconfigure to domain
               /bin/rm /etc/squid/squid.conf
               /bin/ln -s /var/www/squid/squid.conf /etc/squid/squid.conf
               /usr/sbin/squid -k reconfigure
	       # уведомляем админа о том, что все вернулось на круги своя
               /usr/bin/php5 /home/squiduser/mail/mail.php 'from' 'Gone to domain mode' '=)'
       fi
fi

Примечания:
  • файл /etc/squid/squid.conf является символической ссылкой файлы с локальной или доменной конфигурацией (squid.conf и squid.conf.local);
  • для уведомления используется скрипт mail.php, который отправляет сообщение на корпоративную почту;
  • /etc/init.d/joindomain — скрипт ввода компьютера в домен.

Логика скрипта представлена на рисунке ниже.

Чтобы не синхронизировать скрипт в будущем на всех прокси-серверах, закидываем его в сетевую шару на машине PROXY_CONFIG, монтируем ее на всех проксиках и добавляем скрипт в cron на каждую минуту. Таким образом, при пропадании связи с доменом время простоя будет сокращено до одной минуты, то есть злые бухгалтера не успеют позвонить или успеют, но практически сразу возрадуются.
Ну вот, собственно и все. Все работает, проблемы отсутствуют. Хотелось бы сразу ответить на часто задававшиеся мне вопросы по поводу описанной конфигурации.
Вопрос 1
не проще ли развернуть один реальный прокси-сервер на хорошей аппаратной платформе, чем заморачиваться с виртуальными машинами?
Ответ
развернуть — да, проще, управлять — нет. Конечно, если организация небольшая и вы отвечаете и за прокси, и за контроллеры, то городить такой огород нет смысла. В случае же присутствия распределенной сети отделений, филиалов, когда обязанности и ответственность админов разделены, то такая система, «ожидающая» лучшей инфраструктуры — лучшее, что пришло в голову. Если раньше при резком подключении двух новых отделений мне приходилось лезть в конфигурацию и увеличивать количество потоков авторизации, продираясь через жуткие тормоза ОС, развернутой на железе, потом при отказе КД отключать от прокси сетевой кабель, потому что без КД все висит даже локально, тратить несколько десятков минут на ожидание отвисания, то теперь я уже и забыл, как выглядит тот системник, на котором все это развернуто, помню только, что он был черный.
Вопрос 2
зачем надо проверять статус подключения к домену, не проще ли устранить неполадки с контроллером?
Ответ
см. ответ на вопрос 1 в части разделения полномочий.

+30
42.4k 251
Comments 57
Top of the day