Ключевые слова:proxy, cisco, squid, redirect, acl, nat, (найти похожие документы) From : Alex Tutubalin <lexa@home.lexa.ru>
Subj : How To Make a Transparent WWW Proxy (c) http://www.lexa.ru
-------------------------------------------------------------------------------
<b>(c) http://www.lexa.ru</b>
<h4 align=center>How To Make a Transparent WWW Proxy</h4>
<b>Введение в проблему</b><p>
В некоторый момент я обнаружил, что большую часть входящего траффика в
моей сети составляет WWW. При этом клиенты не пользовались Proxy,
хотя это заметно ускорило бы их работу (hit ratio составляет у нас
почти 50%), а пройтись по всем клиентам и поправить настройки их
броузеров я не имел возможности (да и желания тоже). Думаю,
подобные проблемы часто возникают и у ISP, когда поправить настройки
клиентов просто нельзя.
<p>
Почитав Squid'овский
<a href="http://squid.nlanr.net/Squid/FAQ/FAQ.html">FAQ</a>, я
нашел там описание того, как заставить
клиентов насильно использовать Proxy, проблема заключалась только
в том, что предложенный способ не работал.
С другой стороны, я знал, что в
<a href="http://www.radio-msu.net">RadioMSU</a> таки заставили
работать всех своих клиентов через Proxy.<br>
После консультации с Ярославом Тихим все стало на свои места
и окончательное решение было сделано за пару часов.
<p>
<b>Как это у меня работает</b><p>
Действующие лица и исполнители:
<ul>
<li>Роутер Cisco 2511 производства
<a href="http://www.cisco.com">Cisco Systems</a>. К нему
подключены все клиенты (локальная сеть на
Ethernet и клиенты по Dialup), внешний канал на Demos тоже
включен туда же. Для простоты представим, что LAN имеет
адреса/маску 192.168.1/24, а dialup-клиенты - 192.168.2/24.
<li>Машина на которой установлен кэширующий proxy - Pentium-200,
64M RAM, 8Gb дисков. Работает под управлением
<a href="http://www.freebsd.org">FreeBSD 2.2.5</a>. На машине
установлен
<a href="http://coombs.anu.edu.au/ipfilter/">IPFilter 3.2.3</a>
и <a href="http://www.nlanr.net/Squid">Squid 1.NOVM.20</a>.
Для простоты изложения, представим, что адрес этой машины -
192.168.1.1
</ul>
<p>
<b>Настройки</b>
<dl>
<dt>На Cisco</dt>
<dd>Нужно завернуть все транзитные пакетики с destination eq 80
на Proxy. Это делается примерно так:
<pre>
! Prevent loops
access-list 101 deny tcp host 192.168.1.1 any eq 80
! All other clients:
access-list 101 permit tcp 192.168.1.0 0.0.0.255 any eq 80
access-list 101 permit tcp 192.168.2.0 0.0.0.255 any eq 80
! Route-map
route-map forced-proxy permit 10
match ip address 101
set ip next-hop 192.168.1.1
!
! Интерфейсы:
! LAN
interface Ethernet 0
ip policy route-map forced-proxy
!
! Dialup
interface Group-Async 1
group-range 1 16
ip policy route-map forced-proxy
</pre>
</dd>
<dt>На машине с proxy</dt>
<dd>
Первым делом нужно завернуть приходящие на 80-й порт пакеты
на proxy. Предполагая, что proxy работает на порту 3128,
это делается как-то так:
<pre>
ipnat -f - <<EOM
rdr ed0 192.168.1.1/32 port 80 -> 192.168.1.1 port 80
rdr ed0 0.0.0.0/0 port 80 -> 192.168.1.1 port 3128
EOM
</pre>
Первое правило позволяет работать локальному WWW-серверу -
он будет продолжать получать свои пакетики. Вторая строчка
перенаправит все остальные пакеты с destination port 80
на локальный Squid.
<p>
В <b>squid.conf</b> пишутся строчки вида
<pre>
httpd_accel www.your.domain 80
httpd_accel_with_proxy on
httpd_accel_uses_host_header on
</pre>
В <a href="http://squid.nlanr.net/Squid/FAQ/FAQ.html">FAQ</a>
рекомендовано вместо имени сервера в строчке httpd_accel
писать слово <b>virtual</b>. Это - неверно, во всяком случае
для Squid-1.NOVM.18..20. Проблема в том, что в этом режиме
Squid определяет destination server по вызову getsockname(2),
однако этим вызовом он может получить только один из своих
адресов, а это явно не подходит к условиям задачи.
</dl>
Получившаяся конструкция работает по такой вот схеме (я рисовал эту
картинку минут 10 и хотя она тут почти не нужна, не пропадать же
добру):<br>
<!-- img src="proxy.gif" width=640 height=292 align=center alt="Data Flow" -->
<p>
<b>Улучшения</b><p>
При такой настройке, адрес реального сервера к которому
обращается пользователь будет браться из заголовка <b>Host:</b>
HTTP-запроса. Для подавляющего большинства клиентов этого
достаточно - все сколько-нибудь распространенные броузеры этот
заголовок ставят. Однако хочется большего.<p>
В-принципе, можно было бы написать отдельную маленькую программу
(вместо proxy), которая спрашивала бы у <b>IPFilter</b> реальный
destination-address пакета, формировала бы заголовок <b>Host:</b>
и отдавала бы все это кэширующему proxy. Однако такой подход
имеет свои недостатки - в логах Squid будут только локальные
адреса и сопоставить, скажем, Hit Ratio с конкретным клиентом
будет непросто. Я пошел по другому пути и встроил необходимую
функциональность прямо в Squid. Патч для версии
1.NOVM.20 вы можете взять прямо отсюда (
<a href="squid-1.NOVM.20.ipfilter.diff">для версии 1.NOVM.20</a>,
<a href="squid-1.NOVM.22.ipfilter.diff">для версии 1.NOVM.22</a>).
(<i>Необходимые замечания:</i> Патч предполагает,
что *.h-файлы от ipfilter лежат у вас в /sys/netinet, а все
прочие добавленные includes необходимы на FreeBSD 2.2.x, а про
прочие системы я не в курсе).
<p>
После установки этого патча, Squid начнет понимать директиву
httpd_accel_uses_ipfilter_redirect в конфигурационном файле.
Использовать ее нужно так:
<pre>
httpd_accel_uses_ipfilter_redirect on
</pre>
И еще одно замечание - для пользователя с правами которого работает
squid файл /dev/ipnat должен быть доступен на чтение.
<p>
В squid 2.x эта функциональность
присутствует, если запускать <code>configure --enable-ipf-transparent</code>,
при этом никаких дополнительных директив не нужно, это включено по умолчанию.
<p>
После всех этих изменений все работает и без заголовка Host.
Правда требование, чтобы клиент был HTTP-1.x (а не HTTP/0.9)
остается, но уж HTTP/0.9 клиентов я вообще очень давно не встречал.
<p>
<b>Возможные проблемы</b><p>
Возможные проблемы связаны с ICMP-сообщениями 'packet too big'.
Дело тут вот в чем. Допустим, между HTTP-Proxy и клиентом есть
линк с маленьким MTU, при этом и клиент и proxy живут на media
с большим MTU. Представим, что proxy начинает отвечать клиенту
с каким-то размером пакета, который не пролезет через этот тонкий
линк. Ipnat подменит source-address у этого пакета на IP-address
сервера к которому обращался клиент. Если на пути встретится
слишком маленький MTU, то ICMP-сообщение об этом уедет на какой-
нибудь www.microsoft.com, которому он совершенно ни к чему.<p>
Идея лечения понятна - завернуть на той же Cisco все ICMP-пакеты
на хост с WWW-proxy, там их проверять на предмет соответствия
с какой-то строчкой из таблицы трансляции Ipnat, все которые
"неправильные" отдавать прямо местному TCP-стеку, все прочие -
отсылать дальше (можно при этом подменять у них source address
на какой-то левый). Это теория. До практики у меня руки пока не
дошли за полной ненадобностью - у меня все клиенты имеют MTU 1500.