October 23, 2021

SSL сертификат let's encrypt

Базовые настройки

2. Как установить и настроить Certbot для регулярного использования.
2.1 Регистрация в Let's Encrypt
1. Что требуется от nginx и как настроить nginx для получения сертификатов.
3. Как получать сертификаты и как проверить полученный сертификат.
4. Как установить сертификат от Let's Encrypt в nginx.
5. Как автоматически обновлять сертификаты.

1. Что требуется от nginx и как настроить nginx для получения сертификатов.

Для nginx нужны три директивы с путями до сертификатов.

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

Такой конфиг стоит определить в /etc/nginx/conf.d/default.conf, в стороне от конфигов конкретных сайтов:

server { 
    listen server.example.com:80 default_server; 
    include acme; 
    location / { 
        return 301 https://$host$request_uri; 
        } 
    }

Полный рабочий пример конфига.

server {
    server_name www.example.com;
    listen 443 ssl default_server;
    # укажем default_server для клиентов без SNI
    listen [::]:443 ssl default_server;
    # слушаем и IPv6 тоже

    # подключим файл с путями к сертификатам и ключу
    include ssl/example.com.conf;

    # исключим возврат на http-версию сайта
    add_header Strict-Transport-Security "max-age=31536000";

    # явно "сломаем" все картинки с http://
    add_header Content-Security-Policy "img-src https: data:; upgrade-insecure-requests";

    # далее все, что вы обычно указываете
    #location / {
    #    proxy_pass ...;
    #}
}

server {
	server_name first.com www.first.com;
	root /var/www/first.com;

	index index.html;
	location / {
		try_files $uri $uri/ =404;
	}

	listen [::]:443 ssl ipv6only=on; # managed by Certbot
	listen 443 ssl; # managed by Certbot
	ssl_certificate /etc/letsencrypt/live/first.com/fullchain.pem; # managed by Certbot
	ssl_certificate_key /etc/letsencrypt/live/first.com/privkey.pem; # managed by Certbot
	include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
	ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
	if ($host = www.first.com) {
		return 301 https://$host$request_uri;
	} # managed by Certbot

	if ($host = first.com) {
		return 301 https://$host$request_uri;
	} # managed by Certbot

	listen 80 default_server;
	listen [::]:80 default_server;

	server_name first.com www.first.com;
	return 404; # managed by Certbot
}

ssl_certificate /etc/letsencrypt/live/YOUR-DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/YOUR-DOMAIN/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/YOUR-DOMAIN/chain.pem;

ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1440m;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS";

ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1d;
ssl_session_tickets off;

ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;

ssl_stapling on;
ssl_stapling_verify on;

add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload;";
add_header Content-Security-Policy "default-src 'none'; frame-ancestors 'none'; script-src 'self'; img-src 'self'; style-src 'self'; base-uri 'self'; form-action 'self';";
add_header Referrer-Policy "no-referrer, strict-origin-when-cross-origin";
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

location / {
    return 301 https://$server_name$request_uri;
}

 
# After installing NGINX, this needs to be installed at /etc/nginx/sites-available/

server {
	# 10/7/17; See https://github.com/crspybits/SyncServerII/issues/35
	client_max_body_size 100M;
	
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	
	# Edit this to reflect the name of your server. Mine is accessed as `https://syncserver.cprince.com`
	server_name syncserver.cprince.com;
	
	# SyncServer uses some http request headers with underscores
	underscores_in_headers on;

	# You'll need to edit these paths to reflect the location of the SSL files on your system.
	ssl_certificate /etc/letsencrypt/live/syncserver.cprince.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/syncserver.cprince.com/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/syncserver.cprince.com/fullchain.pem;
    
	ssl_session_timeout 1d;
	ssl_session_cache shared:SSL:50m;
	ssl_session_tickets off;

	ssl_protocols TLSv1.2;
	ssl_ciphers EECDH+AESGCM:EECDH+AES;
	ssl_ecdh_curve secp384r1;
	ssl_prefer_server_ciphers on;

	ssl_stapling on;
	ssl_stapling_verify on;

	add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload";
	add_header X-Frame-Options DENY;
	add_header X-Content-Type-Options nosniff;

    location / {
		proxy_set_header Host $http_host;
		proxy_set_header X-NginX-Proxy true;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-ForwaitHub, Inc.
Terms
Privacy
Security
Status

2. Как установить и настроить Certbot для регулярного использования.

Чтобы установить пакет certbot и плагин для Nginx с помощью стандартного пакетного менеджера, введите команду:

sudo apt install -y certbot python3-certbot-nginx

2.1 Регистрация в Let's Encrypt

Регистрацию нужно сделать только один раз:

certbot register --email me@example.com

*IMPORTANT NOTES: - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal.

Ввиду лимитов на обращения сначала попробуем получить сертификат для основного домена в режиме «сухого прогона»:

certbot certonly --nginx --dry-run -d site.com -d www.site.com,subdomain.site.com

*Этот вызов должен завершиться без сообщений об ошибках.

Certbot предлагает различные способы получения сертификатов SSL через плагины. Плагин Nginx позаботится о реконфигурации веб-сервера и при необходимости перезагрузит его. Чтобы использовать этот плагин, введите:

sudo certbot --nginx -d example.com -d www.example.com
sudo certbot certonly -d example.com -d www.example.com

sudo certbot certonly --dns-digitalocean --dns-digitalocean-credentials ~/certbot-creds.ini --cert-name volodichev -d volodichev.com -d www.volodichev.com,me.volodichev.com,django.volodichev.com,smm.volodichev.com,restapi.volodichev.com,parsing.volodichev.com,bot.volodichev.com,parsing.ru.com,aiohttp.volodichev.com,jinja2.volodichev.com,jinja.volodichev.com

certbot certonly --webroot --agree-tos --no-eff-email --email YOUR@EMAIL.COM -w /var/www/letsencrypt -d www.domain.com -d domain.com

*Эта команда запустит certbot с плагином — nginx, а флаг –d определит имена, для которых предназначен сертификат.

У Let's Encrypt есть лимиты на количество обращений за сертификатами, потому сначала попробуем получить необходимый сертификат в режиме для тестов:

certbot certonly --dry-run -d example.com -d www.example.com

* В конце программа должна отчитаться об успешной работе: -> The dry run was successful.

Чтобы проверить статус таймера, введите:

sudo systemctl status certbot.timer

certbot.timer - Run certbot twice dailyLoaded: loaded (/lib/systemd/system/certbot.timer; enabled; vendor preset: enabled)Active: active (waiting) since Mon 2020-05-04 20:04:36 UTC; 2 weeks 1 days agoTrigger: Thu 2020-05-21 05:22:32 UTC; 9h leftTriggers: ● certbot.service

Чтобы протестировать процесс обновления сертификата, запустите сухой прогон:

sudo certbot renew --dry-run

ACME

Нам нужен какой-то каталог, в который certbot будет писать свои файлы, и какой должен быть доступен из сети удостоверяющему серверу согласно протокола ACME.

Чтобы не писать каждый раз длинную строку из опций, а еще лучше — не вспоминать о них, запишем основные настройки в файл конфигурации, который certbot ожидает найти в /etc/letsencrypt/cli.ini:

authenticator = webroot 
webroot-path = /var/www/html
post-hook = service nginx reload
text = True

Ожидается что certbot будет создавать необходимые для проверки прав на домен файлы в подкаталогах ниже по иерархии к указанному. Вроде таких:

/var/www/html/.well-known/acme-challenge/example.html

Эти файлы должны будут быть доступны из сети на целевом домене по крайней мере по HTTP:

http://www.example.com/.well-known/acme-challenge/example.html

Для следующих проверок создадим какой-то такой файл:

mkdir -p /var/www/html/.well-known/acme-challenge
echo Success > /var/www/html/.well-known/acme-challenge/example.html

Создадим файл /etc/nginx/acme с содержанием:

# cat /etc/nginx/acme 
location /.well-known {
    root /var/www/html;
}

Затем для каждого домена и поддомена, для которых нужно получить сертификаты, в блоке server перед всеми блоками location укажем:

include acme;

Перезагрузим nginx и проверим что наш тестовый файл виден:

service nginx reload
curl -L http://www.example.com/.well-known/acme-challenge/example.html
# => Success

После проверки лучше удалить тестовый файл — certbot любит удалять за собой всё лишнее, а такой файл будет мешать и вызывать сообщение об ошибке (Unable to clean up challenge directory).

rm /var/www/html/.well-known/acme-challenge/example.html

Установка и использование сертификатов

Certbot не перезаписывает сертификаты, а заменяет их ссылками на самые актуальные варианты сертификатов в определенном каталоге, одноименном с первым доменом сертификата (т.е. CN).

Давайте посмотрим что за файлы у нас есть:

 # find /etc/letsencrypt/live/ -type l
/etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/chain.pem
/etc/letsencrypt/live/example.com/privkey.pem
/etc/letsencrypt/live/example.com/cert.pem

С этим знанием мы можем задать настройки SSL для nginx:

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

Как видите, cert.pem нигде в конфиге не используется, и это не ошибка. Для nginx он не нужен.

Полный рабочий пример конфига:

server {
    server_name www.example.com;
    listen www.example.com:443 ssl; # default_server;
    # выше можно добавить default_server для клиентов без SNI

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 127.0.0.1 8.8.8.8;

    # исключим возврат на http-версию сайта
    add_header Strict-Transport-Security "max-age=31536000";

    # явно "сломаем" все картинки с http://
    add_header Content-Security-Policy "img-src https: data:; upgrade-insecure-requests";

    # далее всё что вы обычно указываете
    #location / {
    #    proxy_pass ...;
    #}
}

Конфиг для переадресации с голого домена без www:

server {
    server_name example.com;
    listen example.com:443 ssl;
    access_log off;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 127.0.0.1 8.8.8.8;

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

    expires max;
    return 301 https://www.example.com$request_uri;
}

Подразумевается что вы используете какой-то локальный сервер для кеширования DNS запросов. Если это не так, то 127.0.0.1 в директиве resolver нужно заменить на IP используемого DNS сервера.

Настройки шифров и прочее подобное (ssl_dhparam, ssl_session_cache) лучше держать вне конфигов отдельных серверов.

В папке /etc/letsencrypt/live/vps.losst.ru/ находятся такие файлы сертификатов:

  • cert.pem - файл сертификата, использовать его мы не будем;
  • chain.pem - файл цепочки сертификата, тоже не будем использовать;
  • privkey.pem - приватный ключ сертификата, надо прописать в параметре ssl_certificate_key;
  • fullchain.pem - в нём объединено содержимое cert.pem и chain.pem, надо прописать в параметре ssl_certificate.

Для SSL надо создать отдельный файл виртуального хоста, в котором порт прослушивания будет 443, и будут присутствовать несколько директив настройки SSL:

Автообновление сертификата

Минус сертификатов от Lets Encrypt в том, что они актуальны только 90 дней. За 30 дней до истечения этого срока их рекомендуется перевыпускать. Для этого существует специальная команда:

certbot renew

Она проверяет все сертификаты, установленные в системе и перевыпускает те, что скоро будут просрочены. Чтобы настроить автоматический перевыпуск сертификатов просто добавьте эту команду в crontab:

crontab -e
17 */12 * * * certbot renew --quiet --allow-subset-of-names >> /var/log/le-renew.log

*Согласно рекомендаций Let's Encrypt следует пытаться обновить сертификаты два раза в день. Делать это нужно в случайным образом выбранную минуту того часа, а значит вам нужно заменить 42 в этой строке на другое число в диапазоне между 0 и 59. Либо вы можете поступить так как это делается в /etc/cron.d/certbot.

5 0 15 */1 * sudo certbot certonly --dns-digitalocean --dns-digitalocean-creden$
10 0 15 */1 * cp -r -fL /etc/letsencrypt/live/volodichev/. /etc/ssl/volodichev.$
15 0 15 */1 * certbot renew --post-hook "sudo service nginx restart"

**В этой команде ключ --allow-subset-of-names нужен чтобы Certbot пытался получить сертификаты для частичного набора доменов.

Например, были у вас на сервере были сайты www.example.com и shop.example.com, проходящие под одним сертификатом, но потом вы перенесли shop.example.com на другой сервер. Если такой ключ не указать, то Certbot упадет с ошибкой при попытке подтвердить владение shop.example.com, не получив для вас вообще никакого сертификата. Сертификат истечет и ваш сайт уйдет в оффлайн. С этим ключом вы всё же получите сертификаты хотя бы для частичного набора доменов, оставив ваши сайты в сети.

Доступ к HTTPS через брандмауэр

Если вы включили брандмауэр ufw в соответствии с предварительными требованиями, вам нужно будет настроить его так, чтобы разрешить трафик HTTPS. К счастью, при установке Nginx регистрирует в ufw несколько профилей.

Вы можете просмотреть текущие настройки с помощью следующей команды:

sudo ufw status

Copy

Возможно профиль будет выглядеть так, т. е. на веб-сервере будет разрешен только трафик HTTP:

OutputStatus: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Nginx HTTP                 ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Nginx HTTP (v6)            ALLOW       Anywhere (v6)

Чтобы разрешить трафик HTTPS, активируйте профиль Nginx Full и удалите лишний профиль Nginx HTTP:

sudo ufw allow 'Nginx Full'
sudo ufw delete allow 'Nginx HTTP'

Copy

Теперь ваш статус должен выглядеть следующим образом:

sudo ufw status

Copy

OutputStatus: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx Full (v6)            ALLOW       Anywhere (v6)

Если нужно добавить поддомен или домен в сертификат

Если вы вдруг забыли указать поддомен www, или вам нужно добавить другой домен или поддомен в сертификат (которых может быть до 100 в одном сертификате), то это легко сделать после получения сертификата. Просто запустите команду еще раз, добавив требуемое имя:

certbot certonly -d example.com -d www.example.com -d shop.example.com

Вам будет безальтернативно предложено добавить этот домен в сертификат. Если хочется избежать вопросов, то можно сразу указать одобряющий такое поведение ключ:

certbot certonly --expand -d example.com -d www.example.com -d subdomain.example.com

Проверим полученный сертификат

Убедимся что полученный сертификат — именно тот, что нам нужен:

openssl x509 -text -in /etc/letsencrypt/live/example.com/cert.pem

UDP 15/11/2021:

При ошибке типа: "PermissionError: [Errno 13] Permission denied: '/etc/letsencrypt/live" нужно дать права к следующим директjриям:

sudo chmod +x /etc/letsencrypt/live
sudo chmod +x /etc/letsencrypt/archive

Источники:

  1. https://habr.com/ru/post/318952
  2. https://losst.ru/nastrojka-ssl-v-nginx-s-lets-encrypt
  3. https://www.alexeykopytko.com/2017/free-ssl-from-letsencrypt
  4. https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-20-04-ru
  5. https://routerus.com/secure-nginx-with-let-s-encrypt-on-ubuntu-20-04
  6. https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx
  7. https://gist.github.com/cecilemuller/a26737699a7e70a7093d4dc115915de8