[Перевод] Как перевести сайт целиком на постоянный 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 довольно старый, поэтому добавим репозиторий:

sudo add-apt-repository ppa: vbernat/haproxy-1.5 Затем обновим исходники и установим приложение:

sudo aptitude update sudo aptitude install haproxy Основные настройки лежат в /etc/haproxy/haproxy.cfg. Вот мои настройки:

global log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy stats socket /run/haproxy/admin.sock mode 660 level admin stats timeout 30s user haproxy group haproxy daemon # Default SSL material locations ca-base /etc/ssl/certs crt-base /etc/ssl/private ssl-default-bind-ciphers EECDH+ECDSA+AESGCM: EECDH+aRSA+AESGCM: EECDH+ECDSA+SHA384: EECDH+ECDSA+SHA256: EECDH+aRSA+SHA384: EECDH+aRSA+SHA256: EECDH+aRSA+RC4: EECDH: EDH+aRSA:! aNULL:! eNULL:! LOW:!3DES:! MD5:! EXP:! PSK:! SRP:! DSS:! RC4 ssl-default-bind-options no-sslv3 no-tlsv10 tune.ssl.default-dh-param 4096 defaults log global mode http option httplog option dontlognull option forwardfor option http-server-close timeout connect 5000 timeout client 50000 timeout server 50000 errorfile 400 /etc/haproxy/errors/400.http errorfile 403 /etc/haproxy/errors/403.http errorfile 408 /etc/haproxy/errors/408.http errorfile 500 /etc/haproxy/errors/500.http errorfile 502 /etc/haproxy/errors/502.http errorfile 503 /etc/haproxy/errors/503.http errorfile 504 /etc/haproxy/errors/504.http frontend yourservername bind *:80 bind *:443 ssl crt /etc/ssl/private/cert1.pem crt /etc/ssl/private/cert2.pem acl secure dst_port eq 443 redirect scheme https if!{ ssl_fc } rspadd Strict-Transport-Security:\ max-age=31536000;\ includeSubDomains;\ preload rsprep ^Set-Cookie:\ (.*) Set-Cookie:\ \1;\ Secure if secure default_backend webservername backend webservername http-request set-header X-Forwarded-Port %[dst_port] http-request add-header X-Forwarded-Proto https if { ssl_fc } server webservername 192.168.1.50:80 listen stats *:9999 stats enable stats uri / Кстати, если вам понадобится подсветка синтаксиса конфига 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 правильно выбирает нужный из них.

Единственное, что надо сделать — объединить файлы сертификата и приватного ключа в один .pem:

cat your-decrypted-ssl-key.key your-ssl-cert.crt > your-ssl-cert.pem Убедитесь, что владельцем файла будет root: root, и права у него должны быть только на чтение:

sudo chown root: root your-ssl-cert.pem chmod 400 your-ssl-cert.pem От HTTP к HTTPS, и HSTS в качестве бонуса Теперь определим ACL, список контроля доступа. В HAProxy это список вещей, удовлетворяющих определённому критерию: acl secure dst_port eq 443 Мы создали ACL по имени secure, который совпадает со всем, что идёт на TCP порт 443. Он нам скоро понадобится.

Следующая строка — та самая, где происходит перенаправление трафика с HTTP на HTTPS.

redirect scheme https if!{ ssl_fc } Это значит, что если входящий запрос не HTTPS, то надо отправить перенаправление 301 к тому же ресурсу, но по схеме HTTPS.

Это именно то HTTP Strict Transport Security — всем браузерам предписывается использование HTTPS:

rspadd Strict-Transport-Security:\ max-age=31536000;\ includeSubDomains;\ preload Настройка добавляет нужную строку в заголовки. Браузер, распознающий этот заголовок, понимает, что сайт предпочитает работать по HTTPS, и это указание действительно в течение года (31,536,000 seconds). Директива preload сообщает Google-боту, что ваш сайт можно добавить в их список сайтов, поддерживающих HSTS.

HSTS — штука правильная. Шифрование должно быть всегда, и администраторам надо стремиться распространять его везде.

Кстати — необходимо учесть, чтобы все куки также включали атрибут secure, поскольку с точки зрения вашего веб-сервера всё происходит по обычному протоколу HTTP. Для этого используем директиву rsprep:1

rsprep ^Set-Cookie:\ (.*) Set-Cookie:\ \1;\ Secure if secure Обратите внимание на «if secure». Это значит, что меняются только куки, идущие по HTTPS (secure — это переменная, которую мы несколько линий назад определили). Вообще, всё должно работать через HTTPS, поэтому это в принципе необязательно –, но можно и подстраховаться.

Последняя строка определяет, куда отправлять трафик. Это default_backend. Тут можно определять несколько серверов для распределения нагрузки и т.п. Но, поскольку у нас есть один бэкенд-сервер, то всё довольно просто.

back end Поскольку у нас один бэкенд-сервер, эта секция коротка. HAProxy необходимо лишь добавить пару заголовков, чтобы сервер понял, что реальное общение происходит через HTTPS, даже если он видит только лишь HTTP: http-request set-header X-Forwarded-Port %[dst_port] http-request add-header X-Forwarded-Proto https if { ssl_fc } Первая директива устанавливает заголовок, объясняющий, что клиент изначально пришёл на порт 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:

set_real_ip_from 127.0.0.1; real_ip_header X-Forwarded-For; Это будет работать, если вы скомпилили nginx с опцией ngx_http_realip_module.

Перезапустите HAProxy (service haproxy restart) и она начнёт слушать запросы.

Проверка работы Сделайте запрос к сайту через http. Если URL меняется на https и вы видите сайт — всё работает. Можно убедиться, что заголовок HSTS отправляется корректно: curl -s -D- https://yoursite.whatever/ | grep Strict

© Habrahabr.ru