3.7к
0
0
Скопировать ссылку
Telegram
WhatsApp
Vkontakte
Одноклассники
Назад

SSL-сертификаты, Let's Encrypt и Docker

Время чтения 15 минут
Нет времени читать?
Скопировать ссылку
Telegram
WhatsApp
Vkontakte
Одноклассники
3.7к
0
0
Нет времени читать?
Скопировать ссылку
Telegram
WhatsApp
Vkontakte
Одноклассники

Привет, меня зовут Александр Ададуров. Я руководитель проектов ФГБУ «Центр информационно-технического обеспечения». В этой статье я рассмотрю два основных подхода в организации защищенного HTTPS-соединения между клиентами веб-сервисов и серверами веб-приложений, а также рассмотрю примеры создания бесплатных SSL-сертификатов от Let’s Encrypt с использованием Docker-контейнеров. Всё это будет интересно в первую очередь тем, кто работает с веб-приложениями, имеющими распределенную архитектуру. В начале статьи будет небольшое погружение в исторический контекст появления бесплатных сертификатов от Let’s Encrypt.

SSL-сертификаты, Let's Encrypt и Docker

Предыстория

Браузер Google Chrome предупреждает о незащищенном соединении

SSL-сертификаты необходимы для шифрованного HTTPS-соединения между браузером пользователя сайта и сервером. Шифрование данных при передаче затрудняет перехват паролей и вообще любых пользовательских данных на маршруте следования сетевых пакетов от клиента к серверу. Сейчас шифрованные HTTPS-соединения используются не только на сайтах, которые обрабатывают важные пользовательские данные, а практически повсеместно. 

Началось это с 2011 года, когда крупные интернет-компании и разработчики популярных браузеров начали продвигать идею обязательного использования шифрованных соединений на всех сайтах. В частности, Google как часть программы «HTTPS — повсеместно» анонсировал, что будет в своем поиске выше ранжировать сайты, работающие по протоколу HTTPS. Далее в Firefox, а затем и другие браузеры была добавлена блокировка загрузки контента по критерию Mixed content, когда веб-страница работает по HTTPS, а часть ее контента, например видео или изображения, подтягивается с другого ресурса без HTTPS, в результате контент с ресурса без HTTPS не отображается. В 2018 году Google объявил, что HTTPS будет обязательным для некоторых видов запросов и сайты, не соответствующие условию, стал помечать как «небезопасные».

Всё это вынудило владельцев сайтов переходить на шифрованные HTTPS-соединения и приобретать SSL-сертификаты. Рынок данных услуг, по данным маркетинговых исследований TechNavio, вырос почти в шесть раз. Конечно, остались сайты, даже крупные, которые игнорируют мэйнстрим и продолжают работать по HTTP, но это скорее исключение из правила.

Сайт одного из топовых веб-серверов (NGINX) не использует HTTPS

Появление Let’s Encrypt

Let’s Encrypt (англ. «давай зашифруем») появился в 2013 году вследствие описанных выше изменений. Не все владельцы сайтов были готовы к новой статье расходов в своем бюджете, и, чтобы переход на HTTPS был более быстрым и плавным, ключевыми игроками интернет-отрасли была создана некоммерческая организация «Исследовательская группа интернет-безопасности» (ISRG), которая с 2015 года начала выпускать бесплатные сертификаты Let’s Encrypt.

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

Организация HTTPS-соединений

Есть два основных подхода к организации HTTPS-соединения в распределенных веб-приложениях, где прямой выход в интернет имеет только один внешний балансировщик (или их кластер), который получает запросы из интернета и передает по внутренней сети веб-серверу приложения.

Подходы в организации HTTPS-соединений в веб-приложениях с распределенной архитектурой

При первом подходе шифрованное соединение устанавливается только между браузером и балансировщиком, далее по локальной сети трафик передается незашифрованным. Это уместно, если никаких рисков перехвата пользовательских данных во внутренней сети нет, нередко такая внутренняя сеть ограничена серверной или даже одной серверной стойкой. При таком подходе будет достаточно установить один SSL-сертификат на балансировщик.

При втором подходе веб-сервер приложения внутри сети также работает по HTTPS — тогда устанавливать и, соответственно, обновлять сертификаты необходимо в двух местах.

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

