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 module. Implementations of the module in other languages were analyzed. 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. Used ACME version 2 (version 1 - not realized).
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
local acmeLib = require('acme-client')
- acquire a library handlelocal acmeClient = acmeLib.new(options, proc)
- create new acme-objectlocal time = acmeLib.certValidTo(certName)
- returns the expiration date of the certificateacmeClient:getCert()
- the procedure starts the mechanism for automatically obtaining a SSL-certificateacmeClient:validTo()
- returns the expiration date of the certificate
new(options, yourChallengeSetupProc)
This procedure create a new acme-client object with the specified parameters.
The options
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 createdtempRsaPrivateKeyName
- optional, default =privacc.pem
, is name temporary file with private rsa keytempCsrConfName
- optional, default =csr.cnf
, is name temporary file with configuration parameters for CSRtempCsrName
- optional, default =csr.pem
, is name temporary file with CSRchallengeType
- 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.organization
- optional, if the level of the certificate allows, you can add the name of your organization to itorganizationUnit
- optional, if the level of the certificate allows, you can add the name of your organization unit to itcountry
- optional, if the level of the certificate allows, you can add the name of your country to it. It must be a two letter code.state
- optional, if the level of the certificate allows, you can add the name of your state or region to itcity
- optional, if the level of the certificate allows, you can add the name of your city to itemail
- optional, if the level of the certificate allows, you can add email to contact you
The level of the certificate is determined by the service used.
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.
This procedure starts the process of automatically obtaining a certificate.
Gets the expiration date of the certificate specified since certName
in the
current time zone.
The example uses an external module - http.server.
local acmeSettings = {
dnsName = 'mysite.com'
,certPath = '/home/my/projects/project123/cert/'
,certName = 'certificate.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
local acmeClient = require("acme-client").new(settings, myChallengeSetup)
local server = require("http.server").new(nil, 80)
acmeClient:getCert()
local validTo = acmeClient:validTo()
print(os.date("%Y.%m.%d %H:%M:%S", validTo))
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.