И снова здравствуй, дорогой читатель. Вторая глава о похождениях Let’s Encrypt в панели ISPmanager объявляется открытой. В предыдущей статье мы обсудили плагин для ACME v01. В этой поговорим о его эволюции с точки зрения логики работы с пользователем и, разумеется, о протоколе ACME v02 с поддержкой wildcard-сертификатов.

Стараясь окружить пользователя заботой, можно зайти далеко. Так далеко, что он вовсе не сможет работать с функциональностью. И первая часть нашей истории как раз об этом.
Разрабатывая модуль, мы хотели избавить клиента от долгой подготовки к выпуску сертификата. Для этого ввели два ограничения: разрешили заказывать SSL только на зарегистрированные в панели веб-домены и только на те псевдонимы веб-домена, о которых знает панель.
Оба ограничения казались логичными. Первое не давало заказывать сертификаты на несуществующие домены и плодить «мёртвые» сущности — сертификаты, которые не будут выпущены, ведь положить токены проверки некуда. Второе тоже избавляло от ненужных сущностей, а ещё не давало заказывать сертификаты на «*.»-псевдонимы — в то время LE такие сертификаты просто-напросто не поддерживал.
Все было хорошо до тех пор, пока однажды в LE не появилась фича проверки домена через DNS-записи и возможность заказа сертификата на почтовый домен. Тогда при заказе на почтовый домен мы решили добавлять к псевдонимам следующие: «mail», «pop», «smtp» — ведь чаще всего именно на них подключаются сертификаты. В итоге вышло нехорошо: нашлись пользователи, которые изначально настраивали свои почтовые сервера на совершенно другие псевдонимы. Из-за наших ограничений при заказе они не могли добавить нужные имена.
К счастью, мы быстро осознали и исправили ошибку, разрешив пользователям указывать нужные данные при заказе сертификата. Все-таки иногда заботы бывает слишком много :).
А теперь поговорим о переходе на ACME v02, ведь только в этой версии протокола LE появилась поддержка wildcard-сертификатов. Начнём с новой, вернее, изменённой directory:
Первое и самое очевидное отличие — ключи другие :). На мой взгляд, они стали намного интуитивнее. Второе отличие — отдельный URL для получения Replay-Nonce. Делается это теперь так:
Nonce нам, разумеется, ещё пригодится — и не раз.
Теперь поговорим о неочевидных изменениях, которые влечёт за собой переход на ACME v02.
На всякий случай напомню, как выглядел наш старый POST-запрос для общения с первой версией ACME:
Теперь же общая структура данных будет другой:
Как видите, поле «header» упразднено. Подготовительный этап, к вящей радости «любителей» криптографии вроде меня, совершенно не изменился: нам потребуются все те же rsa-ключи, JWK и JWS (подробнее об этом в первой части).
Для регистрации пользователя надо только принять пользовательское соглашение и отправить запрос на «newAccount» из directory.
И составить правильный protected:
Формируем тело запроса, отправляем и… никуда не торопимся! Аккуратно и внимательно обрабатываем хедеры ответа от ACME. Находим хедер под названием Location и сохраняем его содержимое. Это так называемый KID — ключ идентификации только что зарегистрированного пользователя. Все последующие запросы должны будут содержать в protected это значение вместо JWK. Будьте внимательны: если продолжить отправлять запросы по старой схеме, ответом будут только сообщения об ошибке.
Вот наш последующий protected:
Готовимся к отправке запроса на directory [«newOrder»]. Добавляем в payload все псевдонимы нашего веб-домена, на которые собираемся выпускать сертификат:
Учитывайте, что если вы хотите выпускать wildcard-сертификат, то в именах должно быть только основное имя и «*.»-псевдоним. Наличие любых других имен приведёт к ошибке выпуска.
В ответе мы получим JSON, содержащий способы подтверждения владения доменом и URL, который будет использоваться для завершения выпуска сертификата.
Далее получаем подробные инструкции по проверкам:
Процесс подтверждения совершенно не отличается от того, что был реализован для ACME v01. Обратите внимание: для wildcard-сертификата обязательно нужно выбирать подтверждение «dns-01».
После процедуры подтверждения остаётся вызвать URL Finalize. Возможны незначительные задержки, поэтому GET-запрос на этот адрес с��оит делать до тех пор, пока в ответе не получим следующее:
Сертификат уже будет содержать цепочку, поэтому он полностью готов к работе.
По сравнению с первой, вторая версия ACME стала намного удобнее и понятнее. Писать интеграцию стало еще проще, учитывая, что сама «криптография» не изменилась. Я буду с интересом наблюдать за развитием этого потрясающего инструмента и непременно вернусь сюда со свежей информацией, если произойдут какие-то важные и полезные изменения.

