Содержание
Общая информация
Ссылка на GitHub. Подробнее о работе алгоритма и модуля можно посмотреть здесь.
Клиент ACME-протокола используется для автоматического получения сертификата безопасности для вашего сайта. Для бесплатного получения сертификата и автоматического его продления в основном все используют Let’s Encrypt. Но и есть другие сервисы, например Zero SSL. Он тоже поддерживает ACME-протокол.
Я опирался на две статьи с Хабра (эту и эту), а также RFC8555. Но информации в них оказазалось недостаточно, для того, чтобы реализовать собственный вариант модуля. Примерно половину нужной информации потребовалось дополнительно извлечь из нескольких реализаций данного модуля на других языках. Тесты проводил на живом сервисе, поэтому автотестов пока нет. Можете написать и сделать пулл реквест.
Модуль написан под Linux. В статье подробно разобран алгоритм работы — при необходимости Вы можете дописать его под другую ОС. Рассматривается только вторая версия протокола.
В модуле есть что улучшать, поэтому ниже я подробно описываю как всё работает. Можете сделать коммит.
Установка
Доступны следующие способы:
- клонировать репозиторий:
git clone https://github.com/a1div0/acme-client.git
- установить через
tarantoolctl
:tarantoolctl rocks install acme-client
Подготовка к работе
CSR
Предварительно необходимо сформировать запрос на подпись сертификата (Certificate Signing Request) — CSR. Этот файл (назовём его csr.pem
) содержит информацию о вашем домене и организации. Необходимо заполнить следующие поля:
- Доменное имя (CN) — на которое выпускается сертификат;
- Организация (O) — полное имя организации, которой принадлежит сайт;
- Отдел (OU) — подразделение организации, которое занимается выпуском сертификата;
- Страна (C) — код из двух символов, соответствующий стране организации (список);
- Штат/Область (ST) и город (L) — местонахождение организации;
- email (EMAIL) — почта для связи с организацией.
Сгенерировать такой файл можно с помощью онлайн-генераторов, например вот и вот. Можно с помощью OpenSSL. Для этого необходимо ввести команды вида:
openssl genrsa -out private.key 4096
openssl req -new -key private.key -out domain_name.csr -sha256
Далее необходимо ввести указанную выше информацию и запрос готов. Должен получиться текстовый файлик такого вида:
-----BEGIN CERTIFICATE REQUEST-----
MIICyDCCAbACAQAwgYIxCzAJBgNVBAYTAlJVMSQwIgYDVQQIDBvQkNC70YLQsNC5
. . .
Mf5rbR8Ok/PfHohVHsOp85mAyTInt7a5H4PHVHb7U8j5aPhc4HarH+LcJhM=
-----END CERTIFICATE REQUEST-----
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCttTORMQRaZYq2
. . .
QARm4Qu60qmM30MrhtCYOBk=
-----END PRIVATE KEY-----
В планах есть автоматизировать процесс создания CSR, так как практически можно продлять сертификат им же, но лучше создавать каждый раз новый.
Добавить и настроить модуль
См. API.
API
local acmeClient = require('acme-client')
— получить дескриптор библиотекиacmeClient.getCert(settings, proc)
— процедура запускает механизм автоматического получения SSL-сертификата
getCert
getCert(settings, yourChallengeSetupProc)
Эта процедура запускает процесс автоматического получения сертификата. Содержит параметр settings
, представляющий собой таблицу с полями:
dnsName
— обязательное поле, доменное имя с сертификатомcertPath
— обязательное поле, полный путь к папке с сертификатамиcertName
— необязательный, по умолчанию =cert.pem
, это имя файла, с которым будет создан сертификатcsrName
— обязательное поле, имя файла запроса на подпись сертификата, созданного ранее и помещенного в папкуcertPath
challengeType
— необязательный, по умолчанию =http-01
, этот параметр указывает, какой тип подтверждения владения доменом будет использован. Доступно два варианта:http01
иdns01
. Первый тип проверки подтверждает право собственности, посылая GET-запрос на конкретный адрес сайта. Второй тип проверки делает DNS-запрос. Второй тип проверки требуется, если сертификат получаем сразу на все поддомены:*.domain.name
(wildcard-сертификаты). Более подробную информацию можно найти ниже в статье и здесь.acmeDirectoryUrl
— необязательный, по умолчанию = "https://acme-v02.api.letsencrypt.org/directory", это путь к точке входа ACME-сервера.
Второй параметр — proc
— это ваш обработчик установки проверки ACME. Реализация зависит от типа проверки:
Если http-01
function yourProc(url, body)
-- your code --
end
Процедура будет вызываться, когда необходимо установить ответ сервера. Сервер должен прослушивать порт 80
, если мы получаем сертификат SSL в первый раз. Или 443
, если у вас есть действующий сертификат SSL. В момент вызова модуль будет передавать в качестве параметров:
url
— адрес, на который должен быть установлен ответ. Это будет строка типа/.well-known/acme-challenge/<token>
body
— текст, который будет возвращен при поступлении GET-запроса на указанный адрес.
Процедура вызывается дважды — один раз для установки ответа, второй раз для отмены установки. Если тело содержит текст, код ответа должен быть = 200
. Если body == nil, то код ответа должен быть 404
.
Если тип проверки dns-01
function yourProc(key, value)
-- your code --
end
Процедура будет вызываться, когда необходимо установить запись DNS типа TXT
. В момент вызова модуль будет передавать имя ключа key
и его значение value
, которые должны быть записаны в DNS.
Процедура вызывается дважды — один раз для установки записи, второй раз для отмены установки (в параметре value
будет передано nil
).
Пример реализации этого типа проверки выходит за рамки этой статьи.
Пример использования модуля
В примере используется внешний модуль — http.server.
local server = require("http.server").new(nil, 80)
local acmeClient = require("acme-client")
local acmeSettings = {
acmeDirectoryUrl = 'https://acme-v02.api.letsencrypt.org/directory' -- ACME-service
,dnsName = 'mysite.com'
,certPath = '/home/my/projects/project123/cert/'
,certName = 'certificate.pem'
,csrName = 'csr.pem'
,challengeType = 'http-01'
}
local function myChallengeSetup(url, body)
local proc = nil
if body ~= nil then
proc = function (request)
return request:render{status = 200, text = body}
end
else
proc = function (request)
return request:render{status = 404}
end
end
server:route({ path = url }, proc)
end
acmeClient.getCert(settings, myChallengeSetup)
Возможные проблемы
Если есть проблема — обратите внимание на лимиты сервиса. Например, Let’s Encrypt выдаёт не более 5 бесплатных сертификатов на домен в неделю. Есть ограничения по количеству запросов — во время отладки на живом сервисе их легко превысить.