В этом руководстве я бы хотел показать, как сгенерировать LetsEncrypt SSL сертификат для тестового сайта с минимальными трудозатратами. Я буду использовать Nginx в режиме reverse proxy в качестве web сервера.
Этот текст рассчитан для новичков в настройке Nginx и описанный способ настройки не подойдет для использования в production. Я решил написать это руководство потому что мне понадобилось быстро развернуть сайт с SSL, но я не нашел простого способа сделать это. В сети доступны Docker образы, которые позволяют добавить в инфраструктуру слой, отвечающий за обновление сертификатов, но эти решения требуют настройки.
Мы же будем использовать manual режим бота LetsEncrypt или сторонний сервис, который выполнит команду (в случае, если вы не дружите с консолью). Использование стороннего сервиса потенциально небезопасно, так как сервис может сохранить ваш приватный ключ у себя.
Получение ключа
В качестве отправной точки предположим у нас имеется сервис myserv.com
на который Nginx перенаправляет HTTP запросы.
upstream myserv{
server myserv:80;
}
server {
listen 80;
listen[::]:80;
index index.html index.htm index.nginx-debian.html;
server_name myserv.com;
location / {
proxy_pass http://myserv;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 64m;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
}
}
Для того чтобы ответить на запрос LetsEncrypt и подтвердить владение доменом мы должны отдать файл в поддиректории /.well-known
. Для этого добавим следующий код в конфигурацию сервиса:
upstream myserv{
server myserv:80;
}
server {
listen 80;
listen[::]:80;
index index.html index.htm index.nginx-debian.html;
server_name myserv.com;
location /.well-known {
root /var/www/ssl-proof/myserv/;
}
location / {
proxy_pass http://myserv;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 64m;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
}
}
Мы можем получить файлы валидации при помощи команды
sudo certbot certonly --manual --preferred-challenges http -d myserv.com
или инициализации процесса проверки домена на стороннем сервисе (например, SSL For Free или Zero SSL). Далее необходимо скопировать эти файлы в директорию /var/www/ssl-proof/myserv/.well-known
. Если вы используете Docker, то вам придется или подключить эту директорию к контейнеру или пересобрать образ.
Теперь вы можете валидировать домен и получите файл сертификата, приватный ключ (если не указывали свой) и файл цепочки подписей (trust chain).
Настройка SSL
Для работы Nginx достаточно сертификата и приватного ключа. Большинство браузеров сами проверяют промежуточные сертификаты. Если же вы хотите обращаться к своему сервису с помощью веб клиента, то вам придется создать файл сертификата, который включает в себя информацию о промежуточных CA.
Вам нужно склеить файл сертификата с файлом промежуточных сертификатов:
cat myserv.crt bundle.myserv.crt >> chained.myserv.crt
Порядок важен, так как ключ будет применяться к первому сертификату. Если в конце первого сертификата отсутствует перевод строки — добавьте его.
Теперь мы можем обновить конфигурацию нашего сервиса:
upstream myserv{
server myserv:80;
}
server {
listen 80;
listen [::]:80;
server_name myserv.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
listen [::]:443;
index index.html index.htm index.nginx-debian.html;
server_name myserv.com;
ssl_certificate /etc/letsencrypt/live/myserv/chained.myserv.crt;
ssl_certificate_key /etc/letsencrypt/live/myserv/myserv.key;
location /.well-known {
root /var/www/ssl-proof/myserv/;
}
location / {
proxy_pass http://myserv;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 64m;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
}
}
Вы можете проверить наличие промежуточных сертификатов послав запрос к вашему сайту утилитой curl
. Если промежуточные сертификаты не доступны, вы получите предупреждение.
Если ваши сервисы не используют директорию /.well-known
, то можно не останавливаться на достигнутом и настроить автоматическое продление сертификатов. В моем случае /.well-known
использовалась IdentityServer4 и мне пришлось отключить отдачу файлов валидации домена.
Я надеюсь, что эта статья поможет вам попробовать LetsEncrypt и начать использовать их сертификаты вместо self-signed сертификатов.
P. S.: судя по комментариям к посту мне следует еще раз объяснить назначение этого текста. Часто когда я спрашиваю людей, почему они не используют SSL, они говорят о сложностях настройки. Это минимальная инструкция не претендующая на полноту.
Комментарии (11)
spxnezzar
27.10.2017 12:31Я немного не понял. А чем это от стандартного мануала letsencrypt отличается? А где запись в кроне на обновление сертификатов или вы ручками каждые три месяца ходить по серверам планируете?
grossws
27.10.2017 19:38Здесь уже были довольно бездарные статью по использованию le, но вы старались пробить дно, судя по всему. Про отсутствие автоматического перевыпуска сертификатов вам уже написали. Ещё стоит сказать, что настройка tls/ssl в nginx'е выполнена абсолютно бездарно. Рекомендую проверить настроенный таким образом сервер с помощью Qualys SSL Server Test, а потом ознакомиться с guide'ом от Mozilla.
Michael_SL Автор
27.10.2017 21:17Отсутствие перевыпуска сертификата связано с тем, что приложение отдаёт другие данные по
/.well-known
. Если вы можете предложить простой способ настройки маршрутизации запросов, я с удовольствием добавлю его в пост.
Вот проверка сайта настроенного таким образом:grossws
27.10.2017 21:21location /.well-known/acme-challenge { ... }
, остальное в/.well-known
форвардите вторым location'ом. Естественно, имеет смысл настраивать и для http, и для https, чтобы обеспечить и бутстрап, и штатное обновление.
Если вы хотите вставить картинку, то рекомендуется её положить в спойлер и воспользоваться habrastorage.org.
rt3879439
27.10.2017 21:12-1Сначала возмущаешься зачем же люди пишут простенькие howto на хабре, коих и так у же по данной теме предостаточно даже на хабре, а потом смотришь полезные комментарии и успокаиваешься.
grossws
27.10.2017 21:23Только люди, которые пишут эти полезные комментарии с каждым разом всё менее спокойные. Т. к. повторять одно и то же регулярно утомляет.
alexkuzko
certbot очень тяжёлый… Я бы посоветовал посмотреть в сторону dehydrated, как примера очень легковесного acme клиента. Также стоит отметить что если у вас будет более одного сервера, стоит создать единый виртуальный хост для выдачи сертификатов, на который вы и будете делать редиректы с необходимых доменов.
kvaps
А мне acme.sh нравится.
Лёгкий, простой и интуитивно понятный (по сути shell скрипт обычный), но все что нужно он умеет.
questor
Можно подробнее про единый виртуальный хост?
alexkuzko
Смотрите. Когда у вас 2 и более серверов ЛИБО прокси-сервера (CDN-подобные) либо DNS Round Robin фронты, то вам требуется один и тот же сертификат установить на всех звеньях. А т.к. вы не знаете на какой из них дойдет реальный запрос верификации (говорим про Domain Validation через http(s) к .well-known, а не через DNS), то на офф.сайте рекомендуют использовать 301 редиректы на некую единую точку, которую вы авторизуете ответить с подтверждением.
Пример на пальцах: domain1 + domain2, на каждом из них для локации (location) .well-known делаем Rewrite/Redirect на acme.domain3, тем самым мы можем валидировать домены domain1 и domain2 на сервере acme.domain3.
Далее, когда у нас уже есть редирект, мы можем всю процедуру выдачи, сохранения и заливки сертификатов перевести на единый хост (он тоже может быть кластеризирован, но сейчас не об этом). Хотите, можете использовать certbot, хотите dehydrated, хотите acme.sh, хотите — поиграйтесь с Docker и dockerletsencryptmanager + (опционально) le-git-sync.
Это крайне удобно. Представьте, вам надо выдать сертификат на почтовик, а на него ставить ничего нельзя? Никаких клиентов, ничего. Тогда в зависимости от наших возможностей проброса 80-го порта для хостнейма этого почтовика, мы можем сделать редирект на наш acme.domain3 и все делать на нем, включая заливку сертификата на почтовик и его перезапуска. Аналогично и для всех прокси-серверов. Теперь мы можем обновленные сертификаты деплоить на все наши сервера (например, через Ansible, или просто через rsync/scp+ssh).