Чрезмерная забота
Стараясь окружить пользователя заботой, можно зайти далеко. Так далеко, что он вовсе не сможет работать с функциональностью. И первая часть нашей истории как раз об этом.
Разрабатывая модуль, мы хотели избавить клиента от долгой подготовки к выпуску сертификата. Для этого ввели два ограничения: разрешили заказывать SSL только на зарегистрированные в панели веб-домены и только на те псевдонимы веб-домена, о которых знает панель.
Оба ограничения казались логичными. Первое не давало заказывать сертификаты на несуществующие домены и плодить «мёртвые» сущности — сертификаты, которые не будут выпущены, ведь положить токены проверки некуда. Второе тоже избавляло от ненужных сущностей, а ещё не давало заказывать сертификаты на «*.»-псевдонимы — в то время LE такие сертификаты просто-напросто не поддерживал.
Все было хорошо до тех пор, пока однажды в LE не появилась фича проверки домена через DNS-записи и возможность заказа сертификата на почтовый домен. Тогда при заказе на почтовый домен мы решили добавлять к псевдонимам следующие: «mail», «pop», «smtp» — ведь чаще всего именно на них подключаются сертификаты. В итоге вышло нехорошо: нашлись пользователи, которые изначально настраивали свои почтовые сервера на совершенно другие псевдонимы. Из-за наших ограничений при заказе они не могли добавить нужные имена.
К счастью, мы быстро осознали и исправили ошибку, разрешив пользователям указывать нужные данные при заказе сертификата. Все-таки иногда заботы бывает слишком много :).
Wildcard
А теперь поговорим о переходе на ACME v02, ведь только в этой версии протокола LE появилась поддержка wildcard-сертификатов. Начнём с новой, вернее, изменённой directory:
curl -o- 'https://acme-v02.api.letsencrypt.org/directory' { "keyChange": "https://acme-v02.api.letsencrypt.org/acme/key-change", "mIU2Y2m2FsA": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417", "meta": { "caaIdentities": [ "letsencrypt.org" ], "termsOfService": "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf", "website": "https://letsencrypt.org" }, "newAccount": "https://acme-v02.api.letsencrypt.org/acme/new-acct", "newNonce": "https://acme-v02.api.letsencrypt.org/acme/new-nonce", "newOrder": "https://acme-v02.api.letsencrypt.org/acme/new-order", "revokeCert": "https://acme-v02.api.letsencrypt.org/acme/revoke-cert" }
Первое и самое очевидное отличие — ключи другие :). На мой взгляд, они стали намного интуитивнее. Второе отличие — отдельный URL для получения Replay-Nonce. Делается это теперь так:
curl -LD - 'https://acme-v02.api.letsencrypt.org/acme/new-nonce' HTTP/1.1 204 No Content Server: nginx Replay-Nonce: QQgdAERh1MLQ6LHC0SVmB9OJXBcEWnwGB53CP0V4JlQ X-Frame-Options: DENY Strict-Transport-Security: max-age=604800 Expires: Sat, 02 Jun 2018 09:49:47 GMT Cache-Control: max-age=0, no-cache, no-store Pragma: no-cache Date: Sat, 02 Jun 2018 09:49:47 GMT Connection: keep-alive
Nonce нам, разумеется, ещё пригодится — и не раз.
Теперь поговорим о неочевидных изменениях, которые влечёт за собой переход на ACME v02.
На всякий случай напомню, как выглядел наш старый POST-запрос для общения с первой версией ACME:
{ "header": jws, // JSON Web Signature "protected": Base64Url(jws + Replay-Nonce), // Nonce — защита от повторов "payload": Base64Url(payload), // Запрос "signature": Base64Url(sign(protected.payload, private.pem)) // Подпись }
Теперь же общая структура данных будет другой:
{ "protected": Base64Url(protected), "payload": Base64Url(payload), //Запрос "signature": Base64Url(sign(protected.payload, private.pem)) }
Как видите, поле «header» упразднено. Подготовительный этап, к вящей радости «любителей» криптографии вроде меня, совершенно не изменился: нам потребуются все те же rsa-ключи, JWK и JWS (подробнее об этом в первой части).
Регистрация
Для регистрации пользователя надо только принять пользовательское соглашение и отправить запрос на «newAccount» из directory.
payload = {"termsOfServiceAgreed": true}
И составить правильный protected:
{ "alg" : "RS256", "jwk" : jwk, \\ JSON Web Key “url” : url, \\ на который мы будем отправлять запрос “nonce” : Replay-Nonce \\ защита от повторов }
Формируем тело запроса, отправляем и… никуда не торопимся! Аккуратно и внимательно обрабатываем хедеры ответа от ACME. Находим хедер под названием Location и сохраняем его содержимое. Это так называемый KID — ключ идентификации только что зарегистрированного пользователя. Все последующие запросы должны будут содержать в protected это значение вместо JWK. Будьте внимательны: если продолжить отправлять запросы по старой схеме, ответом будут только сообщения об ошибке.
Вот наш последующий protected:
{ "alg" : "RS256", "kid" : kid, \\ ключ идентификации пользователя “url” : url, \\ адрес на который мы будем отправлять запрос “nonce” : Replay-Nonce \\ защита от повторов }
Заказ сертификата
Готовимся к отправке запроса на directory [«newOrder»]. Добавляем в payload все псевдонимы нашего веб-домена, на которые собираемся выпускать сертификат:
payload ={ "identifiers":[ { "type":"dns", "value":"name1" }, ... { "type":"dns", "value":"nameN" } ] }
Учитывайте, что если вы хотите выпускать wildcard-сертификат, то в именах должно быть только основное имя и «*.»-псевдоним. Наличие любых других имен приведёт к ошибке выпуска.
В ответе мы получим JSON, содержащий способы подтверждения владения доменом и URL, который будет использоваться для завершения выпуска сертификата.
{ "status":"pending", "expires":"2018-06-08T08:05:49.437251947Z", "identifiers":[ { "type":"dns", "value":"name1" }, { "type":"dns", "value":"www.name1" } ], "authorizations":[ // подтверждение владения доменом "https://acme-v02.api.letsencrypt.org/acme/authz/Xp0a_...", "https://acme-v02.api.letsencrypt.org/acme/authz/o3Bvy..." ], "finalize":"https://acme-v02.api.letsencrypt.org/acme/finalize/..." // завершение выпуска }
Далее получаем подробные инструкции по проверкам:
curl -o- 'https://acme-v02.api.letsencrypt.org/acme/authz/Xp0a_...' { "identifier":{ "type":"dns", "value":"name1" }, "status":"pending", "expires":"2018-06-08T08:05:49Z", "challenges":[ { "type":"http-01", "status":"pending", "url":"https://acme-v02.api.letsencrypt.org/acme/challenge/Xp0a_.../4906756205", "token":"Me_cKM2Stu3iyCJQWEssho8Kj2nvRKuSJvIPF5tRyko" }, { "type":"dns-01", "status":"pending", "url":"https://acme-v02.api.letsencrypt.org/acme/challenge/Xp0a_.../4906756206", "token":"p-0xyySPQClTXVlgTxwJUvVOQtdHmNPpFht95bWrq8s" } ] }
Процесс подтверждения совершенно не отличается от того, что был реализован для ACME v01. Обратите внимание: для wildcard-сертификата обязательно нужно выбирать подтверждение «dns-01».
Получение сертификата
После процедуры подтверждения остаётся вызвать URL Finalize. Возможны незначительные задержки, поэтому GET-запрос на этот адрес с��оит делать до тех пор, пока в ответе не получим следующее:
{ "status": "valid", ///< сертификат успешно выпущен "expires": "2018-06-11T10:39:24Z", "identifiers": [ { "type": "dns", "value": "name1" }, { "type": "dns", "value": "name2" } ], "authorizations": [ "https://acme-v02.api.letsencrypt.org/acme/authz/Xp0a_...", "https://acme-v02.api.letsencrypt.org/acme/authz/o3Bvy..." ], "finalize": "https://acme-v02.api.letsencrypt.org/acme/finalize/...", "certificate": "https://acme-v02.api.letsencrypt.org/acme/cert/..." ///< сертификат }
Сертификат уже будет содержать цепочку, поэтому он полностью готов к работе.
По сравнению с первой, вторая версия ACME стала намного удобнее и понятнее. Писать интеграцию стало еще проще, учитывая, что сама «криптография» не изменилась. Я буду с интересом наблюдать за развитием этого потрясающего инструмента и непременно вернусь сюда со свежей информацией, если произойдут какие-то важные и полезные изменения.
