Table of contents
General information
Link to GitHub. More details about the operation of the algorithm and the module can be found here.
The ACME protocol client is used to automatically obtain a security certificate for your site. Basically everyone uses Let's Encrypt to get a free certificate and auto-renewal. But there are other services, such as Zero SSL. It also supports the ACME protocol.
I relied on two articles from Habr (this and this), as well as RFC8555. But the information in them was not enough to implement their own version of the modulation. At least several times higher than several implementations of the module [at another level]. The tests were conducted on a live service, so there are no autotests yet. You can write and init pull request.
The module is written under Linux. Only the second version of the protocol is considered.
Installation
You can:
clone the repository:
git clone https://github.com/a1div0/acme-client.git
install the
acme-client
module usingtarantoolctl
:
tarantoolctl rocks install acme-client
Preparing for work
CSR
You must first submit a Certificate Signing Request - CSR. This file (let's call it csr.pem
) contains information about the future domain and organization. Namely, there are fields:
Domain name (CN) - for which the certificate is issued;
Organization (O) - the full name of the organization that owns the site;
Department (OU) - groups of organizations involved in the issuance of a certificate;
Country (C) - code of two characters corresponding to the organization's country (list);
State/Province (ST) and city (L) - the location of the organization;
e-mail (EMAIL) - mail for communication with the contact.
You can generate such a file using online generators, for example here and here. You can use OpenSSL. To do this, enter a command like:
openssl genrsa -out private.key 4096
openssl req -new -key private.key -out domain_name.csr -sha256
Next, you need to enter the above information and request. You should get a text file like this:
-----START CERTIFICATE REQUEST-----
MIICyDCCAbACAQAwgYIxCzAJBgNVBAYTALJVMSQwIgYDVQQIDBvQkNC70YLQsNC5
...
Mf5rbR8Ok/PfHohVHsOp85mAyTInt7a5H4PHVHb7U8j5aPhc4HarH+LcJhM=
-----END OF CERTIFICATE REQUEST-----
-----START PRIVATE KEY-----
MIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgeEAAoIBAQCttTORMQRaZYq2
...
QARm4Qu60qmM30MrhtCYOBk=
-----END PRIVATE KEY-----
There are plans to automate the process of creating a CSR, since it is practically possible to distribute a certificate with it, but it is better to create a new one each time.
Add and configure a module
See API.
API
local acmeClient = require('acme-client')
- acquire a library handleacmeClient.getCert(settings, proc)
- the procedure starts the mechanism for automatically obtaining a SSL-certificate
getCert
getCert(settings, yourChallengeSetupProc)
This procedure starts the process of automatically obtaining a certificate. Contains the settings
parameter, which is a table with fields:
dnsName
- required field, domain name with a certificatecertPath
- required field, full path to the folder with certificatescertName
- optional, default =cert.pem
, this is the name of the file, with which the certificate will be createdcsrName
- required field, the name of the certificate signing request file created earlier and placed in thecertPath
folderchallengeType
- optional, default =http-01
, this setting indicates what type of verification that you own the domain will be used. There are two options available:http01
anddns01
. The first type of verification confirms ownership, the impact of a GET request on a specific site address. The second type of check makes a DNS query. The second type of verification is required if a certificate for a domain name is encountered with all subdomains at once:*.domain.name
(wildcard certificates). More details can be found below in the article and here.acmeDirectoryUrl
- optional, default = "https://acme-v02.api.letsencrypt.org/directory", this is the path to the entry point of the ACME-server.
The second parameter is proc
- this is your procedure to make sure your server does the ACME check. Implementation depends on the type of validation:
If http-01
function yourProc(url, body)
-- your code --
end
The procedure will be called when the server response needs to be set. The server must listen on port 80
if we receive an SSL certificate for the first time. Or 443
if you have a valid SSL certificate. At the time of the call, the module will pass as parameters:
url
- the address to which the response should be set. It will be a line like/.well-known/acme-challenge/<token>
body
- the text to be returned when a GET-request arrives at the specified address. The procedure is called twice - once to set the response, the second time to cancel the installation. If body contains text, response code should be =200
. If body == nil, then response code should be404
.
If dns-01
function yourProc(key, value)
-- your code --
end
The procedure will be called when a DNS record of type TXT
needs to be set. At the time of the call, the module will pass the key name key
and its value value
, which must be recorded in DNS.
The procedure is called twice - once to set the entry, the second time to cancel the setting (nil will be passed in the value
parameter).
An example implementation of this type of validation is beyond the scope of this article.
An example of using the module
The example uses an external module - http.server.
local server = require("http.server").new("123.45.67.89", 80) -- 123.45.67.89 - server's internal ip, 80 - listening port number
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)
Possible problems
If there is a problem, pay attention to the limits of the service. For example, Let's Encrypt issues no more than 5 free certificates per domain per week. There are limits on the number of requests - during debugging on a live service, they are easy to exceed.