Еще одна статья о кэшировании веб-трафика

Введение, или зачем нужна еще одна статья о WCCP?


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



На первый взгляд, решение обладает сплошными достоинствами: реализация несложна, кэширование выполняется абсолютно прозрачно для пользователей, при отказе прокси-сервера запросы автоматически будут перенаправлены напрямую.

Но всегда ли внедрение WCCP проходит гладко? И если нет, как бороться с возникающими проблемами?

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

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

Если читателям интересно, какие проблемы встречаются при реализации подобных схем, и как можно обойти ограничения — добро пожаловать.

Теория — стандарты и особенности реализации


Для начала немного теории. Протокол WCCP предназначен для перенаправления трафика (не только веб) в реальном времени. Изначально протокол был разработан компанией Cisco, потом стал открытым стандартом, использующимся большинством вендоров.

На сегодняшний день актуальной является версия 2, находящаяся в статусе Internet-Draft и описываемая документом draft-mclaggan-wccp-v2rev1-00.

Остановимся на нескольких важных моментах работы этого протокола (см. рисунок).



Все сообщения WCCP представляют собой пакеты UDP с номером порта назначения 2048. Порядок обмена сообщениями следующий:
  1. Если сервер готов обрабатывать запросы на кэширование трафика, он посылает сообщения WCCP2_HERE_I_AM.
  2. Маршрутизатор отправляет серверу сообщение WCCP2_I_SEE_YOU, содержащее сведения о настройках, в частности, поле “Receive ID”.
  3. Сервер в ответ посылает еще одно сообщение WCCP2_HERE_I_AM, в котором содержится поле “Receive ID” с тем же значением, что и на предыдущем шаге, тем самым подтверждая, что готов работать совместно с маршрутизатором.
  4. Маршрутизатор, получив такое сообщение, понимает, что с этого момента запросы пользователей к веб-сайтам следует перенаправлять на сервер кэширования.


Система готова к работе. Процесс обмена сообщениями WCCP2_HERE_I_AM и WCCP2_I_SEE_YOU повторяется периодически (по умолчанию — раз в 10 секунд), и если маршрутизатор не получает ответа от сервера кэширования, последний исключается из процесса.
В реальности протокол несколько сложнее, он предусматривает аутентификацию, разные алгоритмы перенаправления и т. п., но мы сознательно опустим подробности, неважные для дальнейшего понимания. Интересующиеся читатели могут найти их в соответствующем драфте, ссылка на который приведена выше.

Такая реализация способствует отказоустойчивости решения — если сервер кэширования откажет и перестанет отправлять сообщения WCCP2_HERE_I_AM, маршрутизатор перестанет пытаться перенаправлять пакеты и начнет отправлять их в Интернет напрямую. После восстановления сервиса процесс обмена сообщениями WCCP2_HERE_I_AM/WCCP2_I_SEE_YOU повторится, и схема кэширования снова начнет работать.

Для пользователей такой отказ либо совсем незаметен, либо это может выглядеть как однократное сообщение «Unable to connect», которое пропадет после повторной загрузки страницы в браузере.

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



Посмотрим, что происходит при попытке клиента получить данные от веб-сервера. Для наглядности присвоим хостам конкретные IP-адреса, воспользовавшись специальными диапазонами, выделенными для использования в примерах, а для простоты пока исключим из рассмотрения всю лишнюю функциональность (NAT, межсетевое экранирование и т. п.).


  1. Пользовательский браузер инициирует TCP-сессию, отправляя пакет с SRC IP 198.51.100.150, DST IP 192.0.2.20, DST TCP port 80, флагом TCP SYN.
  2. Маршрутизатор, получив такой пакет, не отправляет его дальше в Интернет, а упаковывает целиком в пакет GRE и отправляет серверу кэширования. Пакет GRE имеет, соответственно, SRC IP 192.51.100.1 и DST IP 198.51.100.100. В Wireshark это выглядит, как показано на следующем рисунке.
  3. Сервер кэширования, получив такой пакет, в первую очередь принимает решение, будет ли он этот пакет обрабатывать. Если нет — пакет отправляется обратно маршрутизатору для нормального форвардинга через тот же самый GRE-туннель, и алгоритм завершается. Если да, то сервер переходит к следующему шагу.
  4. Сервер кэширования от своего имени устанавливает соединение с веб-сервером, для чего отправляет пакет с SRC IP 198.51.100.100, DST IP 192.0.2.20, DST TCP port 80, флагом TCP SYN.
  5. В ответ веб-сервер отправляет пакет с SRC IP 192.0.2.20, SRC TCP port 80, DST IP 198.51.100.100, флагами TCP SYN/ACK, т. е. пока все идет в соответствии с обычным началом TCP-сессии по алгоритму three-way handshake.
  6. Сервер кэширования, получив ответ от веб-сервера, делает два действия:
    • отправляет веб-серверу пакет с SRC IP 198.51.100.100, DST IP 192.0.2.20, DST TCP port 80, флагом ACK, т. е. продолжает нормальную TCP-сессию, которая для веб-сервера выглядит так, как будто к нему обратился обычный клиент с IP–адресом 198.51.100.100.
    • отправляет веб-клиенту пакет с SRC IP 192.0.2.20, SRC TCP port 80, DST IP 198.51.100.150, флагами TCP SYN/ACK, т. е. для клиента ситуация выглядит так, как будто веб-сервер ответил ему напрямую. Запомним этот момент, он является ключевым для дальнейшего понимания.

  7. Итак, у нас имеется две установленные TCP-сессии, одна между клиентом и сервером кэширования, другая — между сервером кэширования и веб-сервером. Сервер кэширования получает контент с веб-сервера обычным способом, транслирует клиенту, попутно сохраняя его в памяти или (и) на диске.
    При последующих обращениях к тому же контенту сервер кэширования, при соблюдении определенных условий, сможет не выкачивать его веб-сервера повторно, а отдавать веб-клиенту самостоятельно.


Описанный алгоритм схематически изображен на рисунке.



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


Такая реализация протокола значительно снижает загрузку маршрутизатора, т. к. ему приходится перенаправлять только трафик от веб-клиента к веб-серверу, объем которого обычно небольшой. Трафик от веб-сервера, объем которого обычно значительный, не подвергается никакой сложной обработке — он просто маршрутизируется.

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

Практика — борьба с маршрутизаторами и межсетевыми экранами


Модифицируем предыдущую схему — поместим сервер кэширования за межсетевой экран:



Будем считать, что мы используем популярное оборудование — маршрутизатор Cisco с ПО Cisco IOS версии 12.3 и выше, межсетевой экран Cisco ASA с ПО версии 8.2 и выше, сервер кэширования на основе Linux (дистрибутив RHEL или CentOS), ПО кэширования Squid.
Как в этом случае все настроить? Предположим, что базовая функциональность уже настроена, т. е. веб-клиент и сервер кэширования в состоянии обращаться к ресурсам в Интернете. Начнем с настройки WCCP на Cisco.

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

ip access-list standard l_wccp_service
 permit 203.0.113.100
ip access-list extended l_wccp_redirect
 permit tcp host 198.51.100.150 any eq www


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

Настроим WCCP и включим его на интерфейсе, смотрящем в сторону внутренних пользователей, т. е. имеющем адрес 198.51.100.1. Для определенности пусть это будет FastEthernet0/0):

ip wccp web-cache redirect-list l_wccp_redirect group-list l_wccp_service
interface FastEthernet0/0
 ip wccp web-cache redirect in


На межсетевом экране разрешим обмен пакетами WCCP и GRE между маршрутизатором и сервером кэширования.

access-list l_wccp extended permit gre host 198.51.100.1 host 203.0.113.100 
access-list l_wccp extended permit udp host 198.51.100.1 host 203.0.113.100
access-group l_wccp in interface outside


Теперь настроим сервер кэширования. Для начала установим и настроим squid, для чего с помощью любимого текстового редактора откроем файл /etc/squid/squid.conf и убедимся, что он содержит следующие строки:

# /etc/squid/squid.conf
http_port 3128 transparent
wccp2_router 198.51.100.1
wccp2_forwarding_method 1
wccp2_return_method 1
wccp2_assignment_method hash
wccp2_service standard 0


Cоздадим туннельный интерфейс, для чего опять же в любимом редакторе создадим файл /etc/sysconfig/network-scripts/ifcfg-tun0 со следующим содержимым:

# /etc/sysconfig/network-scripts/ifcfg-tun0
DEVICE=tun0
BOOTPROTO=none
ONBOOT=yes
TYPE=GRE
PEER_OUTER_IPADDR=198.51.100.1
PEER_INNER_IPADDR=192.168.168.1
MY_INNER_IPADDR=192.168.168.2


IP-адреса PEER_INNER_IPADDR и MY_INNER_IPADDR могут быть абсолютно любыми — через этот туннель нормальным способом ничего маршрутизироваться не будет. Вместо этого весь приходящий в него TCP-трафик с DST port 80 будет заворачиваться на squid с использованием iptables. В предположении, что squid отвечает на порту 3128, поднимем туннельный интерфейс и завернем нужный трафик на squid:

/etc/sysconfig/network-scripts/ifup tun0
iptables -t nat -A PREROUTING -i tun0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 203.0.113.100:3128
/etc/init.d/iptables save 


Проверим, что сервер кэширования зарегистрировался на маршрутизаторе:

