Настраиваем Nginx для работы по HTTPS (SSL) с сертификатами Let’s Encrypt

Продолжая тему настройки защищенного соединения для веб-сервера, мы сегодня рассмотрим работу с Nginx. Данный сервер может применяться как самостоятельно, а также в виде frontend для Apache, либо вообще, как обратный-прокси. Это позволяет эффективно использовать его для защиты посредством SSL даже тех узлов и сервисов, настроить работу которых по защищенному протоколу может быть проблематично. В данном материале мы будем рассматривать настройку SSL в связке с Let’s Encrypt — бесплатным центром сертификации, сделавшем шифрование доступным действительно каждому.

Мы не будем касаться первоначальной настройки, будем считать, что веб-сервер уже настроен вами и работает. Наш экземпляр был настроен по инструкции Настраиваем веб-сервер на базе Nginx + PHP-FPM в Debian / Ubuntu Server и мы в дальнейшем будем придерживаться его конфигурации. В данном примере используются Nginx 1.17PHP 7.3 и MariaDB 10.3 в среде Debian 10, но с небольшими поправками данная статья может использоваться для любого основанного на Debian дистрибутива.

Будем считать, что все конфигурационные файлы сайтов располагаются в /etc/nginx/sites-available, символические ссылки на активные сайты в /etc/nginx/sites-enabled, а шаблоны настроек в /etc/nginx/templates. Если вы используете иное расположение конфигурационных файлов, то это следует учитывать при выполнении описанных ниже действий.

В качестве примера будем рассматривать некий сайт example.org для которого будет настроена минимальная конфигурация следующего вида и установлена популярная CMS WordPress:

server {

listen 80;

server_name example.org;
charset utf-8;
root /var/www/example.org;

index index.php;

access_log /var/log/nginx/example.org_access.log;
error_log /var/log/nginx/example.org_error.log;

include /etc/nginx/templates/php-fpm.conf;
}

server {

listen 80;

server_name www.example.org;
rewrite ^(.*) http://example.org$1 permanent;
}

Прежде всего создадим новый шаблон для работы с Let’s Encrypt:

touch /etc/nginx/templates/le.conf

Откроем его и внесем следующий текст:

location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/letsencrypt;
}

location = /.well-known/acme-challenge/ {
return 404;
}

И подключим его в конфигурационный файл нашего сайта, добавив в самый конец основной секции server строку:

 include /etc/nginx/templates/le.conf;

Также не забудем создать указанную нами в шаблоне директорию и сделать ее владельцем веб-сервер:

mkdir /var/www/letsencrypt
chown -R www-data:www-data /var/www/letsencrypt

Проверим правильность конфигурации nginx:

nginx -t 

и перезапустим веб-сервер:

nginx -s reload

Затем установим certbot, в современных дистрибутивах достаточно выполнить:

apt install certbot

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

certbot certonly --dry-run --webroot -w /var/www/letsencrypt -d example.com -d www.example.com

Ключ —dry-run указывает на тестовый режим, —webroot задает используемый плагин, в данном случае работающий с уже имеющимся веб-сервером. Опция -w указывает рабочую директорию, которую мы настроили ранее, а через опцию -d задаются домены и поддомены, для которых мы хотим получить сертификат. Если все прошло успешно, то вы получите лаконичное сообщение:

Убедившись, что все работает нормально, можно получить рабочие сертификаты, для этого еще раз запустим приведенную выше команду, но уже без ключа —dry-run:

certbot certonly --webroot -w /var/www/letsencrypt -d example.com -d www.example.com

Символические ссылки на полученные сертификаты хранятся в /etc/letsencrypt/live, это позволяет использовать один и тот же путь несмотря на постоянное обновление сертификатов раз в 90 дней. Настройки продления для каждого сайта расположены в /etc/letsencrypt/renewal, откроем файл с настройками нашего домена /etc/letsencrypt/renewal/example.com.conf и внесем в секцию [renewalparams] следующую опцию:

[renewalparams]
...
renew_hook = nginx -s reload

Порядок расположения опций значения не имеет, данная настройка задает действие при успешном обновление сертификата, в данном случае это перезапуск веб-сервера nginx.

После получения сертификата настроим наш виртуальный хост на работу с ним. Для этого вернемся к конфигурационному файлу /etc/nginx/sites-available/example.org.conf и создадим в нем новую секцию server, внеся в нее следующие строки:

server {
listen 443 ssl http2;
server_name example.org;

if ($host != $server_name) {
return 444;
}

Первая опция предписывает использовать ssl и протокол HTTP/2 (если такая возможность поддерживается клиентом), а вот следующее за ним условие требует пояснений. Работа с HTTPS устроена таким образом, что если у сервера был запрошен по защищенному протоколу сайт, не имеющий сертификата, либо вообще произошло обращение по IP-адресу, то в ответ будет показан тот защищенный сайт, который находится первым в конфигурации. Во многих случаях такое поведение будет неожиданным для пользователя и его следует избегать. В нашем случае, при запросе узла отличного от указанного в конфиге сервер оборвет соединение без отправки данных (ошибка 444 в Nginx).

Ниже добавим пути к сертификатам и настройки SSL-сессии:

ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

Для обеспечения режима прямой секретности нам понадобится файл параметров Диффи-Хелмана, создадим его:

openssl dhparam -out /etc/ssl/private/dhparam.pem 2048

В данном случае мы используем криптостойкость ключа в 2048 бит, это минимальный размер для современных условий, для повышения криптостойкости можно использовать ключ длинной 4096 бит. Генерация ключа может занять длительное время в зависимости от вычислительной мощности вашего сервера.

Добавим путь к нему в наш конфигурационный файл:

ssl_dhparam /etc/ssl/private/dhparam.pem;

Теперь один из наиболее важных шагов — указание используемых шифров, это достаточно сложная тема, поэтому воспользуемся для генерации актуального и современного набора алгоритмов шифрования сервисом moz://a SSL Configuration Generator, из предлагаемых им вариантов наиболее оптимальным является вариант Intermediate.

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

Первая строка указывает допустимые протоколы, вторая — шифры и, наконец, третья, включает приоритет клиента в выборе протокола и алгоритма шифрования.

Следующую опцию пока следует закомментировать:

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

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

Следующие строки отвечают за поддержку OCSP Stapling, что позволяет ускорить проверку сертификата клиентом и ускорить загрузку сайта.

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.org/chain.pem;
resolver 8.8.8.8;

Ниже скопируем из основной секции сервер оставшиеся параметры и немного отредактируем их:

charset utf-8;
root /var/www/example.org;
index index.php;

access_log /var/log/nginx/example.org_ssl_access.log;
error_log /var/log/nginx/example.org_ssl_error.log;

include /etc/nginx/templates/php-fpm.conf;
include /etc/nginx/templates/le.conf;
}

Проверим конфигурацию и перезапустим веб-сервер:

nginx -t 
nginx -s reload

После чего убедимся, что сайт работает по защищенному протоколу HTTPS:

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

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

add_header Strict-Transport-Security "max-age=63072000" always;

Затем приведем к следующему виду секции:

server {
listen 80;
server_name example.org;
return 301 https://$host$request_uri;
}

server {
listen 80;
server_name www.example.org;
rewrite ^(.*) https://example.org$1 permanent;
}

Которые обеспечат перенаправление всех HTTP запросов на HTTPS версию сайта.

Еще раз проверим конфигурацию и перезапустим веб-сервер:

nginx -t 
nginx -s reload

На этом настройка Nginx для работы по защищенному протоколу с использованием сертификатов Let’s Encrypt закончена, но приведенная нами конфигурация не является истиной в последней инстанции, технологии развиваются и то, что еще сегодня считалось отличным, завтра может оказаться недостаточным. Поэтому советуем время от времени изучать последние рекомендации и обновлять конфигурацию собственного сервера.