Debian.pro

Блог для пользователей и администраторов Debian


Большой мануал: часть 17. Настраиваем SSL в nginx и получаем A-rate.

Эта статья — часть Большого Мануала по настройке lamp-сервера на debian.

Предыдущая часть цикла — Подготавливаем nginx.

Следующая часть цикла — Подготавливаем apache2.

Сейчас, когда у нас есть сертификат и более-менее подготовленный к реальной жизни nginx, мы можем настроить ssl (и подготовиться к тому, чтобы заводить сайты с SSL).
Первым делом, я напоминаю, что нельзя «включить ssl» для домена, можно включить ssl только для ip+порта. То есть, если у вас на сервере заведено 2 сайта и вы включаете ssl только для первого, то по адресу https://второй_сайт у вас тоже будет открываться первый сайт (либо другая ЁНХ, в зависимости от конфигурации). При этом вы не можете тупо повесить редирект для каждого не-https сайта на http — для каждого доменного имени вам понадобится сертификат (а ещё лучше — один сертификат на все имена). Браузер (как и любой другой https-клиент) проверяет содержимое сертификата до того, как сделать запрос к веб-серверу по https (то бишь сначала браузер делает хендшейк с сервером, устанавливает зашифрованное соединения (проверяя валидность сертификата), и только потом отправляет http-запрос с какими-либо данными внутри зашифрованной сессии). Поэтому, чтобы браузер с адреса https://второй_сайт смог средиректить на http://второй_сайт, для второго сайта тоже нужен валидный сертификат.
Если вы всё же попали в данную ситуацию, то намного проще заказать для сервера дополнительный ip и включать https только на дополнительном ip.

Теперь о том, что на одном ip «можно» использовать несколько сертификатов. Я вам не советую (конечно, если речь не о сайте с посещалкой <1000 человек, среди которых вряд ли найдутся маргиналы со старыми ОС). Дело в том, что SNI (а именно он позволяет делать финт ушами с несколькими сертификатами) в виндах стал распространяться начиная с висты. В XP SNI реализован только в некоторых браузерах, да и то с определенной версии. В итоге в голом интернете ходят люди с XP+Firefox3.x, которые вообще никогда не слышали про SNI и чхать они на него хотели - они на вашем сайте увидят сообщение о невалидном сертификате (при условии, что они не пришли на сайт, сертификат которого совпадает с сертификатом в дефолт-хосте).
Суть SNI в том, что браузер перед хендшейком передаёт серверу заголовок Host (обычно это доменное имя сайта, на который нам нужно попасть), что позволяет серверу выбрать правильный сертификат до проверки валидности сертификата браузером. А печаль в том, что в интернете 10-15% пользовательских машин не умеют SNI. Всякие банковские софтины (если банк ходит к вам с запросами при оплате на вашем сайте, например) SNI не поддерживают. Вообще 90% всего написанного на Java в мире SNI не поддерживает по ощущениям =) Да чего уж там, curl в debian 6 тоже не поддерживал SNI, хотя всего лишь 4 месяца назад он был lts-дистрибутивом и до сих пор широко распространен в интернетах. Так что задумайтесь, надо ли оно в вашем случае, и откуда может раздаться выстрел в ногу.

Теперь о том, почему A-rate, а не A+. Всё очень просто — механика получения А+ зависит от регистратора вашего сертификата. Ей богу, мне лень описывать хотя бы десяток примеров с разными регистраторами сертов, а их больше сотни, емнип. A-rate подразумевает, что вы настроили ssl так, что в нём нет известных уязвимостей, этого достаточно в 99% случаев (gmail.com имеет A, google.com B). Если вы не знаете, что такое ssl-rate, то пошарьтесь где-нибудь здесь (там же сможете и свой сервер протестировать).

Ну а теперь перейдем к настройке nginx. Сертификат уже должен быть получен.
Первым делом генерируем файл с шифрами для DH. Этот файл нужен для того, чтобы заработал т.н. «perfect forward secrecy». Тут стоит сделать лирическое отступление и напомнить, что для расшифровки ssl-трафика нужен не только приватный ключ сертификата, но и сессионный ключ. Если дальше не лезть в дебри криптографии, то DH позволяет генерировать «одноразовые» ключи, которые используются при обмене сессионными ключами. Они уничтожаются по окончанию ssl-хендшейка, что затрудняет последующее восстановление сессионного ключа из дампа трафика (т.к. спереть нужно будет уже не только приватный ключ и дамп трафика, но и файл с dhparam — без всего этого восстановить содержимое tls-трафика уже не получится).

root@server:~# mkdir /etc/nginx/ssl/
root@server:~# openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048

Теперь скопируем наш сертификат в /etc/nginx/ssl (помните мы его в /root/cert.pem клали?). Если сертификат у вас где-то ещё — то положите его в /etc/nginx/ssl/cert.pem сами.

root@server:~# mv /root/cert.pem /etc/nginx/ssl

