haproxy redirect to https
Redirect HTTP to HTTPS with HAProxy
Use the HAProxy load balancer to redirect users from HTTP to HTTPS automatically.
For decades, our lives have become increasingly dependent on sending and receiving data from across the Internet. Now, with more people working, studying, and hanging with friends remotely, that trend is showing an uptick. Yet, adversaries seem to be grasping at that communication from every direction. From hackers sniffing traffic at WiFi hotspots to governments seeking access to unprecedented levels of browsing data, privacy on the Web is becoming a David versus Goliath scenario.
One way to protect user privacy is by encrypting the communication en route by using the venerated protocol, HTTPS, which is the secure version of HTTP. A case can be made for encrypting every website with HTTPS, regardless of whether a website collects sensitive information or not, to give people a blanket shield of privacy even during their most run-of-the-mill activities. By encrypting communication, website owners can hide what could be considered too much information in the wrong hands: the specific web pages you’ve visited, the terms you’ve searched for, and the details of your browser and operating system.
The problem is that HTTPS is not the default; Unencrypted, visible-to-anyone-with-the-means HTTP is the default: When you type google.com or youtube.com into your browser’s address bar, do you prefix it with https? If you’re like most people, the answer is no. The result is that you’re sent to the non-secure, HTTP version of the site. (Luckily, these specific websites automatically redirect to HTTPS). If you’re using the Chrome browser, you’ll see a bold warning, “Not secure”, when you visit an unencrypted site.
It’s easy to correct this shortcoming on your own website: Place HAProxy in front of your web servers and configure it to reroute users from HTTP to HTTPS automatically. HAProxy, besides being known as the fastest and most widely used software load balancer, is also heavily relied upon for its ability to terminate SSL, which is to say, it can handle all aspects of HTTPS by acting as a middleman between the client and servers. However, since it can balance any type of TCP traffic, not just HTTP, you must choose whether to enable a secure connection for HTTP.
Enabling HTTPS
HAProxy load balances traffic across a pool of web servers, ensuring that if one of your servers fails, there are others to take over. As traffic passes through, HAProxy terminates SSL, which means that it decrypts the traffic before it is forwarded to the servers and encrypts it again on its way back out to the user.
In the following example, the HAProxy configuration file is set to listen for HTTP traffic on port 80 and HTTPS traffic on port 443:
It’s common to listen for both types of traffic and then forward all HTTP requests to HTTPS, as you’ll see in the next section. In the current example, we’re accepting connections for either, but have not yet added a redirect. HTTPS traffic is decrypted using the file ssl.pem, which contains both the site’s public SSL certificate and its private key. To create it, you’ll cut and paste your X.509 certificate and private key into one file so that it looks like the following snippet (truncated here for space):
With this setup, the backend servers receive decrypted traffic only and never need to bother with SSL themselves. If you’d like to inform the backend server whether HTTPS was used, you can append an X-Forwarded-Proto request header by adding the http-request set-header directive:
You can learn much more about HAProxy’s SSL capabilities in our blog post HAProxy SSL Termination.
Redirect to HTTPS
After you’ve configured HAProxy to terminate SSL, the next step is to redirect all users to HTTPS. This is very simple: add an http-request redirect line to your frontend section, as shown here:
The http-request redirect line is quite versatile, allowing you to redirect users to a different domain, such as from oldsite.com to newsite.com, or redirect them to the canonical name of your site, such as from mysite.com to www.mysite.com. For our purposes, we use it to change the protocol from http:// to https:// by including the scheme parameter, which is set to https.
Since you don’t want to send users to HTTPS if they’ve already connected that way, which would cause an infinite loop, append unless < ssl_fc >to the end of the line. The ssl_fc function returns true if the connection came in over HTTPS. By checking it, you’re stating that you want to redirect the user, but only if they’re not already using a secure connection.
This technique will only work when using mode http because it redirects at the HTTP layer using a 302 Found HTTP response status, which is known as a temporary redirect. Once you’re fully committed to using HTTPS and have tested it thoroughly on your website, you may wish to instruct the browser to cache the redirect, which will save one round trip between the browser and HAProxy, speeding up page load times. Set the code parameter to 301 to send a 301 Moved Permanently status back, which browsers can cache:
Using mode http has other benefits too, since it proxies the request at Layer 7; In this mode, you can then use metadata from the decrypted HTTP request, such as cookie values, HTTP headers, and the URL path, to choose the best backend, modify the message, or perform extra validation.
You may also want to set up HSTS to instruct browsers to use HTTPS with your website by default. Read our blog post HAProxy and HTTP Strict Transport Security (HSTS) to learn more.
Conclusion
As concern about the privacy of our online lives continues to grow in relevance, it’s essential that more websites adopt a secure-by-default posture. Automatically redirecting users to HTTPS is one way to protect people from eavesdropping. HAProxy has SSL termination built in, giving you the ability to encrypt communication as it leaves your network and reroute all users to a secure version of your site. The good news is that enabling this feature is easy!
Want to stay up to date on similar topics? Subscribe to this blog! You can also follow us on Twitter and join the conversation on Slack.
Interested in advanced security and administrative features? HAProxy Enterprise is the world’s fastest and most widely used software load balancer. It powers modern application delivery at any scale and in any environment, providing the utmost performance, observability, and security. Organizations harness its cutting edge features and enterprise suite of add-ons, backed by authoritative expert support and professional services. Ready to learn more? Sign up for a free trial.
Как перевести сайт целиком на постоянный 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 отправляется корректно:
Разбираем методы проксирования на основе HAProxy
Недавно пришлось разбираться с проксированием доступа к веб-серверам с помощью HAProxy. Основная проблема оказалась в шифрованном доступе. Кому интересна эта тема, добро пожаловать под кат.
Есть в нашей компании ряд веб-серверов. Для экономии адресов доступ к ним организован через HAProxy. Примерно вот так:
При этом конфигурация самого HAProxy крайне простая (пример №1):
backend mytest1_web
mode http
cookie SERVERID insert indirect nocache
server mytestweb1 192.168.1.5:80 check cookie mytestweb1
backend mytest2_web
mode http
cookie SERVERID insert indirect nocache
server mytestweb2 192.168.1.10:80 check cookie mytestweb2
Здесь и далее я буду приводить не полные примеры конфиг-файла, а только интересующие нас куски.
Предельно просто — слушаем 80 порт и разбираем весь входящий траффик. Если запрашивается mytest1.loc, то он попадает в access-list is_mytest1, в этом случае используется бэкенд mytest1_web, в котором мы перенаправляем траффик на внутренний хост 192.168.1.5, где у нас и находится данный сайт. Аналогично и для mytest2.loc. Все предельно просто и при этом мы экономим реальные IP адреса.
Встал вопрос отказоустойчивости, тем более что в соседнем городе у нас тоже есть сервера, где мы можем поднять данные веб-сайты. Ну и есть виртуалка с линуксом в облаке Amazon, которая делает тоже самое, но для сайтов, расположенных в облаке. Можем ли мы использовать 2 HAProxy подряд? Поднимаем примерно такую тестовую схему и смотрим:
Конфигурации HAProxy2 и HAProxy3 не изменились, однако в HAProxy1 добавился параметр балансировки (пример №2):
backend mytest1_web
mode http
balance roundrobin
cookie SERVERID insert indirect nocache
server HAProxy1 1.1.1.1:80 check cookie haproxy1_1
server HAProxy2 3.3.3.3:80 check cookie haproxy2_1
backend mytest2_web
mode http
balance roundrobin
cookie SERVERID insert indirect nocache
server HAProxy1 1.1.1.1:80 check cookie haproxy1_3
server HAProxy2 3.3.3.3:80 check cookie haproxy2_3
Все отлично заработало. Казалось бы, можно радоваться, но тут сайты решено было переделать под работу через SSL. И начались проблемы.
Вернемся к началу и рассмотрим все с начала. Первое допущение — нам не нужно шифровать траффик между прокси-сервером и самим сайтом. Второе — нам не нужно заботиться о привязке сертификата к сайту, то есть у нас везде используются самоподписанные сертификаты.
Что нам нужно сделать? Сгенерировать и установить на нашем HAProxy сервере самоподписанный сертификат, тогда клиент, обращаясь на сайт, будет попадать на наш прокси-сервер, брать его сертификат, устанавливать защищенное соединение с сервером и дальше перенаправляться на веб-сайт. Причем для всех сайтов, находящихся за прокси-сервером, будет использоваться один сертификат.
Итак, приступим. Генерируем:
Записываем в один файл:
cat server.key > /etc/ssl/mytest.loc.pem
cat server.crt >> /etc/ssl/mytest.loc.pem
И редактируем конфигурацию HAProxy(пример №3):
backend mytest1_web
mode http
balance roundrobin
cookie SERVERID insert indirect nocache
server mytestweb1 192.168.1.5:80 check cookie mytestweb1
backend mytest2_web
mode http
balance roundrobin
cookie SERVERID insert indirect nocache
server mytestweb2 192.168.1.10:80 check cookie mytestweb2
Кстати, проверять конфигурацию перед перезапуском — это очень полезная привычка:
Проверяем и видим, что все отлично работает.
Что ж, а теперь возьмем случай, когда на каждом сайте свой сертификат, причем не самоподписанный, а купленный. И в нем есть строгая привязка к имени сайта. В этом случае мы можем решить вопрос двумя способами: разместить сертификаты для сайтов на HAProxy сервере или проксировать TCP вместо HTTP. Но в обоих случаях мы не сможем обойтись одним IP адресом для двух наших сайтов.
Рассмотрим первый случай:
Все отличие данного случая от предыдущего(с самоподписанными сертификатами) только в том, что здесь нам придется слушать отдельные интерфейсы и выдавать сертификат в зависимости от интерфейса (пример №4):
backend mytest1_web
mode http
balance roundrobin
cookie SERVERID insert indirect nocache
server mytestweb1 192.168.1.5:80 check cookie mytestweb1
backend mytest2_web
mode http
balance roundrobin
cookie SERVERID insert indirect nocache
server mytestweb2 192.168.1.10:80 check cookie mytestweb2
Вроде все понятно, если траффик пришел на интерфейс с адресом 1.1.1.1, значит, клиент запрашивает сайт mytest1.loc. Значит, мы выдаем ему сертификат этого сайта и дальше проксируем на backend mytest1_web.
Во-втором случае мы пробрасываем полностью весь TCP-траффик, который пришел к нам на 443 порт. Это стоит сделать, например, тогда, когда вы по каким-либо причинам не хотите, чтоб сертификаты сайтов хранились на прокси сервере. Или, например, не доверяете внутренней сети между прокси и веб серверами.
Конфигурация HAProxy будет примерно следующая(пример №5):
frontend mytest1_frontend
bind 1.1.1.1:443
mode tcp
use_backend mytest1_webssl
backend mytest1_webssl
mode tcp
option ssl-hello-chk
server mytestweb 192.168.1.5:443
frontend mytest2_frontend
bind 9.9.9.9:443
mode tcp
use_backend mytest2_webssl
backend mytest2_webssl
mode tcp
option ssl-hello-chk
server mytestweb 192.168.1.10:443
Вроде вполне понятная конфигурация. Наверно, стоит сказать, что поскольку мы прокидываем весь TCP траффик, мы не можем анализировать его, и поэтому наличие любых access-lists в frontend части будет выдавать ошибку.
Настало время вернуться к нашей задаче с разделением web сайтов по разным городам. Для начала рассмотрим более простой случай:
Поскольку между HAProxy1 и HAProxy2 у нас интернет, то даже при использовании самоподписанных сертификатов мы не можем использовать HTTP PROXY MODE на HAProxy1, иначе теряется весь смысл в шифровании такого соединения. Будем использовать на HAProxy1 tcp mode, а на HAProxy2 http mode.
Конфигурация для HAProxy1 (пример №6):
frontend https_frontend
bind *:443
mode tcp
use_backend https_web
backend https_web
mode tcp
option ssl-hello-chk
server haproxy2 1.1.1.1:443
Конфигурация для HAProxy2 будет идентична конфигурации в примере №3ю
Настало время добавить вторую часть серверов:
Конфигурация для HAProxy1 (пример №7):
frontend https_frontend
bind *:443
mode tcp
use_backend https_web
backend https_web
mode tcp
balance roundrobin
option ssl-hello-chk
server haproxy2 1.1.1.1:443 check
server haproxy3 3.3.3.3:443 check
Все отличие данного примера от предыдущего — наличие балансировки и отказоустойчивости между HAProxy2 и HAProxy3, которые по нашему условию должны находиться в разных городах.
Что ж, последний пример будет в случае наличия несамоподписанных сертификатов. Предположим, что они установлены на HAProxy серверах — как в примере №4:
Конфигурация для HAProxy1 будет как в предыдущем примере, а для HAProxy2 — как в примере №4. Такая же для HAProxy3 с небольшими изменениями реальных адресов а frontend части.
Также стоит сказать, что можно все 3 HAProxy сервера настроить в TCP MODE — и это тоже будет работоспособное решение.
Ну и напоследок хотелось бы сказать: если кто-то знает какие-то принципиально отличающиеся решения для данной задачи, буду признателен, если вы поделитесь ими.
Небольшой апдейт. Мне тут подумалось, что если мы пробрасываем трафик, то нам незачем использовать на HAProxy2 и HAProxy3 набор различных IP адресов, гораздо проще просто использовать разные порты.
Давайте рассмотрим вот такую схему:
backend mytest1_web
mode http
balance roundrobin
cookie SERVERID insert indirect nocache
server mytestweb1 1.1.1.1:80 check cookie mytestweb1
server mytestweb1 2.2.2.2:80 check cookie mytestweb1
backend mytest2_web
mode http
balance roundrobin
cookie SERVERID insert indirect nocache
server mytestweb2 1.1.1.1:80 check cookie mytestweb2
server mytestweb2 2.2.2.2:80 check cookie mytestweb2
backend mytest3_web
mode http
balance roundrobin
cookie SERVERID insert indirect nocache
server mytestweb3 2.2.2.2:80 check cookie mytestweb3
frontend mytest1_frontend
bind 3.3.3.3:443
mode tcp
use_backend mytest_webssl1
backend mytest_webssl1
mode tcp
balance roundrobin
option ssl-hello-chk
server mytestweb1 1.1.1.1:55551
server mytestweb2 2.2.2.2:55551
frontend mytest2_frontend
bind 4.4.4.4:443
mode tcp
use_backend mytest_webssl2
backend mytest_webssl2
mode tcp
balance roundrobin
option ssl-hello-chk
server mytestweb1 1.1.1.1:55552
server mytestweb2 2.2.2.2:55552
frontend mytest3_frontend
bind 5.5.5.5:443
mode tcp
use_backend mytest_webssl3
backend mytest_webssl3
mode tcp
balance roundrobin
option ssl-hello-chk
server mytestweb2 2.2.2.2:55553
И конфигурация HAProxy3:
frontend mytest1_frontend
bind 2.2.2.2:55551 ssl crt /etc/ssl/mytest1.loc.pem
mode http
option httpclose
option forwardfor
reqadd X-Forwarded-Proto:\ https
use_backend mytest1_web
frontend mytest2_frontend
bind 2.2.2.2:55552 ssl crt /etc/ssl/mytest2.loc.pem
mode http
option httpclose
option forwardfor
reqadd X-Forwarded-Proto:\ https
use_backend mytest2_web
frontend mytest3_frontend
bind 2.2.2.2:55553 ssl crt /etc/ssl/mytest3.loc.pem
mode http
option httpclose
option forwardfor
reqadd X-Forwarded-Proto:\ https
use_backend mytest3_web
backend mytest1_web
mode http
balance roundrobin
stats enable
cookie SERVERID insert indirect nocache
server mytestweb1 192.168.1.5:80 check cookie mytestweb1
backend mytest2_web
mode http
balance roundrobin
stats enable
cookie SERVERID insert indirect nocache
server mytestweb2 192.168.1.10:80 check cookie mytestweb2
backend mytest3_web
mode http
balance roundrobin
stats enable
cookie SERVERID insert indirect nocache
server mytestweb3 192.168.1.15:80 check cookie mytestweb3