Содержание
Общая информация
Установка
Подготовка к работе
API
Пример использования модуля
Возможные проблемы
Общая информация
Ссылка на 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 бесплатных сертификатов на домен в неделю. Есть ограничения по количеству запросов - во время отладки на живом сервисе их легко превысить.
alexandrim
А хорошо ли терминировать tls на однопоточном сервере приложений?
В каком случае это допустимо, на ваш взгляд?
1div0 Автор
Я не понял, что Вы спросили ). Можете спросить то же самое другими словами пожалуйста?
alexandrim
Почему на стоит предпочесть классический вариант с proxy описанному выше?
1div0 Автор
На мой взгляд это зависит от задачи. Если у меня небольшой проект - всё, что я хочу сделать - это запустить ровно одну команду на сервере. И получать команды снаружи я хочу ровно в том виде, как мне удобно. Я не хочу настраивать nginx и всё остальное прочее.
Надеюсь я правильно понял вопрос.
alexandrim
Да, вопрос об этом и ответ понятен, спасибо.
Но возникает другой вопрос.
На каком небольшом проекте может понадобиться Tarantool?
Или это не вопрос надобности,а вопрос предпочтений?
1div0 Автор
Нельзя сказать, что Tarantool прям необходим на небольшом проекте - серверную часть можно написать на чём угодно.
Вот Вы говорите "классический вариант", а я не знаю точно, что Вы имеете ввиду. Есть где-то руководство, где написано, как делать классический вариант? Чем классический вариант лучше или предпочтительнее остальных?
Тем не менее, Tarantool удобен - что бы реализовать какой-то концепт не нужно много кода. Он даёт большой потенциал роста - если проект вырастет, будет несложно его масштабировать. Впоследствии, можно будет докрутить и тот же nginx (если он понадобится) и всё что нужно ещё.
Отвечая на вопрос - выбор инструмента это всегда вопрос предпочтений, но если небольшой проект может вырасти, то имеет смысл написать его, используя Tarantool.
alexandrim
Спору нет. Хотелось понять мотивацию. Ответ понятен. Благодарю!