Модуль ACME-клиента для Tarantool

Содержание

Общая информация

Ссылка на 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) содержит информацию о вашем домене и организации. Необходимо заполнить следующие поля:

  1. Доменное имя (CN) — на которое выпускается сертификат;
  2. Организация (O) — полное имя организации, которой принадлежит сайт;
  3. Отдел (OU) — подразделение организации, которое занимается выпуском сертификата;
  4. Страна (C) — код из двух символов, соответствующий стране организации (список);
  5. Штат/Область (ST) и город (L) — местонахождение организации;
  6. 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 бесплатных сертификатов на домен в неделю. Есть ограничения по количеству запросов — во время отладки на живом сервисе их легко превысить.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *