Достаточно часто нам приходится работать с SSL сертификатами. Давайте вспомним процесс создания и установки сертификата (в общем случае для большинства).
- Найти провайдера (сайт на котором мы можем купить SSL).
- Сгенерировать CSR.
- Отправить его провайдеру.
- Подтвердить владение доменом.
- Получить сертификат.
- Преобразовать сертификат в нужную форму (опционально). Например, из pem в PKCS #12.
- Установить сертификат на веб сервер.
Относительно быстро, не сложно и понятно. Этот вариант вполне годится, если у нас есть максимум десяток проектов. А если их больше, и у них минимум по три окружения? Классический dev — staging — production. В этом случае стоит задуматься об автоматизации этого процесса. Предлагаю, немного углубиться в проблему и найти решение которое в дальнейшем минимизирует временные затраты на создание и обслуживание сертификатов. В статье будет присутствовать анализ проблемы и небольшое руководство к повторению.
Заранее оговорюсь: основная специализация нашей компании — .net, а соответственно IIS и прочие виндовые вытекающие. Поэтому, ACME клиент и все действия для него тоже будут описаны с точки зрения использования windows.
Для кого это актуально и некоторые исходные данные
Компания К в лице автора. URL (для примера): company.tld
Проект X — один из наших проектов, занимаясь которым я пришел к выводу что все таки нужно двигаться в сторону максимальной экономии времени при работе с сертификатами. У этого проекта есть четыре окружения: dev, test, staging и production. Dev и test находятся на нашей стороне, staging и production на клиентской.
Особенностью проекта является то, что у него есть большое количество модулей, которые доступны как поддомены.
То-есть, имеем следующую картину:
Dev | Test | Staging | Production |
---|---|---|---|
projectX.dev.company.tld | projectX.test.company.tld | staging.projectX.tld | projectX.tld |
module1.projectX.dev.company.tld | module1.projectX.test.company.tld | module1.staging.projectX.tld | module1.projectX.tld |
module2.projectX.dev.company.tld | module2.projectX.test.company.tld | module2.staging.projectX.tld | module2.projectX.tld |
... | ... | ... | ... |
moduleN.projectX.dev.company.tld | moduleN.projectX.test.company.tld | moduleN.staging.projectX.tld | moduleN.projectX.tld |
Для production используется купленный wildcard сертификат, тут вопросов не возникает. Но он покрывает только первый уровень поддомена. Соответственно, если есть сертификат для *.projectX.tld — то для staging.projectX.tld он работать будет, а вот для module1.staging.projectX.tld уже нет. А покупать отдельный как-то не хочется.
И это только на примере одного проекта одной компании. А проект, естественно, не один.
Общие для всех причины заняться решением этого вопроса выглядят примерно так:
- Относительно недавно Google предложили уменьшить максимальный срок действия SSL сертификатов. Со всеми вытекающими.
- Облегчить процесс выпуска и обслуживания SSL для внутренних нужд проектов и компании в целом.
- Централизованное хранение записей о сертификатах, которое частично решает проблему подтверждения домена с помощью DNS и последующего автоматического обновления, а также решает вопрос доверия клиента. Все же, больше доверия вызывает CNAME на сервер компании партнера/исполнителя, чем на сторонний ресурс.
- Ну, и наконец, в этом случае фраза “лучше иметь чем не иметь” подходит отлично.
Выбор провайдера SSL и подготовительные шаги
Из доступных вариантов бесплатных SSL сертификатов рассматривались cloudflare и letsencrypt. DNS для этого (и некоторых других проектов) как располагается на cloudflare, но я не сторонник использования их сертификатов. Поэтому, было решено использовать letsencrypt.
Для создания wildcard SSL сертификата нужно подтвердить владение доменом. Эта процедура предполагает создание некоторой DNS записи (TXT или CNAME), с последующей ее проверкой при выпуске сертификата. В Linux есть утилита — certbot, которая позволяет частично (или полностью для некоторых DNS провайдеров) автоматизировать этот процесс. Для Windows же из найденных и проверенных вариантов ACME клиентов я остановился на WinACME.
А запись для домена создана, переходим к созданию сертификата:
Нас интересует последний вывод, а именно — доступные варианты подтверждения владения доменом для выпуска wildcard сертификата:
- Создание DNS записей вручную (автоматическое обновление не поддерживается)
- Создание DNS записей с помощью acme-dns сервера (подробнее можно почитать тут.
- Создание DNS записей с помощью собственного скрипта (аналог плагина cloudflare для certbot).
На первый взгляд, третий пункт вполне подходит, но если DNS провайдер не поддерживает этот функционал? А нам необходим общий случай. А общий случай — это CNAME записи, уж их то все поддерживают. Следовательно, останавливаемся на пункте 2, и идем настраивать свой ACME-DNS сервер.
Настройка ACME-DNS сервера и процесс выпуска сертификата
Для примера, я создал домен 2nd.pp.ua, и в дальнейшем буду использовать его.
Обязательным требованием для корректной работы сервера является создание NS и А записи для его домена. И первый неприятный момент с которым я столкнулся — cloudflare (по крайней мере в режиме бесплатного использования) не позволяет одновременно создать NS и A запись для одного и того же хоста. Не то чтобы это было проблемой, но в bind это можно. Саппорт ответил, что их панель так делать не позволяет. Не беда, создадим две записи:
acmens.2nd.pp.ua. IN A 35.237.128.147
acme.2nd.pp.ua. IN NS acmens.2nd.pp.ua.
На данном этапе у нас должен резолвиться хост acmens.2nd.pp.ua
.
$ ping acmens.2nd.pp.ua
PING acmens.2nd.pp.ua (35.237.128.147) 56(84) bytes of data
А вот acme.2nd.pp.ua
резолвиться не будет, так как DNS сервер который его обслуживает еще не запущен.
Записи созданы, переходим к настройке и запуску ACME-DNS сервера. Жить он у меня будет на ubuntu server в docker контейнере, но запустить его можно везде где есть golang. Windows тоже вполне подойдет, но я все же предпочитаю Linux сервер.
Создаем необходимые директории и файлы:
$ mkdir config
$ mkdir data
$ touch config/config.cfg
Воспользуемся vim вашим любимым текстовым редактором и вставим в config.cfg образец конфигурации.
Для успешной работы достаточно подправить секции general и api:
[general]
listen = "0.0.0.0:53"
protocol = "both"
domain = "acme.2nd.pp.ua"
nsname = "acmens.2nd.pp.ua"
nsadmin = "admin.2nd.pp.ua"
records =
"acme.2nd.pp.ua. A 35.237.128.147",
"acme.2nd.pp.ua. NS acmens.2nd.pp.ua.", ]
...
[api]
...
tls = "letsencrypt"
…
Также, по желанию, создадим docker-compose файл в основной директории сервиса:
version: '3.7'
services:
acmedns:
image: joohoi/acme-dns:latest
ports:
- "443:443"
- "53:53"
- "53:53/udp"
- "80:80"
volumes:
- ./config:/etc/acme-dns:ro
- ./data:/var/lib/acme-dns
Готово. Можно запускать.
$ docker-compose up -d
На данном этапе должен начать резолвиться хост acme.2nd.pp.ua
, и появиться 404 на https://acme.2nd.pp.ua
$ ping acme.2nd.pp.ua
PING acme.2nd.pp.ua (35.237.128.147) 56(84) bytes of data.
$ curl https://acme.2nd.pp.ua
404 page not found
Если этого не появилось — docker logs -f <container_name>
в помощь, благо, логи вполне читабельные.
Можем приступать к созданию сертификата. Открываем powershell от имени администратора и запускаем winacme. Нас интересуют выборы:
- M: Create new certificate (full options)
- 2: Manual input
- 2: [dns-01] Create verification records with acme-dns (https://github.com/joohoi/acme-dns)
- На вопрос о ссылке на ACME-DNS сервер вводим в ответ URL созданного сервера (https). URL of the acme-dns server: https://acme.2nd.pp.ua
В отвер клиент выдает запись которую надо добавить в существующий DNS сервер (процедура единоразовая):
[INFO] Creating new acme-dns registration for domain 1nd.pp.ua
Domain: 1nd.pp.ua
Record: _acme-challenge.1nd.pp.ua
Type: CNAME
Content: c82a88a5-499f-464f-96e4-be7f606a3b47.acme.2nd.pp.ua.
Note: Some DNS control panels add the final dot automatically.
Only one is required.
Создаем необходимую запись, и убеждаемся что она создалась корректно:
$ dig CNAME _acme-challenge.1nd.pp.ua +short
c82a88a5-499f-464f-96e4-be7f606a3b47.acme.2nd.pp.ua.
Подтверждаем что мы создали нужную запись в winacme, и продолжаем процесс создания сертификата:
Как использовать certbot в качестве клиента описано тут.
На этом процесс создания сертификата закончен, можно устанавливать его на веб-сервер и использовать. Если при создании сертификата создать также и задание в планировщике, то в дальнейшем процесс обновления сертификата будет происходить автоматически.