Наличие HTTPS на веб-сайте долгое время считалось роскошью. Тому много причин: особо за безопасность владельцы блогов и «визиток» не парились, а сам сертификат стоил денег (что-то в районе $100 в год).
Но всё больше в последнее время давление на владельцев веб-ресурсов:
- Гугл обещал поднять в выдаче сайты с HTTPS;
- Браузеры ругаются на страницы полями пароля без HTTPS;
- В конце концов сертификаты стали бесплатными.
Зачем нужен сертификат сайту
Подробнее остановимся на последнем. Всё ещё можно купить сертификат, в котором будет указано, что домен подписан сертификатом, который принадлежит компании «Roga & Kopyta Int.». Но надо ли это?
Сервис Let’s Encrypt предлагает бесплатные сертификаты типа «Этот сетрификат выдан этому домену». То есть по сути гарантирует лишь то, что никакой шутник не подменил сайт, а значит данные пересылаются безопасно. Большинству сайтов такой защиты — за глаза!
Настраиваем Let’s Encrypt на nginx в Ubuntu
Данный способ несколько раз проверен на PHP-сайтах (в том числе wordpress) и Django-сайтах (через uwsgi).
Для начала установим необходимые пакеты для добавления ppa-репозиториев:
apt update apt install -y \ python-software-properties software-properties-common
Добавим программу certbot, которая будет обновлять нам сертификаты. Замечу, что Let’s Encrypt выдаёт сертификаты на 2 месяца, поэтому его надо обновлять автоматически — этим и займётся certbot.
add-apt-repository ppa:certbot/certbot apt update
Ну и поставим сам certbot — репозиторий его ведь добавили!
apt install -y \ certbot
Дальше нам понадобиться добавить путь, по которому сервис Let’s Encrypt будет проверять — мы ли это.
server { ... # Let's Encrypt location ^~ /.well-known/acme-challenge/ { root /path/to/static/; add_header Cache-Control public; allow all; } ... }
Многие руководства советуют добавлять location /.well-known
, но certbot и letsencrypt используют именно /.well-known/acme-challenge
. В связи с этим у меня были определённые трудности, когда я это настраивал для Django-сервиса.
Перезагрузим конфиги nginx:
systemctl reload nginx
И теперь мы можем уже запустить генерацию сертификатов:
certbot certonly -a webroot --webroot-path=/path/to/static/ -d имя-домена.ru -d www.имя-домена.ru
В этот момент certbot положит специальные файлы в /path/to/static
, которые будут доступны по урлу http://имя-домена.ru/.well-know/acme-challenge
. Сервер let’s encrypt сходит по этому url и проверит — мы ли это, никто не подменил наш сайт. После того, как проверка завершится, certbot положит сертификаты, подписанные letsencrypt в директорию /etc/letsrncrypt/live/имя-домена.ru
(их мы и будем использовать).
После чего мы можем включить https:
server { listen 443 ssl; server_name имя-домена.ru www.имя-домена.ru; # SSL cert ssl_certificate /etc/letsencrypt/live/имя-домена.ru/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/имя-домена.ru/privkey.pem; # Let's Encrypt location ^~ /.well-known/acme-challenge/ { root /path/to/static/; add_header Cache-Control public; allow all; } }
И снова перечитаем конфигурацию nginx:
systemctl reload nginx
Теперь проверяем — работает ли https (заходим на сайт по https).
Ну и чтобы всегда использовать https — настроим перенаправление с http на https:
server { listen 80; server_name имя-домена.ru www.имя-домена.ru; return 301 https://имя-домена.ru$request_uri; access_log off; }
В очередной раз перечитаем конфигурацию nginx:
systemctl reload nginx
Теперь должно всё работать.
Забодьтесь о безопасности своих пользователей — теперь это ещё и «бесплатно».
Ответы на вопросы читателей
Q: Я правильно понимаю, что бот certbot будет сам заходить на сервис Let’s Encrypt и продлевать сертификаты каждые 2 месяца?
A: Современные версии certbot добавляют в crontab запись для автоматического обновления.
Если же не хотите пологаться на то, что кто-то позаботится об обновлении,
можете добавить сами в cron команду
/usr/bin/letsencrypt renew && nginx -s reload
. Раз в день / неделю – впролне себе нормально.
Например, от юзера, у которого есть права на letsencrypt renew
и nginx -s reload
выполнить:
echo "0 0 * * * /usr/bin/letsencrypt renew && nginx -s reload" | crontab
– каждый день в полночь по времени сервера.
Q: Эту /.well-known/acme-challenge/
директорию надо создавать или нет?
A: В описанном location есть директива root
. Она указывает на директорию, куда будет направлять данный URL.
Эту же директорию мы указываем certbot-у как --webroot-path
- он её сам создаст и будет генерить нужные файлы для подтверждения,
что это действительно тот домен, которым представляется.
Q: Ладно, все заработало - но есть нюанс - по http показывает обычный рабочий сайт, но стоит зайти по https - выдает заглушку nginx - типа он установлен бла-бла-бла
A: Нужно все локейшены продублировать в сервере, который с https (тот что listen 443), ведь это отдельный сервер, который сидит на отдельном порту.
Есть вариант всё это proxy_pass-ом завернуть на http-шный сервер, но я бы советовал нормально все локейшены и директивы а ля "gzip on" и тд перенести в https сервер. После этого - проверить и сам http редиректить на https. Например, вот так:
server { listen 80; server_name 900913.ru www.900913.ru; return 301 https://900913.ru$request_uri; access_log off; }
Вообще, весь конфиг сайта может выглядеть вот так (в примере – uwsgi-приложение):
# Редирект с http на https server { listen 80; server_name 900913.ru www.900913.ru; return 301 https://900913.ru$request_uri; access_log off; } # Редирект с www на домен без www server { listen 443 ssl; server_name www.900913.ru; return 301 https://900913.ru$request_uri; ssl_certificate /path/to/file/fullchain.pem; ssl_certificate_key /path/to/file/privkey.pem; access_log off; } server { listen 443 ssl; server_name 900913.ru; ssl_certificate /path/to/file/fullchain.pem; ssl_certificate_key /path/to/file/privkey.pem; gzip on; gzip_comp_level 7; gzip_vary on; gzip_static on; gzip_types text/plain text/css text/javascript application/javascript application/x-javascript image/svg+xml image/png image/jpeg; client_max_body_size 20m; fastcgi_pass_request_body on; # Let's Encrypt location ^~ /.well-known/acme-challenge/ { root /certbot/webroot-path/; add_header Cache-Control public; allow all; } location ~ /\. { deny all; } location = /robots.txt { alias /path/to/file/robots.txt; } # static location ~* \.(html|txt|jpg|jpeg|gif|png|pdf|ico|css|bmp|js|swf|otf|woff|ttf|gz|svg|ogg)$ { root /path/to/static-files; expires 1M; add_header Cache-Control public; } location / { uwsgi_pass 127.0.0.1:{{ UWSGI PORT }}; include uwsgi_params; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Host $http_host; } }