Certbot + Docker: выпуск сертификата

Типовые инсталляции Certbot и настройка обновления сертификатов неоднократно рассматривались в статьях, поэтому рассмотрим нестандартный случай, когда Certbot невозможно установить, например потому, что Python-библиотеки нашего приложения конфликтуют с ним или политика/архитектура приложения не позволяет ничего прямо установить на хосте, принимающем входящие запросы. Для этого Certbot мы будем запускать в Docker.

У Let’s Encrypt есть в Docker-репозитории собственный образ certbot/certbot, воспользуемся им. Предполагается, что Docker уже установлен, у меня он работает от имени root. Для наглядности опишу исходные данные примера:

1. У меня зарегистрирован тестовый домен conf.novaanna.ru, система DNS его уже видит, и он ищется по IP из любой точки мира.

2. Путь к сайту http://conf.novaanna.ru — /home/ubuntu/www/conf.novaanna.ru. По этому пути лежит index.html со следующим содержанием:


<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Сайт conf.novaanna.ru</title>
</head>

    <body>
        <h1>Сайт conf.novaanna.ru - работает</h1>
    </body>
</html>

 

3. Используется веб-сервер Nginx со следующей конфигурацией в /etc/nginx/sites-enabled/conf.novaanna.ru.conf:


server {
    listen 80;
     server_name conf.novaanna.ru;
     root /home/ubuntu/www/conf.novaanna.ru;

     index index.html index.htm;

    error_log /var/log/nginx/conf_error.log;
    access_log /var/log/nginx/conf_access.log;

    location / {
        try_files $uri $uri/ =404;
     }
 }

 

4. Проверено, что сайт работает, и можно приступать к генерации SSL-сертификата.

Запускаем выпуск сертификата через Docker-контейнер следующей длинной bash-командой:


sudo docker run -it --rm 
  --mount src=/etc/letsencrypt,target=/etc/letsencrypt,type=bind 
  --mount src=/var/lib/letsencrypt,target=/var/lib/letsencrypt,type=bind 
  --mount src=/home/ubuntu/www/conf.novaanna.ru,target=/home/ubuntu/www/conf.novaanna.ru,type=bind 
  --entrypoint /bin/sh 
  certbot/certbot -c "certbot certonly --webroot 
  -w /home/ubuntu/www/conf.novaanna.ru -d conf.novaanna.ru --agree-tos --email adadurov@mail.ru"

 

При запуске Certbor в Docker-контейнере есть определенные нюансы:

  1. Мы не хотим, чтобы контейнер после работы оставался на хосте, поэтому удаляем его ключом –rm.
  2. В то же время нужно позаботиться о том, чтобы результаты работы Certbot не пропали вместе с удаляемым контейнером, поэтому ключом –mount монтируем в контейнер каталоги хоста /etc/letsencrypt и /var/lib/letsencrypt.
  3. Для работы Certbot потребуется доступ в корневую папку сайта (root или DocumentRoot веб-сервера) — /home/ubuntu/www/conf.novaanna.ru, поэтому также монтируем ее в контейнер.
  4. Как известно, Certbot задает разные вопросы при запуске. Чтобы этого не было, мы сразу ему всё сообщим в командной строке: –webroot -w /home/ubuntu/www/conf.novaanna.ru -d conf.novaanna.ru –agree-tos –email adadurov@mail.ru. 

В результате выполнения команды скачался Docker-образ Certbot, создался контейнер на его основе и внутри контейнера был выпущен новый Let’s Encrypt сертификат, о чем сообщает вывод на рисунке ниже.

Вывод bash-команды создания сертификата Let’s Encrypt внутри Docker-контейнера

Теперь можно скорректировать настройку Nginx и перезапустить его, чтобы сайт стал работать по защищенному соединению HTTPS.


server {
 listen 80;
 server_name conf.novaanna.ru;
 return 301 https://$host:443$request_uri;
}

server {
 listen 443 ssl;
 server_name conf.novaanna.ru;
 root /home/ubuntu/www/conf.novaanna.ru;

 ssl_certificate /etc/letsencrypt/live/conf.novaanna.ru/fullchain.pem;   # добавлена строка для работы сертификата
 ssl_certificate_key /etc/letsencrypt/live/conf.novaanna.ru/privkey.pem;  # добавлена строка для работы сертификата

 index index.html index.htm;

 error_log /var/log/nginx/conf_error.log;
 access_log /var/log/nginx/conf_access.log;

 location / {
         try_files $uri $uri/ =404;
 }
}

 

Перезапускаем Nginx командой sudo systemctl reload nginx, и, если зайти на сайт, можно увидеть, что он стал работать по HTTPS.

Сайт заработал по HTTPS

Certbot + Docker: обновление сертификата

Теперь нужно автоматизировать выпуск новых сертификатов по истечении срока. Главный нюанс при обновлении сертификата, особенно в Docker-контейнере, — это необходимость перезапуска Nginx после обновления. Если веб-сервер не перечитает настройки, новый сертификат не вступит в силу. К счастью, для этих целей в Certbot есть специальный ключ –deploy-hook, который позволяет указать скрипт, запускающийся сразу после успешного обновления сертификата.

Из Docker-контейнера перезапустить Nginx на хосте можно через SSH. Для этого выполним ряд действий и дополним нашу bash-команду запуска контейнера. Что нужно сделать прежде всего:

  1. Разрешить подключаемому по SSH пользователю перезапускать веб-сервер. По умолчанию это может делать только root. У меня пользователь ubuntu, и я хочу именно его и использовать. Для этого я через команду sudo visudo добавлю в файл /etc/sudoers строчку “ubuntu ALL=(ALL) NOPASSWD: /bin/systemctl reload nginx”, разрешающую пользователю ubuntu перезапускать Nginx.
  2. Необходимо дать из контейнера по SSH доступ к хосту без пароля. Я это сделаю через монтирование каталога .ssh с хоста прямо в контейнер, в каталог /root, чтобы пользователь контейнера автоматически мог использовать закрытый ключ пользователя ubuntu хоста и подключиться к хосту командой ssh ubuntu@ip_хоста.
  3. Необходимо сделать исполняемый файл со скриптом перезагрузки Nginx, который тоже нужно будет монтировать в контейнер.

Обновленная bash-команда будет выглядеть так:


sudo docker run -it --rm 
  --mount src=/etc/letsencrypt,target=/etc/letsencrypt,type=bind 
  --mount src=/var/lib/letsencrypt,target=/var/lib/letsencrypt,type=bind 
  --mount src=/home/ubuntu/.ssh,target=/root/.ssh,type=bind 
  --mount src=/home/ubuntu/reload-nginx.sh,target=/root/reload-nginx.sh,type=bind 
  --entrypoint /bin/sh 
  certbot/certbot -c "apk update && apk add openssh && certbot renew --deploy-hook /root/reload-nginx.sh"

 

В этой команде мы монтируем SSH-ключ –mount src=/home/ubuntu/.ssh для подключения к хосту из контейнера, подключаем скрипт этого подключения /home/ubuntu/reload-nginx.sh, который будет запущен в контейнере при успешном обновлении:


#!/bin/sh
ssh ubuntu@192.168.1.10 sudo systemctl reload nginx

 

Также мы даем команду на установку самого SSH apk update && apk add openssh, так как в образе Certbot его по умолчанию нет. В конце указываем, что при успешном обновлении нужно запустить скрипт перезапуска Nginx –deploy-hook /root/reload-nginx.sh. В скрипт перезапуска можно добавить также код архивации и отправки архива с сертификатами куда-то в сетевую шару, на почту или в Телеграм (если это не противоречит ИБ-политикам). Это позволит без задержек передать новый сертификат для установки на балансировщике. Если такая интеграция кому-то интересна, пишите в комментариях, я приведу примеры с описаниями.

Обновленную bash-команду следует добавить в Cron и запускать с интервалом 12 часов (два раза в день). Если срок действия сертификата еще не истекает, ничего обновляться не будет, если истекает — команда обновит сертификат и перезапустит Nginx.

Комментарии0
Тоже интересно
Комментировать
Поделиться
Скопировать ссылку
Telegram
WhatsApp
Vkontakte
Одноклассники