Almost Over

SSL сертификат от Let's Encrypt под NGINX

SSL сертификат от Let's Encrypt под NGINX

Я понимаю, что тема о Let’s Encrypt уже давно не нова, только найти максимально чёткую инструкцию по установке и автопродлению мне так и не удалось. Да, есть темы на Хабре в 2016-ом за Март, Май и Июль. Нашел ещё такой вариант, такой и даже такой. И каждая из них чем-то да отличается. Причём не понятно, какой мануал универсальнее и правильнее. Поэтому, предлагаю свою вариацию руководства, собрав самое лучшее из всего найденного.

Для инструкции по установке под различные системы следует посетить официальный сайт Let’s Encypt.

Текущее же руководство подразумевает установку Let’s Encypt под консольный CentOS 6/7.

$DOMAIN — основной домен сайта (например, almostover.ru).
$SUB.DOMAIN — поддомен сайта (например, cdn.almostover.ru).

Установка Let’s Encypt

$ cd /usr/sbin
$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto

Команды Let’s Encypt

Вывод общей справки

$ certbot-auto --help

Вывод справки по топикам

$ certbot-auto --help all|automation|commands|paths|security|testing

Вывод справки по подкомандам

$ certbot-auto --help certificates|certonly|config_changes|delete|install|plugins|register|renew|revoke|rollback|run(default)|unregister|update_symlinks

Вывод справки по плагинам

$ certbot-auto --help apache|nginx|standalone|manual|webroot

Вывод полной справки

$ certbot-auto --help all

Разберём самые важные моменты.

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

1
2
3
4
5
6
7
8
--apache              Obtain and install certs using Apache (default: False)
--nginx Obtain and install certs using Nginx (default: False)
--standalone Obtain certs using a "standalone" webserver. (default:
False)
--manual Provide laborious manual instructions for obtaining a
cert (default: False)
--webroot Obtain certs by placing files in a webroot directory.
(default: False)
  1. Получение и установка используя Apache. Данная опция подразумевает, что в конфиги Апача будут автоматически внесены изменения.
  2. Получение и установка используя Nginx. Аналогично с Апаче.
  3. Автономный веб-сервер. Подразумевает запуск независимого сервера при генерации сертификата и его остановку при завершении задачи.
  4. Ручная установка.
  5. Автоматически загружает нужные для валидации файлы в указанную директорию.

Получение сертификата

Наиболее частые используемые плагины для автоматической генерации сертификатов Let’s Encypt:

  1. Webroot.
  2. Standalone.

Через Webroot

Первый вариант попроще второго, но он и имеет как минимум один существенный недостаток: оставляет в корне сайта директорию «.well-known».
Если Вы выбрали этот вариант, следует выполнить команду ниже и перейти к настройке SSL.

$ certbot-auto certonly --agree-tos -m admin@$DOMAIN --rsa-key-size 4096 --webroot -w /var/www/vhosts/$DOMAIN -d $DOMAIN -d www.$DOMAIN -d $SUB.DOMAIN

Через Standalone

Второй вариант не оставляет нигде никаких лишних директорий. Используется автономный веб-сервер, с проксированием запросов на Nginx.

Создаём letsencrypt.conf