cisco# show ip wccp
Global WCCP information:
    Router information:
        Router Identifier:                   198.51.100.1
        Protocol Version:                    2.0

    Service Identifier: web-cache
        Number of Service Group Clients:     1
        Number of Service Group Routers:     1
        Total Packets s/w Redirected:        175623
          Process:                           0
          Fast:                              0
          CEF:                               175623
        Redirect access-list:                l_wccp_redirect
        Total Packets Denied Redirect:       113892411
        Total Packets Unassigned:            20590
        Group access-list:                   l_wccp_service
        Total Messages Denied to Group:      26558
        Total Authentication failures:       0
        Total Bypassed Packets Received:     0


Здесь нас может ожидать неприятная засада: у маршрутизатора обычно бывает несколько интерфейсов с разными IP-адресами. И ничто не мешает ему отправлять пакеты WCCP2_I_SEE_YOU с SRC IP одного интерфейса, а пакеты GRE — с SRC IP другого интерфейса.
В некоторых, но далеко не во всех версиях встроенного программного обеспечения маршрутизаторов Cisco IOS предусмотрена команда “ ip wccp source-interface”, которая позволяет жестко задать интерфейс, IP-адрес которого будет использоваться в качестве SRC IP для всех пакетов, имеющих отношение к подсистеме WCCP.

Если ваш маршрутизатор поддерживает такую команду, вам повезло. Выполните ее:

ip wccp source-interface FastEthernet 0/0


Если же в ответ на такую команду маршрутизатор выдаст что-то вроде “Syntax error”, поступаем следующим образом — запускаем на МЭ диагностику, а на сервере кэширования какой-нибудь сетевой анализатор (хотя бы tcpdump) и выясняем, с каких IP-адресов приходят пакеты WCCP, и с каких — пакеты GRE.

Далее в настройках squid прописываем первый IP-адрес, в настройках туннельного интерфейса и iptables — второй. Модифицируем соответствующим образом списки доступа на МЭ.

Чтобы IP-адрес, с которого приходят пакеты WCCP, не ездил между интерфейсами при последующих перенастройках маршрутизатора, можно завести на последнем loopback-интерфейс. В этом случае протокол WCCP будет использовать для отправки своих пакетов наибольший IP-адрес среди всех loopback-интерфейсов.

interface lo0
 ip address 198.51.100.20 255.255.255.255


Проверим, что перенаправление работает. Для начала убедимся, что счетчики пакетов в списках доступа, созданных ранее, растут:

cisco# show access-list l_wccp_redirect
Extended IP access list l_wccp_redirect
    10 permit tcp host 198.51.100.150 any eq www (2399 matches)


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

%ASA-4-313004: Denied ICMP type=0, from 192.0.2.20 on interface dmz to 198.51.100.150: no matching session


Если мы попробуем погуглить, первая же ссылка скажет нам что-нибудь про ассиметричный роутинг. Разберемся, что это означает.
Межсетевой экран Cisco ASA представляет собой устройство, работающее в режиме Stateful Inspection, т. е. для того чтобы пропустить через себя пакет с флагами TCP SYN/ACK от сервера кэширования к клиенту, необходимо, чтобы до этого соответствующий пакет с флагом TCP SYN от клиента к веб-сайту прошел в прямом направлении через тот же самый МЭ.

В этом случае МЭ поймет, что клиент инициировал TCP-сессию, создаст соответствующие внутренние структуры и начнет правильно отслеживать состояние этой TCP-сессии.

В нашей схеме инициирующий SYN-пакет проходит через МЭ а) внутри GRE-туннеля и б) «как бы не в том направлении».
Соответственно, МЭ не заводит TCP-сессию в своей таблице соединений и не может понять, что сессия началась, и ее пакеты надо пропускать.

Что в такой ситуации делать? Если подключить сервер кэширования в обход МЭ никак невозможно, остается только отключить для пакетов, приходящих со стороны DMZ, проверку на наличие открытой TCP-сессии.

В Cisco ASA функция отключения проверки называется TCP bypass. Функция имеет ограничения:

  1. Не работает на Cisco ASA с ПО версии младше 8.2.
  2. Неизвестен (по крайней мере, нам не удалось найти) способ организации на одном и том же МЭ модели Cisco ASA одновременно и клиентской зоны, и DMZ — предсказуемо не работают трансляции IP-адресов.


Итак, включаем функцию TCP bypass:

access-list l_bypass extended permit tcp any eq www host 198.51.100.150
class-map c_bypass
 match access-list l_bypass
policy-map p_bypass
 class c_bypass
  set connection advanced-options tcp-state-bypass
service-policy p_bypass interface dmz


В список доступа l_bypass надо поместить диапазон клиентских IP-адресов.

Вот теперь все должно работать. По крайней мере, у нас заработало.