Выставим корректные права на каталог и файлы внутри:

root@server:~# chmod 700 /etc/nginx/ssl/
root@server:~# chmod 600 /etc/nginx/ssl/*

Теперь создадим файл /etc/nginx/includes/ssl, который мы будем подключать там, где нужно включить ssl (plain-text без комментариев)

# указываем путь до сертификата (цепочка должна быть внутри этого файла).
ssl_certificate /etc/nginx/ssl/cert.pem;
# указываем путь до приватного ключа сертификата (мы их клали в один файл, если у вас файлы разные - то указывайте правильный файл)
ssl_certificate_key /etc/nginx/ssl/cert.pem;

# указываем путь до файла с dh-шифрами
ssl_dhparam /etc/nginx/ssl/dhparam.pem;

# на самом деле это нахрен тут не нужно, но на всякий случай оставим.
# мы игнорируем "просьбы" клиентов о приоритете шифров внутри старых реализаций SSL.

ssl_prefer_server_ciphers on;
# вот только все "старые" реализации ssl мы отключаем, ибо они уязвимы к перехвату трафика.
# оставляем только семейство протоколов TLS, уязвимость которых пока не доказана.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# включаем только те шифры, которые на текущий момент считаются более или менее безопасными.
ssl_ciphers kEECDH+AES128:kEECDH:kEDH:-3DES:kRSA+AES128:kEDH+3DES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv2;
# включаем кэш для SSL-сессий (на 64 мегабайта)
# если у вас много клиентов - увеличивайте размер кэша.

ssl_session_cache shared:SSL:64m;
# ssl-сессия из кэша удаляется через 28 часов, но клиент сможет запросить новую сессию раньше.
ssl_session_timeout 28h;

# Если вы собираетесь включить HSTS - то расскомментируйте строчку ниже.
# Единожды получивший этот заголовок браузер больше никогда не обратится к вашему ресурсу по http (даже если пользователь руками явно пойдет на http) до тех пор, пока не истечет max-age (в секундах).
# при этом при каждом новом посещении сайта по https счетчик max-age будет обновляться
# используйте этот параметр только тогда, когда будете уверены, что https настроен на каждом сайте корректно и он там настроен навсегда.

#add_header Strict-Transport-Security "max-age=31536000; always";

Если у вас всё же отдельные сертификаты на каждый сайт — то параметры ssl_certificate и ssl_certificate_key уберите из файла includes/ssl и прописывайте их руками в каждом server {}. Ну и вообще вы можете не использовать include в этих местах, я просто делаю как удобнее. Скорее всего, эти параметры (особенно, список шифров) когда-нибудь придется менять и лучше менять их в одном месте.

Теперь создадим файл /etc/nginx/listen_ssl, который мы будем инклудить в те server{} в которых нам нужно включить ssl. Ещё раз напоминаю вам, что для сайтов с ssl лучше держать отдельный IP (только dns-запись сменить не забудьте), чтобы сайты, которым не досталось ssl-сертификата, вообще «не отвечали», если на них зайти по https.
Файл /etc/nginx/listen_ssl у нас будет выглядеть примерно так:

listen 198.51.100.1:443 ssl;
include includes/ssl;

Или так (но учтите, что вы включите https на всех ip-адресах и не забудьте про то, что listen port; и listen ip:port; сочетаются между собой весьма неожиданным образом):

listen 443 ssl;
include includes/ssl;

Само собой, нам нужно создать default-vhost и для https-порта. Создайте файл /etc/nginx/sites-available/00-default-ssl:

server {
    listen 198.51.100.1:443 ssl default;
    include includes/ssl;
    location / { return 404; }
    include includes/letsencrypt;
}

Сделайте симлинк:

root@server:~# ln -s /etc/nginx/sites-available/00-default-ssl /etc/nginx/sites-enabled/00-default-ssl

Ну и напоследок. Если вам лень заморачиваться с инклудами, читать статью, а хочется просто скопипастить, то в конфиг того server{} для которого вы собираетесь включить ssl, вам нужно вставить вот такое:

listen 443 ssl;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/cert.pem;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers kEECDH+AES128:kEECDH:kEDH:-3DES:kRSA+AES128:kEDH+3DES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv2;
ssl_session_cache shared:SSL:64m;
ssl_session_timeout 28h;
#add_header Strict-Transport-Security "max-age=31536000; always";

После всех мучений рестартуйте nginx (именно restart, а не reload в данном случае). Перед этим проверьте валидность конфиг-файла:

root@server:~# nginx -t
18.06.2016 byinkvizitor68sl|big-manual

Комментарии (3):

  1. cru5ader :

    Кстати, This server is vulnerable to the POODLE attack. If possible, disable SSL 3 to mitigate. Grade capped to C — debian.pro

  2. Нууу… сапожник без сапог =)

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

  3. Кстати, fixed.

Написать комментарий