$ nano /etc/nginx/global/letsencrypt.conf
1
2
3
4
5
6
location ~ ^/(.well-known/acme-challenge/.*)$ {
proxy_pass http://127.0.0.1:9999/$1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Подключаем letsencrypt.conf

$ nano /etc/nginx/sites/$DOMAIN
1
2
3
4
5
6
7
8
server {
listen *:443 ssl http2;
server_name $DOMAIN;

+# if SSL
+ # Include letsencrypt standalone server.
+ include /etc/nginx/global/letsencrypt.conf;
}

Обратите внимание, letsencrypt.conf должен быть подключен ко всем доменам/поддоменам, для которых будут генерироваться сертификаты через standalone сервер.

Генерируем сертификат через standalone сервер

$ certbot-auto certonly --allow-subset-of-names --agree-tos -m admin@$DOMAIN --rsa-key-size 4096 --standalone --preferred-challenges http --http-01-port 9999 --server https://acme-v01.api.letsencrypt.org/directory -d $DOMAIN -d www.$DOMAIN -d $SUB.DOMAIN

Настройка SSL

Генерируем dhparam.pem

$ openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048

На 3х ядерном VDS под KVM ключ на 2048 сгенерировался за ~2 минуты.
Для генерации ключа 4096 при подобном среднем сервере придётся очень долго ждать, поэтому тут рекомендую оставить на ночь.

Создаём ssl.conf

$ nano /etc/nginx/global/ssl.conf
1
2
3
4
5
6
7
8
9
10
11
12
# Generic SSL enhancements. Use https://www.ssllabs.com/ssltest/ to test and recommend further improvements.

# Don't use outdated SSLv3 protocol. Protects against BEAST and POODLE attacks.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

# Use secure ciphers.
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK';

# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits. To generate your dhparam.pem file, run in the terminal
# openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096
ssl_dhparam /etc/nginx/ssl/dhparam.pem;

Создаём ssl-hsts.conf

$ nano /etc/nginx/global/ssl-hsts.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
# Config to enable HSTS (https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security)
# to avoid ssl stripping (https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping).

# This header tells browsers to treat all subdomains of main domain and to connect exclusively via HTTPS.
# ATTENTION! Option "preload" save your domains in Chrome preload list. It's strongly not recommended to use it,
# if you not completely understand what you are doing. More info about it here: https://hstspreload.org
#add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload" always;

# This header tells browsers to reset cache of saved HSTS sessions, if "preload" option was not defined.
#add_header Strict-Transport-Security "max-age=0" always;

# This header tells browsers that it should only be communicated with using HTTPS, instead of using HTTP.
add_header Strict-Transport-Security "max-age=31536000" always;

Создаём ssl-session.conf

$ nano /etc/nginx/global/ssl-session.conf
1
2
3
4
5
6
# Define the size of the SSL session cache in MBs. 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions. Default: none.
# Why in http block? See: http://stackoverflow.com/questions/22732045/session-cache-not-detected-in-nginx
ssl_session_cache shared:SSL:10m;

# Define the time in minutes to cache SSL sessions. Default: 5m.
ssl_session_timeout 1h;

Подключаем ssl-session.conf

$ nano /etc/nginx/nginx.conf
1
2
3
http {
+ include /etc/nginx/global/ssl-session.conf;
}
$ service nginx reload

При вышеописанной SSL-конфигурации, ранг на SSL Server Test будет A+.
Для большинства сайтов этого с лихвой хватит, но если нужно что-то более изысканное, могу предложить к прочтению руководство на тему тонкой настройки SSL.

Установка сертификата

$ nano /etc/nginx/sites/$DOMAIN
1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen *:443 ssl http2;
server_name $DOMAIN;

# if SSL
# Include letsencrypt standalone server.
include /etc/nginx/global/letsencrypt.conf;

+ ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
+ include /etc/nginx/global/ssl.conf;
+ include /etc/nginx/global/ssl-hsts.conf;
}
$ service nginx reload

Настройка автопродления

Тестовая проверка продления

$ certbot-auto renew --allow-subset-of-names --post-hook "service nginx reload" --dry-run

Ручное продление

$ certbot-auto renew --allow-subset-of-names --noninteractive --post-hook "service nginx reload" --force-renewal

Добавление в крон

$ crontab <(crontab -l; echo "00 3 * * 0 certbot-auto renew --allow-subset-of-names --noninteractive --post-hook "service nginx reload" >> /var/log/le-renew.log")
$ service crond reload

Отличие --post-hook и --renew-hook заключается в том, что post хук выполняется после обновления всех сертификатов, а renew - после обновления каждого.
Стало быть, нет никакого смысла использовать --renew-hook в связке с NGINX, ведь перезагрузить конфиг достаточно единожды по окончании всей операции.

Самые часто используемые команды

$ certbot-auto certificates - вывод списка текущих сертификатов.
$ certbot-auto --help automation - вывод справки по автоматизации.