Заключение


Статья написана на основе опыта внедрения функции кэширования веб-трафика в сети небольшого оператора связи, и в очередной раз иллюстрирует два старых принципа в работе инженера-сетевика:
  • не пренебрегайте стандартами и описаниями работы протокола;
  • не понимаете, что происходит — не поленитесь, подключите сетевой анализатор.


Удачного тестирования и внедрения! И пусть отныне и всегда ваши каналы транспортируют как можно меньше лишнего трафика.
Поделиться публикацией

Комментарии 15

  • НЛО прилетело и опубликовало эту надпись здесь
      0
      Используется, особенно в небольших.
      Блокировка — отдельная интересная тема. Действительно, в связи с последними инициативами наших законодателей провайдерам приходится что-то предпринимать. Те, у кого бюджет позволяет — ставят «промышленные» решения DPI. Те, что поменьше, часто поступают так — берут тот самый список Роскомнадзора, трафик на запрещенные IP заворачивают в сторону (как правило, не с помощью access-lists, а с помощью средств динамической маршрутизации), потом по WCCP перенаправляют на внешний кэш, и там уже разбирают по URL.
        0
        Блокировку, по крайней мере в России, проще делать модулем iptables. Проскакивал где-то специальный модуль, который даже списки сам обновляет.
          0
          А iptables умеет искать строку не в одном отдельно взятом пакете, а в сессии? В принципе, он вроде бы имеет некоторые stateful-фичи, так что может и уметь. Исследую вопрос при случае, спасибо за наводку.
            0
            Повторюсь — там специальный модуль, именно для России. Разработчик — какая-то русская компания.
            К сожалению, найти сейчас не смог — щелкнул когда-то баннер на nag-е, прочитал из интереса, но я не в России :).
              0
              ответил в ЛС
                0
                Вот случайно увидел на nag-е баннер, открыл — может будет интересно:
                www.carbonsoft.ru/products/reductor/carbon-reductor-4.0.2/?utm_source=naga&utm_medium=banner&utm_campaign=reductor
            0
            Используется разве что в некоторых регионах, где это выгодно.
            Обычно дешевле докупить канал, чем кешировать. Оно даёт максимум 1-2 процента экономии.
            0
            И какова эффективность всего этого дела? Хоть на 3% удалось снизить веб-трафик во внешнем канале? Затраты окупаются?
              0
              Затрат здесь немного — сервер и труды. Оборудование обычно уже есть. Проект у нас пока в стадии опытной эксплуатации, число процентов назвать не готов. Но уже видно, какой трафик хорошо кэшируется — это прежде всего Windows Update.
                0
                Если это организация, где есть AD и возможность выделить Win сервер под WSUS, то лучше так и сделать.
                  0
                  Это — провайдер не в России (в СНГ), с нешироким каналом за границу. Типичный его абонент — индивидуальный пользователь или небольшой офис с парой-тройкой компьютеров под Windows. О WSUS, соответственно, у абонентов понятия никакого нет.
                  Мы пробовали подсунуть WSUS Windows-машинам прозрачным образом (вместо штатного сервера Windows Update), но наши эксперименты успехом не завершились, к сожалению. А решение с прокси вроде бы работает.
                    0
                    Если это провайдер, то тогда только прозрачно кэшировать. WSUS без AD не будет работать.
                  0
                  Просто ~10 лет назад я «провайдил» интернет для своей студенческой общаги, для чего поднял роутер на линукс-боксе со сквидом. При том что в общаге была очень ровная социальная группа (умные молодые люди 17-23 лет), было тесное социальное общение, работал мессажник (vypress messanger), по которому разлетались по внутренней сети бродкастом ссылки на лулзы, был посещаемый внешний портал с курсачами и т.п., а оплата внешнего канала шла по трафику — по факту сквид давал всего порядка 2% экономии.
                  Если прикинуть, что сервер с дисками для сквида стоит $2k при эффективности сквида 2%, ежегодные расходы на внешний трафик должны обходиться минимум в $100k. Это, наверное, работает для Тикси и Нерюнгри, где интернет только спутниковый, но в нормальных сетях — вряд ли. Не представляю себе таких сумм за внешний интернет-канал в современном мире.
                  И торрентов с ютубами, кстати, тогда не было.
                    0
                    Странно это. В 2000-2004 годах крутил squid. Сначала в провайдерской сети, а затем в сети офиса. В провайдерской сети был выигрыш в районе 30 процентов. В пользовательской в районе 40 процентов. Правда я и в том и в другом случае этот squid реально крутил. Смотрел логи, генерировал отчеты и настраивал. На мой взгляд прокси тогда был очень оправдан, сейчас, если оплата за канал (а не за трафик) не стоит и заморачиваться.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое