haproxy redirect to url
Redirecting URL paths with HAProxy and retaining Query Strings
We have a URL map file loaded into HAProxy using the following:
An example of a line in the map file would be:
This redirect works as expected. The only issue I’m experiencing is when a query string is passed with the /shop-by-category path. E.g:
This will not redirect.
I’ve tried adding what I believe to be the HAProxy query variable to the redirect map like:
But HAProxy does not appear to recognise this in the map file. Is there any way I can have HAProxy dynamically recognise the query string and also pass it through when it redirects?
1 Answer 1
capture.req.uri refers to URI in the RFC sense:
This extracts the request’s URI, which starts at the first slash and ends before the first space in the request (without the host part). Unlike » path » and » url «, it can be used in both request and response because it’s allocated.
. so it sounds like you should be using the path fetch for matching and the query fetch in the rewrite, if it is set.
This extracts the request’s URL path, which starts at the first slash and ends before the question mark (without the host part).
This extracts the request’s query string, which starts after the first question mark. If no question mark is present, this fetch returns nothing. If a question mark is present but nothing follows, it returns an empty string. This means it’s possible to easily know whether a query string is present using the «found» matching method. This fetch is the complement of «path» which stops before the question mark.
Two lines for match and rewrite are then required.
First, a rewrite if the query string is present:
Then, a rewrite if the query string is absent:
Как перевести сайт целиком на постоянный HTTPS для всех
Шифруем всё подряд
Эра незашифрованного веба проходит, и это хорошо. В этой инструкции мы предполагаем, что на вашем сервере работает веб-сервер Nginx. И теперь мы сделаем так, чтобы все посетители сайта пользовались исключительно протоколом HTTPS. Кроме этого мы включим HSTS – это «HTTP Strict Transport Security», когда сайт не только поддерживает HTTPS, но и настаивает на его использовании.
Для этого есть множество способов, но я опишу метод под названием «HTTPS termination». Иначе говоря, мы поставим перед веб-сервером обратный прокси, который и будет обеспечивать HTTPS. Это получается проще и гибче, чем настраивать HTTPS только при помощи возможностей веб-сервера. Возможно, вам покажется контринтуитивным, что добавление ещё одного приложения в стек упростит вашу жизнь – но это действительно так.
Уточним, что данный рецепт подходит для серверов на базе Linux, на которых установлен Nginx.
То, что будет работать прежде всех остальных приложений в стопке – это HAProxy. Это в первую очередь приложение для балансировки – он умеет распределять приходящие запросы между разными физическими серверами. Много высоконагруженных сайтов используют его в этом качестве (тот же reddit), но в последней версии у него появилась возможность выполнять SSL termination. Он умеет устанавливать HTTPS-соединения от имени сервера.
Поэтому мы поставим HAProxy, скормим ему наши сертификаты SSL/TLS, поручим перенапрявлять все HTTP запросы на HTTPS, и покажем ему уже сам веб-сервер в качестве бэкенда.
Установка HAProxy
Порты 80 и 443 будут смотреть в интернет и получать HTTP и HTTPS трафик. Все HTTP запросы получат редирект 301 на тот же URL, но по HTTPS, и затем перенаправятся на бэкендовый веб-сервер (nginx) по чистому HTTP.
HAProxy package, включённый в поставку Ubuntu 14.04 LTS довольно старый, поэтому добавим репозиторий:
Затем обновим исходники и установим приложение:
Основные настройки лежат в /etc/haproxy/haproxy.cfg. Вот мои настройки:
Кстати, если вам понадобится подсветка синтаксиса конфига HAProxy для vim, её можно взять здесь. Разберём настройки подробнее.
Global
Оставляем первый кусок нетронутым. Это настройки логов, директория и права доступа.
Следующие части нужно поправить – там указано, где находится CA root и лежат сертификаты SSL/TLS. Возможно, нужно будет поменять ca-base и crt-base.
Строка ssl-default-bind-ciphers определяет, какие коды SSL/TLS будут применяться HAProxy при соединении с клиентом. Я использую рекомендованный список от Qualys/SSL Labs. Я также отредактировал строчку ssl-default-bind-options и запретил SSLv3 и TLS1.0, т.к. они дырявые. Последняя строка, tune.ssl.default-dh-param, сообщает программе о необходимости использовать не более 4096 бит в параметре Diffie-Hellman при обмене ключами DHE.
Defaults
Добавляем пару вещей — forwardfor и http-server-close. Поскольку мы используем приложение в качестве прокси, оно должно сообщать серверу IP-адреса, с которых идут запросы. Иначе это будет выглядеть, будто весь трафик идёт с HAProxy. Поэтому forwardfor сообщает, что программа работает как обратный прокси, и необходимо добавлять заголовок X-Forwarded For для сервера.
Настройка http-server-close нужна для быстродействия — HAProxy будет решать, закрывать ли соединение или использовать его повторно, при этом поддерживая более продвинутые вещи вроде WebSockets.
Certificates
Первая часть секции сообщает, какой трафик HAproxy должна обрабатывать и куда его отправлять.
Мы привязываем HAproxy к портам 80 и 443, она слушает HTTP на порту 80 и HTTPS на порту 443. Для HTTP мы скармливаем ей два разных сертификата. HAproxy использует Server Name Identification (SNI) чтобы привести хост входящего запроса в соответствие с нужным SSL/TLS сертификатом. У меня на сервере есть три сайта, они используют разные групповые сертификаты (*.bigdinosaur.org, *.chroniclesofgeorge.com и *.bigsaur.us) и HAProxy правильно выбирает нужный из них.
Убедитесь, что владельцем файла будет root:root, и права у него должны быть только на чтение:
От HTTP к HTTPS, и HSTS в качестве бонуса
Теперь определим ACL, список контроля доступа. В HAProxy это список вещей, удовлетворяющих определённому критерию:
Мы создали ACL по имени secure, который совпадает со всем, что идёт на TCP порт 443. Он нам скоро понадобится.
Следующая строка – та самая, где происходит перенаправление трафика с HTTP на HTTPS.
Это значит, что если входящий запрос не HTTPS, то надо отправить перенаправление 301 к тому же ресурсу, но по схеме HTTPS.
Это именно то HTTP Strict Transport Security – всем браузерам предписывается использование HTTPS:
Настройка добавляет нужную строку в заголовки. Браузер, распознающий этот заголовок, понимает, что сайт предпочитает работать по HTTPS, и это указание действительно в течение года (31,536,000 seconds). Директива preload сообщает Google-боту, что ваш сайт можно добавить в их список сайтов, поддерживающих HSTS.
HSTS – штука правильная. Шифрование должно быть всегда, и администраторам надо стремиться распространять его везде.
Кстати – необходимо учесть, чтобы все куки также включали атрибут secure, поскольку с точки зрения вашего веб-сервера всё происходит по обычному протоколу HTTP. Для этого используем директиву rsprep:
1
Обратите внимание на «if secure». Это значит, что меняются только куки, идущие по HTTPS (secure – это переменная, которую мы несколько линий назад определили). Вообще, всё должно работать через HTTPS, поэтому это в принципе необязательно – но можно и подстраховаться.
Последняя строка определяет, куда отправлять трафик. Это default_backend. Тут можно определять несколько серверов для распределения нагрузки и т.п. Но, поскольку у нас есть один бэкенд-сервер, то всё довольно просто.
back end
Поскольку у нас один бэкенд-сервер, эта секция коротка. HAProxy необходимо лишь добавить пару заголовков, чтобы сервер понял, что реальное общение происходит через HTTPS, даже если он видит только лишь HTTP:
Первая директива устанавливает заголовок, объясняющий, что клиент изначально пришёл на порт 443. Вторая усиливает первую, устанавливая X-Forwarded-Proto в HTTPS, если запрос шёл через HTTPS, чтобы веб-сервер понимал, что происходит и не создавал неправильных ответов.
Затем мы объясняем HAProxy, куда слать запросы – имя, IP и порт веб-сервера. У меня веб-сервер работал на отдельной машине, поэтому я пишу сюда другой IP и 80-й порт. Если у вас всё работает на одном компьютере, то пишите localhost и порт.
Статистика, если нужно
Последняя секция диктует HAProxy выдавать статусную страницу по заданному порту. Тут можно добавить basic auth, добавив stats auth username:password, или определить отличающийся URL.
Если вы используете директиву HSTS «includesubdomains», у вас может не получиться запросить статусную страницу по имени, поскольку веб-браузер попытается загрузить её HTTPS-версию, а HAProxy отдаёт только HTTP-версию. Это можно обойти, запрашивая страницу по IP вместе с портом (http://192.168.x.x:9999).
Подчищаем
Сохраните настройки, но пока не перезапускайте сервис HAProxy. Если у вас всё работает на одной машине, и nginx также слушает события на портах 80 и 443, нужно кое-что подправить.
Поскольку nginx больше не будет отдавать HTTPS-запросы, нужно убрать все упоминания HTTPS из всех файлов виртуальных хостов.
Вот и всё. Нужно только добавить в главный файл nginx.conf две строчки, чтобы убедиться, что nginx будет заменять ip-адреса согласно заголовку X-Forwarded-For, если запросы приходят от 127.0.0.1:
Это будет работать, если вы скомпилили nginx с опцией ngx_http_realip_module.
Перезапустите HAProxy (service haproxy restart) и она начнёт слушать запросы.
Проверка работы
Сделайте запрос к сайту через http. Если URL меняется на https и вы видите сайт – всё работает. Можно убедиться, что заголовок HSTS отправляется корректно:
IIS как пограничный веб-сервер (теперь haproxy)
В этой статье я хочу описать не самый типичный сценарий, который, тем не менее, имеет право на жизнь.
Дело в том, что мы используем IIS как прокси для других веб-серверов компании. Я расскажу, как это реализовано и с какими трудностями пришлось столкнуться.
Постановка задачи
Разберём на примере сервера YouTrack. Он представлен неприглядным srv-youtrack-01.local.domain и находится на веб-сервере внутри компании. Задача заключается в том, чтобы обеспечить доступ к нему из интернета по красивому имени yt.company.ru. При этом обязательно должен использоваться https.
Реализация
Для начала работы нам понадобится установить компонент URL Rewrite. Это можно сделать при помощи web platform installer, а также вручную. После его установки мы увидим в диспетчере служб IIS новый ярлык
переопределение URL-адресов».
При помощи этого инструмента можно создать правило переопределения адресов «обратный прокси-сервер».
При создании правила нужно указать URL сервера (без префикса http:// — его IIS добавит автоматически), на который будет происходить проксирование. В итоге мы получим правило, доступное для редактирования. Оно применяется не ко всем запросам, а только к тем, которые подходят под критерии, которые мы можем настраивать. Для начала проверяется URL на соответствие шаблону, после чего в ход идут проверки по другим критериям.
Сразу оговорюсь, что тут есть два пути: первый путь — создать набор правил с разными шаблонами URL-адресов для разных ресурсов на одном IIS-сайте; а второй — создать по сайту для каждого проксируемого ресурса и в каждом из них сделать по одному правилу. Понимая, что первый путь более джедайский, я все-таки избрал путь второй — пусть не такой красивый, зато я не рискую написав неправильное регулярное выражение для одного сайта сломать всю маршрутизацию. Поэтому шаблон URL-адреса у меня везде дефолтный «(.*)».
Итак, я создаю сайт yt.company.ru с биндингами на 80 и 443 порт и обязательным указанием имени узла, чтобы IIS знал, к какому сайту я обращаюсь. Про получение и установку сертификата для 443 позволю себе не упоминать. Обращу лишь внимание, что сам сервис настраивать на использование https не нужно — внутри сети шифроваться не от кого, а внешние запросы будут соединяться по ssl с пограничным сервером, который будет уже по незащищенному каналу проксировать запросы внутри сети.
Покуда обязательным требованием является использование https, будем проксировать только те запросы, которые приходят на 443 порт, для чего создадим простое условие. При его создании предлагается выпадающий список возможных вариантов.
Отлично, теперь все запросы yt.company.ru проксируются на внутренний сервер с неприглядным именем srv-youtrack-01.local.domain прозрачно для пользователя.
Однако, все запросы yt.company.ru отсекаются с ошибкой 403, что не очень красиво. Для решения этой проблемы можно либо создать index.html с редиректом, либо еще одно правило URL Rewrite, у которого в поле «действие» выберем постоянное перенаправление на нужный нам URL.
Следует обратить внимание, что правила для сайта применяются по порядку, поэтому сначала нужно расположить правило с условием, а потом правило без условий. При этом, так как второе правило применяется ко всем URL без исключения, для первого правила необходимо поставить (оставить поставленной) галочку «Остановить обработку последующих правил».
После манипуляций с графическим интерфейсом в корне нашего сайта создается web.config, который содержит все созданные правила. Поэтому если требуется проксировать еще какой-то сайт, то эти манипуляции повторять не нужно, можно просто скопировать web.config и изменить в нем требуемый URL или после копирования воспользоваться графическим интерфейсом для изменения правил. Более того, можно вообще не пользоваться интерфейсом, а писать сразу туда — кому как нравится.
Подводные камни
При переходе на вкладку «Agile boards» YouTrack генерит URL-адрес вида yt.company.ru/rest/agile/Overview-0/sprint/Iteration+24. Далее, при переключении между спринтами yt.company.ru/rest/agile/Overview-0/sprint/Iteration%252023?q=. При переходе на эти урлы IIS стал возвращать мне 404 ошибку. Это свидетельствовало о том, что запросы не проксируются. При этом переходы между сохраненными запросами вида yt.company.ru/issues/IT?q=%23
Эксперименты с добавлением знака вопроса в середину проблемного URL закончились тем, что я стал получать 404 ошибку уже от YouTrack-сервера, а не IIS. Это натолкнуло меня на мысль, что IIS по каким-то своим соображениям (привет, Microsoft) интерпретирует URL и это надо исправить.
Проблема со знаком «плюс» в середине адреса решилась добавлением параметра requestFiltering allowDoubleEscaping=«true»:
Но после этого все еще не работало переключение между спринтами. Выяснилось, что IIS считает такие запросы небезопасными. Эту проверку тоже пришлось отключить:
Вот какой web.config получился после всех манипуляций:
Итоги
Вероятно, решения, которые я нашел, не являются оптимальными и вместо разрешения всего подряд нужно было аккуратно прописать правила, подходящие в конкретном случае. Но сейчас это работает. Буду рад выслушать ваши соображения и предложения.
Таким образом у нас в организации проксируются абсолютно все веб-серверы, которым нужен доступ извне, среди которых есть и nginx, и apache, и svn, и gitlab, и exchange web access.
Основная проблема, которая заставляет меня находиться в поиске решения заключается в том, что через прокси не работает NTLM-авторизация, так нужная для многих сервисов компании Microsoft. Мёртвый продукт TMG использовать не хочется, поэтому сейчас пытаюсь разобраться с новой службой Windows Server 2012 R2 под названием Web Application Proxy параллельно поглядывая на nginx и apache, которые, кажется, тоже пока не умеют проксировать NTLM.
Ссылки
Большой update:
В комментариях мне посоветовали попробовать haproxy. Зайдя на сайт я сделал поиск по странице «ntlm» и нашел «full HTTP keep-alive for better support of NTLM and improved efficiency in static farms», что придало мне уверенности.
После нескольких дней активной возни в консоли я таки осилил этот замечательный инструмент и теперь IIS в качестве прокси-сервера мне больше не нужен. Отдельную статью про это, думаю, писать не стоит, поэтому решил обновить топик.
Работает всё это дело достаточно просто и из коробки:
1. Устанавливается из backports при помощи apt-get (предпочитаю debian)
2. Пишется конфиг. Слегка правятся настройки проксируемых приложений
3. Переключается iptables на новый прокси
На втором пункте остановлюсь подробнее.
В секцию defaults конфига дописал
Последний пункт нужен для того, чтобы бакенду передавалось имя хоста, остальные — «как у всех».
Далее создаются фронтенды для 80 и 443 портов, которые будут слушать и решать, на какой бакенд посылать запрос в зависимости от некоторых условий. А условие у меня только одно — пришедшее имя хоста.
С https несколько сложнее. На помощь пришёл соседний топик, в комментариях которого рекомендовали использовать SNI. Его и использовал
Это оказалось очень просто! Первым делом генерируются сертификаты для всех бакендов — именно они и будут отдаваться клиентам. Я использую PKI от Микрософта, поэтому пришлось немного повозиться с генерацией реквестов, выдачей и передачей на прокси этих сертификатов. Допускается, кстати, использование *.company.name, но я решил что это как-то не очень солидно, тем более при таком малом количестве бакендов. После того, как сертификаты готовы, нужно написать их тупо в строчку как на примере выше, а дальше написать правила для бакендов — сертификаты будут подсовываться по порядку.
Конструкция с использованием sni настолько простая, что даже объяснять не надо. Правда, вышло так, что большинство почтовых клиентов андроида не умеют (или не хотят) sni, и шлют запросы на 443 порт без указания имени хоста. Не беда! Для таких случаев есть
(я, кстати, не проверил, какой сертификат подсовывается в этом случае)
Ну а дальше описываются бакенды. С http всё тривиально:
Здесь it.company.name — это имя хоста, которое будет передано на srv-web-01. Мне это нужно по тому, что IIS на этом сервере использует идентификацию по имени хоста.
Для https вот такие конструкции
Тут можно разгрузить SSL, указав 80 порт — тогда шифроваться будет трафик между клиентом и проксей, а внутри сети не будет. А можно и дальше использовать https (verify none означает «не придираться к сертификату»). Стоит, однако, понимать, что клиент все-таки получает тот сертификат, который мы вписали при создании фронтенда. Если нужно подсовывать именно сертификат конечного сервера, то можнжо использовать способ, описанный в топике, который я указал выше.
Ещё один момент: хочется красиво редиректить http на https для некоторых серверов. Для этого я создал специальные бакенды с постфиксом _r, которые аккуратно перекидывают ничего не подозревающего пользователя на https.
Закомментированную строку не стал убирать сознательно — изначально использовался такой вариант, но он перенаправлял меня в корень сайта, что очень неудобно, если пользователь нажал на длинную ссылку http ://site.company.name/lib/doc/Русские%20буквы%20в%20названии.docx, а его перекинуло на главную страницу без всякой надежды найти свой документ. Велика вероятность, что он закроет её и попробует пройти по ссылке снова, но опять ничего не получит и очень расстроится. Чтобы такого не произошло, нам помогает конструкция redirect scheme https, которая аккуратно перенаправляет пользователя, подставляя весь URL.
Подробно обо всех тонкостях конфигурирования на странице документации cbonte.github.io/haproxy-dconv/configuration-1.5.html#4.2
Спасибо за внимание. Надеюсь, кому-то мой опыт окажется полезным.
Балансировщик HAProxy. Часть 1: базовые понятия и уровни
Это первая статья из цикла про HAProxy. Здесь я затрону базовые понятия и уровни распределения запросов.
Итак, если увеличивается нагрузка, пути строго два: увеличивать производительность сервера, либо добавлять новые сервера и объединять их в кластер. Оба способа помогут справиться с нагрузкой, но вот с отказоустойчивостью у первого не очень, так как мало пользы от мощного сервера, если он лежит. С кластером в этом плане жить комфортно: упала одна нода — подхватили остальные, да и предела для масштабирования нет.
С объединением нескольких серверов в кластер мы и подходим к необходимости распределять нагрузку между ними. Эту задачу решает балансировщик. В D2C мы используем HAProxy, поэтому расскажу варианты его конфигурации в зависимости от архитектуры, которую надо получить на выходе. Все с примерами.
Artem Zaytsev
Что такое HAProxy и почему он подойдет
HAProxy — это балансировщик нагрузки, который умеет распределять и проксировать запросы по TCP и HTTP протоколу. Преимуществ у него немало:
Еще HAProxy использует Airbnb, Alibaba, Github, Instagram, Vimeo и еще не менее сорока известных высоконагруженных проектов. Так что причины его использовать весьма основательны.
Базовые понятия конфигурации
Настройка HAProxy обычно сосредоточена вокруг пяти разделов: global, defaults, frontend, backend, реже listen.
Раздел global определяет общую конфигурацию для всего HAProxy
Defaults определяет настройки по-умолчанию для остальных разделов проксирования.
Раздел listen объединяет в себе описание для фронтенда и бэкенда и содержит полный список прокси. Он полезен для TCP трафика.
Раздел Frontend определяет, каким образом перенаправлять запросы к бэкенду в зависимости от того, что за запрос поступил от клиента.
Секция Backend содержит список серверов и отвечает за балансировку нагрузки между ними в зависимости от выбранного алгоритма
Разделы global, defaults и listen я затрагивать в статье почти не буду, поэтому просто дам стандартную конфигурацию из панели D2C, которую достаточно взять и применить:
А вот на разделах Frontend и Backend я остановлюсь подробнее. Они помогут нам настроить балансировщик исходя из наших задач.
Frontend и типы распределения нагрузки
Фронтенд обычно отвечает за тип распределения нагрузки и выбор бэкенда исходя из правил списков контроля доступа (ACL).
Транспортный уровень или Layer 4
Пример конфигурации фронтенда для работы на транспортном уровне:
На Бэкенде так же придется прописать mode tcp. В случае с транспортным уровнем конфигурация бэкенда может выглядеть так:
В HAProxy в одном конфигурационном файле допустимо использовать несколько типов распределения нагрузки. Например, можно для 80 порта задать режим http и распределять нагрузку на 7 уровне, а для 443 задать TCP и перенаправлять запросы на 4 уровне.
Такая функциональность будет полезна при работе с SSL: можно переадресовать запросы с 80 порта на 443:
потом на транспортном уровне передать обработку запросов дальше:
и тогда уже сервера на бэкенде будут разбираться с SSL, а не HAProxy 🙂
Так будет выглядеть схема. В этом случае HAProxy передаст запрос на транспортном уровне, а его терминация произойдет на одном из серверов бэкенда.
Прикладной уровень или Layer 7
Прикладной уровень посложнее. Однако он позволит запускать несколько групп серверов приложений на одном домене и распределять между ними запросы в зависимости от их содержания.
Распределение нагрузки на прикладном уровне пригодится, если, например, один сайт состоит из разных веб-приложений.
При таком раскладе вы бы точно захотели разъединить все это физически. В этом помогут списки контроля доступа (ACL). Чтобы решить задачу, распределим трафик в завимости от пути на сайте:
HAProxy reqrep not replacing string in url
We have deprecated some software which was available under http://localhost/test and is now available under http://localhost/app/testing. We are using HAProxy 1.4
3 Answers 3
Since url rewriting isn’t possible with version 1.4 and we didn’t want to update HAProxy, we went on using reqrep and keeping the old link as is with
Try this
This works for me with your scenario on HAProxy 1.6
Using redirect prefix is meant, going by the HAProxy examples, more for changing the hostname, but keeping the path. For example, http://apple.com/my/path could be redirected to http://orange.com/my/path with:
With «redirect prefix», the «Location» header is built from the concatenation of and the complete URI path
To me, that suggests that you would expect whatever you put for the «prefix» will be prefixed to what was already in the path. That explains the behavior you were seeing. This is useful for changing to a new domain (e.g. from apple.com to orange.com), but keeping the original path (e.g. /my/path).
You can switch to using redirect location to replace the entire URL. The following would redirect http://apple.com/my/path to http://orange.com/path:
You might be able to set the «prefix» to «/». The HAProxy docs say:
As a special case, if equals exactly «/», then nothing is inserted before the original URI. It allows one to redirect to the same URL (for instance, to insert a cookie).
Using your code example, something like this: