29 Апрель 2016

Получаем и обновляем SSL сертификат Let's Encrypt

Obtaining and renewing Let's Encrypt SSL certificate

Не так давно в промышленную эксплуатацию был запущен центр сертификации Let's Encrypt.

От всех прочих провайдеров SSL сертификатов его отличает две главные особенности.

Во-первых, Let's Encrypt позволяет получить (и в ряде случаев даже установить) полученный сертификат в вашу систему в автоматическом режиме, а, во-вторых, сделать это совершенно бесплатно.

Сочетание этих двух факторов даёт возможность легко получить, быстро установить и забыть об обновлении полученного (одного или нескольких) SSL сертификатов в будущем.

Итак, небольшой how-to который был подготовлен на базе настройки этого самого блога.

Имеем сервер на FreeBSD где, помимо прочего, работает веб-сервер Lighttpd.

root@beta:~ # uname -v
FreeBSD 10.3-RELEASE #0 r297264: Fri Mar 25 02:10:02 UTC 2016     root@releng1.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC
root@beta:~ # lighttpd -v
lighttpd/1.4.39 (ssl) - a light and fast webserver
Build-Date: Mar  3 2016 22:22:13

1. Устанавливаем официальный клиент Let's Encrypt

Удобнее всего это сделать из дерева портов системы.

root@beta:~ # cd /usr/ports/security/py-certbot/
root@beta:/usr/ports/security/py-certbot # make install clean

Сам сайт и система управления им расположены по стандартными системным путям.

root@beta:~ # cd /usr/local/www/kostikov.co/
root@beta:/usr/local/www/kostikov.co # ll
total 104
-rw-r--r--   1 root  wheel   8196  7 сен  2015 .DS_Store
-rw-r--r--   1 root  wheel     80  7 сен  2015 .gitignore
-rw-r--r--   1 root  wheel   1272  7 сен  2015 COPYRIGHT.txt
-rw-r--r--   1 root  wheel  35148  7 сен  2015 LICENSE.txt
drwxr-xr-x   9 root  wheel    512  7 сен  2015 admin/
-rw-r--r--   1 root  wheel    748  7 сен  2015 admin.php
drwxrwxrwx   5 root  wheel    512 27 апр 17:04 content/
-rw-r--r--   1 root  wheel   2072  7 сен  2015 feed.php
-rw-r--r--   1 root  wheel    766  7 сен  2015 index.php
-rw-r--r--   1 root  wheel  21268  7 сен  2015 install.php
drwxr-xr-x   2 root  wheel    512  7 сен  2015 languages/
drwxr-xr-x  18 root  wheel    512  7 сен  2015 plugins/
-rw-r--r--   1 root  wheel   1239  7 сен  2015 sitemap.php
drwxr-xr-x   7 root  wheel    512  7 сен  2015 themes/
-rw-r--r--   1 root  wheel  11690  7 сен  2015 update.php

Добавим в конфигурацию Lighttpd секцию для обслуживания сайта.

root@beta:~ # cat /usr/local/etc/lighttpd/lighttpd.conf
...
# -- kostikov.co
$HTTP["host"] == "kostikov.co" {
        server.document-root = "/usr/local/www/kostikov.co/"
}
...

2. Получаем SSL сертификат

Ввиду того, что мы уже имеем развёрнутый и работающий веб-сервер, воспользуемся для получения сертификата методом webroot (подробнее о доступных методах см. certbot -h).

root@beta:~ # certbot certonly --agree-tos --email postmaster@kostikov.co --webroot -w /usr/local/www/kostikov.co/ -d kostikov.co -d www.kostikov.co

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /usr/local/etc/letsencrypt/live/kostikov.co/fullchain.pem. Your
   cert will expire on 2016-07-26. To obtain a new version of the
   certificate in the future, simply run Let's Encrypt again.
 - If you like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Немного об опциях:

  • certonly - получаем сертификат, но не устанавливаем его автоматически;
  • --agree-tos - соглашаемся с условиями сервиса;
  • --email - предоставляем контактный e-mail, это обязательный параметр;
  • --webroot - подтверждаем право владения доменом через запрос кода по HTTP;
  • -w - путь к контенту сайта для временного размещения кода;
  • -d - домены, которые будет подтверждать данный сертификат. В данном случае, мы выписываем в один сертификат сразу два доменных имени. Let's Encrypt позволяет включить в него до 100 доменов одновременно. К сожалению, и это первый относительный минус данного центра сертификации, он не поддерживает т.н. wildcard, которые позволяют покрыть все домены более низкого уровня по отношению к базовому.

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

root@beta:~ # cd /usr/local/etc/letsencrypt/live/kostikov.co/
root@beta:/usr/local/etc/letsencrypt/live/kostikov.co # ll
total 0
lrwxr-xr-x  1 root  wheel  35 27 апр 17:52 cert.pem@ -> ../../archive/kostikov.co/cert1.pem
lrwxr-xr-x  1 root  wheel  36 27 апр 17:52 chain.pem@ -> ../../archive/kostikov.co/chain1.pem
lrwxr-xr-x  1 root  wheel  40 27 апр 17:52 fullchain.pem@ -> ../../archive/kostikov.co/fullchain1.pem
lrwxr-xr-x  1 root  wheel  38 27 апр 17:52 privkey.pem@ -> ../../archive/kostikov.co/privkey1.pem

Обратите внимание, что предложенный путь ведёт на ссылки на сертификаты (симлинки), а не сами файлы, которые хранятся в другом месте.

root@beta:/usr/local/etc/letsencrypt/live/kostikov.co # cd ../../archive/kostikov.co/
root@beta:/usr/local/etc/letsencrypt/archive/kostikov.co # ll
total 10
-rw-r--r--  1 root  wheel  1814 27 апр 17:52 cert1.pem
-rw-r--r--  1 root  wheel  1647 27 апр 17:52 chain1.pem
-rw-r--r--  1 root  wheel  3461 27 апр 17:52 fullchain1.pem
-rw-r--r--  1 root  wheel  1704 27 апр 17:52 privkey1.pem

Здесь мы видим автоматически сгенерированный набор сертфикатов и их индекс - версию (в данном случае 1):

  • cert1.pem - сам публичный сертификат для наших доменов;
  • chain1.pem - корневые сертификаты центра сертификации Let's Encrypt;
  • fullchain1.pem - полная цепочка сертификатов, включающая cert1.pem и chain1.pem;
  • privkey1.pem - закрытый ключ сертификата.

В связи с тем, что Lighttpd требует предоставления файла сертификата, который включает в себя публичную и закрытую его части создадим его вручную под именем privandcert.pem.

root@beta:/usr/local/etc/letsencrypt/archive/kostikov.co # cat privkey1.pem cert1.pem > privandcert1.pem
root@beta:/usr/local/etc/letsencrypt/archive/kostikov.co # ll
total 14
-rw-r--r--  1 root  wheel  1814 27 апр 17:52 cert1.pem
-rw-r--r--  1 root  wheel  1647 27 апр 17:52 chain1.pem
-rw-r--r--  1 root  wheel  3461 27 апр 17:52 fullchain1.pem
-rw-r--r--  1 root  wheel  3518 27 апр 18:10 privandcert1.pem
-rw-r--r--  1 root  wheel  1704 27 апр 17:52 privkey1.pem

Создадим также и ссылку на него в принятом для данной системы месте.

root@beta:/usr/local/etc/letsencrypt/archive/kostikov.co # cd ../../live/kostikov.co/
root@beta:/usr/local/etc/letsencrypt/live/kostikov.co # ln -s ../../archive/kostikov.co/privandcert1.pem privandcert.pem
root@beta:/usr/local/etc/letsencrypt/live/kostikov.co # ll
total 0
lrwxr-xr-x  1 root  wheel  35 27 апр 17:52 cert.pem@ -> ../../archive/kostikov.co/cert1.pem
lrwxr-xr-x  1 root  wheel  36 27 апр 17:52 chain.pem@ -> ../../archive/kostikov.co/chain1.pem
lrwxr-xr-x  1 root  wheel  40 27 апр 17:52 fullchain.pem@ -> ../../archive/kostikov.co/fullchain1.pem
lrwxr-xr-x  1 root  wheel  42 27 апр 18:13 privandcert.pem@ -> ../../archive/kostikov.co/privandcert1.pem
lrwxr-xr-x  1 root  wheel  38 27 апр 17:52 privkey.pem@ -> ../../archive/kostikov.co/privkey1.pem

3. Подключаем полученные сертификаты к сайту

root@beta:~ # cat /usr/local/etc/lighttpd/lighttpd.conf
...
# --- path to Let's Encrypt cert storage
lepath = "/usr/local/etc/letsencrypt/live"

# -- SSL support
$SERVER["socket"] == ":443" {
        ssl.engine = "enable"
        ssl.ca-file = lepath + "/my.server/chain.pem"
        ssl.pemfile = lepath + "/my.server/privandcert.pem"
...
        $HTTP["host"] == "kostikov.co" {
                ssl.ca-file = lepath + "/kostikov.co/chain.pem"
                ssl.pemfile = lepath + "/kostikov.co/privandcert.pem"
        }
...
}
...

Обратите внимание, что поскольку на сервере размещён не один только сайт, то используется несколько наборов сертификатов. В качестве базового при начальном соединении используется SSL сертификат для домена my.server, а после посредством механизма TLS SNI предоставляется уже необходимый. Если вы размещаете только один сайт или используете единый для всех сертификат, вы можете обойтись без секции $HTTP["host"] прописав путь к вашему единственному сертификату по аналогии с my.server в вышеприведённом файле конфигурации.

4. Настраиваем механизм автоматического обновления сертификатов

Все сертификаты, выдаваемые Let's Encrypt имеют срок действия в 90 дней и это его второй относительный минус. Столь короткий срок, равно как и отсутствие возможности использования wildcard, обусловлены заботой о повышении уровня безопасности сертифицируемых доменов. Обновление всей базы полученых SSL сертификатов производится простой командой.

root@beta:~ # certbot renew

-------------------------------------------------------------------------------
Processing /usr/local/etc/letsencrypt/renewal/my.server.conf
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
Processing /usr/local/etc/letsencrypt/renewal/kostikov.co.conf
-------------------------------------------------------------------------------

The following certs are not due for renewal yet:
  /usr/local/etc/letsencrypt/live/my.server/fullchain.pem (skipped)
  /usr/local/etc/letsencrypt/live/kostikov.co/fullchain.pem (skipped)
No renewals were attempted.

Как видно, обновления не произошло, поскольку используются "свежие" сертификаты. Однако, если бы до срока их окончания оставалось 30 дней и меньше они были бы обновлены в автоматическом режиме. При этом индексный номер самих файлов сертификатов получил бы следующее по порядку значение.

Зная это, напишем простой shell-скрипт для автоматического обновления базы сертификатов по cron.

root@beta:~ # cd /usr/local/etc/letsencrypt/
root@beta:/usr/local/etc/letsencrypt # ll lerenew.sh
-rwxr-xr-x  1 root  wheel  929 27 апр 15:08 lerenew.sh*
root@beta:/usr/local/etc/letsencrypt # cat lerenew.sh
#!bin/sh

# Update "Let's Encrypt" certs and make cert and key joined file for Lighttpd
# (c)2016 by Max Kostikov http://kostikov.co e-mail: max@kostikov.co
#
# cat /etc/crontab | grep lerenew
# 0 0 * * 1 root /usr/local/etc/letsencrypt/lerenew.sh >/dev/null 2>&1

lepath="/usr/local/etc/letsencrypt"
joincrt="privandcert"                                   # joined file name

certbot renew -nv >> /var/log/letsencrypt/renew.log 2>&1

certs=`find ${lepath}/archive/*/cert*.pem -mtime -1d`

if [ "${certs}" ];
then
        for i in $certs;
        do
                dir=`dirname $i`                        # path to certs
                dom=`basename $dir`                     # domain name
                ind=`basename $i | tr -d '[:alpha:].'`  # current cert version
                cat ${dir}/privkey${ind}.pem $i > ${dir}/${joincrt}${ind}.pem
                cd ${lepath}/live/${dom}
                ln -fs ../../archive/${dom}/${joincrt}${ind}.pem ${joincrt}.pem
        done
        service lighttpd restart
fi

В данном случае при его выполнении будет произведена проверка сроков действия имеющихся в хранилище сертификатов и в случае их возраста старше 60 дней будет произведена их автоматическая замена. В последнем случает будет подготовлен и новый совместный файл публичной и закрытой части сертификата для Lighttpd и произведена его переинициализация.

Добавим задание в cron для еженедельной проверки и обновления выданных Let's encrypt сертификатов.

root@beta:~ # cat /etc/crontab | grep lerenew
0       0       *       *       1       root    /usr/local/etc/letsencrypt/lerenew.sh >/dev/null 2>&1

5. PROFIT!

Статья была полезной? Тогда прошу не стесняться и поддерживать деньгами через PayPal или Яндекс.Деньги.


dev  shell  FreeBSD  security  letsencrypt  lighttpd  SSL