{"id":387298,"date":"2024-06-29T07:14:57","date_gmt":"2024-06-29T07:14:57","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=387298"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=387298","title":{"rendered":"<span>ACME-client for Tarantool<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h3>Table of contents<\/h3>\n<ul>\n<li>\n<p><a href=\"#general-information\" rel=\"noopener noreferrer nofollow\">General information<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#installation\" rel=\"noopener noreferrer nofollow\">Installation<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#preparing-for-work\" rel=\"noopener noreferrer nofollow\">Preparing for work<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#api\" rel=\"noopener noreferrer nofollow\">API<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#an-example-of-using-the-module\" rel=\"noopener noreferrer nofollow\">An example of using the module<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#possible-problems\" rel=\"noopener noreferrer nofollow\">Possible problems<\/a><\/p>\n<\/li>\n<\/ul>\n<h3>General information<\/h3>\n<p>Link to <a href=\"https:\/\/github.com\/a1div0\/acme-client\" rel=\"noopener noreferrer nofollow\">GitHub<\/a>. More details about the operation of the algorithm and the module can be found <a href=\"https:\/\/1div0.ru\/about-acme-client\/\" rel=\"noopener noreferrer nofollow\">here<\/a>.<\/p>\n<p>The ACME protocol client is used to automatically obtain a security certificate for your site. Basically everyone uses <a href=\"https:\/\/letsencrypt.org\/\" rel=\"noopener noreferrer nofollow\">Let&#8217;s Encrypt<\/a> to get a free certificate and auto-renewal. But there are other services, such as <a href=\"https:\/\/zerossl.com\/\" rel=\"noopener noreferrer nofollow\">Zero SSL<\/a>. It also supports the ACME protocol.<\/p>\n<p>I relied on two articles from Habr (<a href=\"https:\/\/habr.com\/ru\/company\/ispsystem\/blog\/354420\/%22this%22\" rel=\"noopener noreferrer nofollow\">this<\/a> and <a href=\"https:\/\/habr.com\/ru\/company\/ispsystem\/blog\/413429\/\" rel=\"noopener noreferrer nofollow\">this<\/a>), as well as <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc8555\" rel=\"noopener noreferrer nofollow\">RFC8555<\/a>. 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.<\/p>\n<p>The module is written under Linux. Only the second version of the protocol is considered.<\/p>\n<h3>Installation<\/h3>\n<p>You can:<\/p>\n<ul>\n<li>\n<p>clone the repository:<\/p>\n<\/li>\n<\/ul>\n<pre><code>git clone https:\/\/github.com\/a1div0\/acme-client.git<\/code><\/pre>\n<ul>\n<li>\n<p>install the <code>acme-client<\/code> module using <code>tarantoolctl<\/code>:<\/p>\n<\/li>\n<\/ul>\n<pre><code>tarantoolctl rocks install acme-client<\/code><\/pre>\n<h3>Preparing for work<\/h3>\n<h4>CSR<\/h4>\n<p>You must first submit a Certificate Signing Request &#8212; <a href=\"https:\/\/en.wikipedia.org\/wiki\/Certificate_signing_request\" rel=\"noopener noreferrer nofollow\">CSR<\/a>. This file (let&#8217;s call it <code>csr.pem<\/code>) contains information about the future domain and organization. Namely, there are fields:<\/p>\n<ol>\n<li>\n<p>Domain name (CN) &#8212; for which the certificate is issued;<\/p>\n<\/li>\n<li>\n<p>Organization (O) &#8212; the full name of the organization that owns the site;<\/p>\n<\/li>\n<li>\n<p>Department (OU) &#8212; groups of organizations involved in the issuance of a certificate;<\/p>\n<\/li>\n<li>\n<p>Country (C) &#8212; <a href=\"https:\/\/ru.wikipedia.org\/wiki\/ISO_3166-1_alpha-2\" rel=\"noopener noreferrer nofollow\">code<\/a> of two characters corresponding to the organization&#8217;s country (<a href=\"https:\/\/ru.wikipedia.org\/wiki\/ISO_3166-2\" rel=\"noopener noreferrer nofollow\">list<\/a>);<\/p>\n<\/li>\n<li>\n<p>State\/Province (ST) and city (L) &#8212; the location of the organization;<\/p>\n<\/li>\n<li>\n<p>e-mail (EMAIL) &#8212; mail for communication with the contact.<\/p>\n<\/li>\n<\/ol>\n<p>You can generate such a file using online generators, for example <a href=\"https:\/\/csrgenerator.com\/\" rel=\"noopener noreferrer nofollow\">here<\/a> and <a href=\"https:\/\/www.reg.ru\/ssl-certificate\/generate_key_and_csr\" rel=\"noopener noreferrer nofollow\">here<\/a>. You can use OpenSSL. To do this, enter a command like:<\/p>\n<pre><code>openssl genrsa -out private.key 4096 openssl req -new -key private.key -out domain_name.csr -sha256<\/code><\/pre>\n<p>Next, you need to enter the above information and request. You should get a text file like this:<\/p>\n<pre><code>-----START CERTIFICATE REQUEST----- MIICyDCCAbACAQAwgYIxCzAJBgNVBAYTALJVMSQwIgYDVQQIDBvQkNC70YLQsNC5 ... Mf5rbR8Ok\/PfHohVHsOp85mAyTInt7a5H4PHVHb7U8j5aPhc4HarH+LcJhM= -----END OF CERTIFICATE REQUEST-----  -----START PRIVATE KEY----- MIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgeEAAoIBAQCttTORMQRaZYq2 ... QARm4Qu60qmM30MrhtCYOBk= -----END PRIVATE KEY-----<\/code><\/pre>\n<p>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.<\/p>\n<h4>Add and configure a module<\/h4>\n<p>See <a href=\"#api\" rel=\"noopener noreferrer nofollow\">API<\/a>.<\/p>\n<h3>API<\/h3>\n<ul>\n<li>\n<p><code>local acmeClient = require('acme-client')<\/code> &#8212; acquire a library handle<\/p>\n<\/li>\n<li>\n<p><code>acmeClient.getCert(settings, proc)<\/code> &#8212; the procedure starts the mechanism for automatically obtaining a SSL-certificate<\/p>\n<\/li>\n<\/ul>\n<h4>getCert<\/h4>\n<pre><code>getCert(settings, yourChallengeSetupProc)<\/code><\/pre>\n<p>This procedure starts the process of automatically obtaining a certificate. Contains the <code>settings<\/code> parameter, which is a table with fields:<\/p>\n<ul>\n<li>\n<p><code>dnsName<\/code> &#8212; required field, domain name with a certificate<\/p>\n<\/li>\n<li>\n<p><code>certPath<\/code> &#8212; required field, full path to the folder with certificates<\/p>\n<\/li>\n<li>\n<p><code>certName<\/code> &#8212; optional, default = <code>cert.pem<\/code>, this is the name of the file, with which the certificate will be created<\/p>\n<\/li>\n<li>\n<p><code>csrName<\/code> &#8212; required field, the name of the certificate signing request file created earlier and placed in the <code>certPath<\/code> folder<\/p>\n<\/li>\n<li>\n<p><code>challengeType<\/code> &#8212; optional, default = <code>http-01<\/code>, this setting indicates what type of verification that you own the domain will be used. There are two options available: <code>http01<\/code> and <code>dns01<\/code>. 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: <code>*.domain.name<\/code> (wildcard certificates). More details can be found below in the article and <a href=\"https:\/\/letsencrypt.org\/en\/docs\/challenge-types\/\" rel=\"noopener noreferrer nofollow\">here<\/a>.<\/p>\n<\/li>\n<li>\n<p><code>acmeDirectoryUrl<\/code> &#8212; optional, default = &#171;<a href=\"https:\/\/acme-v02.api.letsencrypt.org\/directory\" rel=\"noopener noreferrer nofollow\">https:\/\/acme-v02.api.letsencrypt.org\/directory<\/a>&#171;, this is the path to the entry point of the ACME-server.<\/p>\n<\/li>\n<\/ul>\n<p>The second parameter is <code>proc<\/code> &#8212; this is your procedure to make sure your server does the ACME check. Implementation depends on the type of validation:<\/p>\n<p>If <code>http-01<\/code><\/p>\n<pre><code class=\"lua\">function yourProc(url, body)     -- your code -- end<\/code><\/pre>\n<p>The procedure will be called when the server response needs to be set. The server must listen on port <code>80<\/code> if we receive an SSL certificate for the first time. Or <code>443<\/code> if you have a valid SSL certificate. At the time of the call, the module will pass as parameters:<\/p>\n<ul>\n<li>\n<p><code>url<\/code> &#8212; the address to which the response should be set. It will be a line like <code>\/.well-known\/acme-challenge\/&lt;token><\/code><\/p>\n<\/li>\n<li>\n<p><code>body<\/code> &#8212; the text to be returned when a GET-request arrives at the specified address. The procedure is called twice &#8212; once to set the response, the second time to cancel the installation. If body contains text, response code should be = <code>200<\/code>. If body == nil, then response code should be <code>404<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p>If <code>dns-01<\/code><\/p>\n<pre><code class=\"lua\">function yourProc(key, value)     -- your code -- end<\/code><\/pre>\n<p>The procedure will be called when a DNS record of type <code>TXT<\/code> needs to be set. At the time of the call, the module will pass the key name <code>key<\/code> and its value <code>value<\/code>, which must be recorded in DNS.<br \/> The procedure is called twice &#8212; once to set the entry, the second time to cancel the setting (nil will be passed in the <code>value<\/code> parameter).<br \/> An example implementation of this type of validation is beyond the scope of this article.<\/p>\n<h3>An example of using the module<\/h3>\n<p>The example uses an external module &#8212; <a href=\"https:\/\/github.com\/tarantool\/http\" rel=\"noopener noreferrer nofollow\">http.server<\/a>.<\/p>\n<pre><code class=\"lua\">    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)<\/code><\/pre>\n<h3>Possible problems<\/h3>\n<p>If there is a problem, pay attention to the <a href=\"https:\/\/letsencrypt.org\/ru\/docs\/rate-limits\/\" rel=\"noopener noreferrer nofollow\">limits<\/a> of the service. For example, Let&#8217;s Encrypt issues no more than 5 free certificates per domain per week. There are limits on the number of requests &#8212; during debugging on a live service, they are easy to exceed.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/646899\/\"> https:\/\/habr.com\/ru\/articles\/646899\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h3>Table of contents<\/h3>\n<ul>\n<li>\n<p><a href=\"#general-information\" rel=\"noopener noreferrer nofollow\">General information<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#installation\" rel=\"noopener noreferrer nofollow\">Installation<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#preparing-for-work\" rel=\"noopener noreferrer nofollow\">Preparing for work<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#api\" rel=\"noopener noreferrer nofollow\">API<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#an-example-of-using-the-module\" rel=\"noopener noreferrer nofollow\">An example of using the module<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#possible-problems\" rel=\"noopener noreferrer nofollow\">Possible problems<\/a><\/p>\n<\/li>\n<\/ul>\n<h3>General information<\/h3>\n<p>Link to <a href=\"https:\/\/github.com\/a1div0\/acme-client\" rel=\"noopener noreferrer nofollow\">GitHub<\/a>. More details about the operation of the algorithm and the module can be found <a href=\"https:\/\/1div0.ru\/about-acme-client\/\" rel=\"noopener noreferrer nofollow\">here<\/a>.<\/p>\n<p>The ACME protocol client is used to automatically obtain a security certificate for your site. Basically everyone uses <a href=\"https:\/\/letsencrypt.org\/\" rel=\"noopener noreferrer nofollow\">Let&#8217;s Encrypt<\/a> to get a free certificate and auto-renewal. But there are other services, such as <a href=\"https:\/\/zerossl.com\/\" rel=\"noopener noreferrer nofollow\">Zero SSL<\/a>. It also supports the ACME protocol.<\/p>\n<p>I relied on two articles from Habr (<a href=\"https:\/\/habr.com\/ru\/company\/ispsystem\/blog\/354420\/%22this%22\" rel=\"noopener noreferrer nofollow\">this<\/a> and <a href=\"https:\/\/habr.com\/ru\/company\/ispsystem\/blog\/413429\/\" rel=\"noopener noreferrer nofollow\">this<\/a>), as well as <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc8555\" rel=\"noopener noreferrer nofollow\">RFC8555<\/a>. 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.<\/p>\n<p>The module is written under Linux. Only the second version of the protocol is considered.<\/p>\n<h3>Installation<\/h3>\n<p>You can:<\/p>\n<ul>\n<li>\n<p>clone the repository:<\/p>\n<\/li>\n<\/ul>\n<pre><code>git clone https:\/\/github.com\/a1div0\/acme-client.git<\/code><\/pre>\n<ul>\n<li>\n<p>install the <code>acme-client<\/code> module using <code>tarantoolctl<\/code>:<\/p>\n<\/li>\n<\/ul>\n<pre><code>tarantoolctl rocks install acme-client<\/code><\/pre>\n<h3>Preparing for work<\/h3>\n<h4>CSR<\/h4>\n<p>You must first submit a Certificate Signing Request &#8212; <a href=\"https:\/\/en.wikipedia.org\/wiki\/Certificate_signing_request\" rel=\"noopener noreferrer nofollow\">CSR<\/a>. This file (let&#8217;s call it <code>csr.pem<\/code>) contains information about the future domain and organization. Namely, there are fields:<\/p>\n<ol>\n<li>\n<p>Domain name (CN) &#8212; for which the certificate is issued;<\/p>\n<\/li>\n<li>\n<p>Organization (O) &#8212; the full name of the organization that owns the site;<\/p>\n<\/li>\n<li>\n<p>Department (OU) &#8212; groups of organizations involved in the issuance of a certificate;<\/p>\n<\/li>\n<li>\n<p>Country (C) &#8212; <a href=\"https:\/\/ru.wikipedia.org\/wiki\/ISO_3166-1_alpha-2\" rel=\"noopener noreferrer nofollow\">code<\/a> of two characters corresponding to the organization&#8217;s country (<a href=\"https:\/\/ru.wikipedia.org\/wiki\/ISO_3166-2\" rel=\"noopener noreferrer nofollow\">list<\/a>);<\/p>\n<\/li>\n<li>\n<p>State\/Province (ST) and city (L) &#8212; the location of the organization;<\/p>\n<\/li>\n<li>\n<p>e-mail (EMAIL) &#8212; mail for communication with the contact.<\/p>\n<\/li>\n<\/ol>\n<p>You can generate such a file using online generators, for example <a href=\"https:\/\/csrgenerator.com\/\" rel=\"noopener noreferrer nofollow\">here<\/a> and <a href=\"https:\/\/www.reg.ru\/ssl-certificate\/generate_key_and_csr\" rel=\"noopener noreferrer nofollow\">here<\/a>. You can use OpenSSL. To do this, enter a command like:<\/p>\n<pre><code>openssl genrsa -out private.key 4096 openssl req -new -key private.key -out domain_name.csr -sha256<\/code><\/pre>\n<p>Next, you need to enter the above information and request. You should get a text file like this:<\/p>\n<pre><code>-----START CERTIFICATE REQUEST----- MIICyDCCAbACAQAwgYIxCzAJBgNVBAYTALJVMSQwIgYDVQQIDBvQkNC70YLQsNC5 ... Mf5rbR8Ok\/PfHohVHsOp85mAyTInt7a5H4PHVHb7U8j5aPhc4HarH+LcJhM= -----END OF CERTIFICATE REQUEST-----  -----START PRIVATE KEY----- MIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgeEAAoIBAQCttTORMQRaZYq2 ... QARm4Qu60qmM30MrhtCYOBk= -----END PRIVATE KEY-----<\/code><\/pre>\n<p>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.<\/p>\n<h4>Add and configure a module<\/h4>\n<p>See <a href=\"#api\" rel=\"noopener noreferrer nofollow\">API<\/a>.<\/p>\n<h3>API<\/h3>\n<ul>\n<li>\n<p><code>local acmeClient = require('acme-client')<\/code> &#8212; acquire a library handle<\/p>\n<\/li>\n<li>\n<p><code>acmeClient.getCert(settings, proc)<\/code> &#8212; the procedure starts the mechanism for automatically obtaining a SSL-certificate<\/p>\n<\/li>\n<\/ul>\n<h4>getCert<\/h4>\n<pre><code>getCert(settings, yourChallengeSetupProc)<\/code><\/pre>\n<p>This procedure starts the process of automatically obtaining a certificate. Contains the <code>settings<\/code> parameter, which is a table with fields:<\/p>\n<ul>\n<li>\n<p><code>dnsName<\/code> &#8212; required field, domain name with a certificate<\/p>\n<\/li>\n<li>\n<p><code>certPath<\/code> &#8212; required field, full path to the folder with certificates<\/p>\n<\/li>\n<li>\n<p><code>certName<\/code> &#8212; optional, default = <code>cert.pem<\/code>, this is the name of the file, with which the certificate will be created<\/p>\n<\/li>\n<li>\n<p><code>csrName<\/code> &#8212; required field, the name of the certificate signing request file created earlier and placed in the <code>certPath<\/code> folder<\/p>\n<\/li>\n<li>\n<p><code>challengeType<\/code> &#8212; optional, default = <code>http-01<\/code>, this setting indicates what type of verification that you own the domain will be used. There are two options available: <code>http01<\/code> and <code>dns01<\/code>. 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: <code>*.domain.name<\/code> (wildcard certificates). More details can be found below in the article and <a href=\"https:\/\/letsencrypt.org\/en\/docs\/challenge-types\/\" rel=\"noopener noreferrer nofollow\">here<\/a>.<\/p>\n<\/li>\n<li>\n<p><code>acmeDirectoryUrl<\/code> &#8212; optional, default = &#171;<a href=\"https:\/\/acme-v02.api.letsencrypt.org\/directory\" rel=\"noopener noreferrer nofollow\">https:\/\/acme-v02.api.letsencrypt.org\/directory<\/a>&#171;, this is the path to the entry point of the ACME-server.<\/p>\n<\/li>\n<\/ul>\n<p>The second parameter is <code>proc<\/code> &#8212; this is your procedure to make sure your server does the ACME check. Implementation depends on the type of validation:<\/p>\n<p>If <code>http-01<\/code><\/p>\n<pre><code class=\"lua\">function yourProc(url, body)     -- your code -- end<\/code><\/pre>\n<p>The procedure will be called when the server response needs to be set. The server must listen on port <code>80<\/code> if we receive an SSL certificate for the first time. Or <code>443<\/code> if you have a valid SSL certificate. At the time of the call, the module will pass as parameters:<\/p>\n<ul>\n<li>\n<p><code>url<\/code> &#8212; the address to which the response should be set. It will be a line like <code>\/.well-known\/acme-challenge\/&lt;token><\/code><\/p>\n<\/li>\n<li>\n<p><code>body<\/code> &#8212; the text to be returned when a GET-request arrives at the specified address. The procedure is called twice &#8212; once to set the response, the second time to cancel the installation. If body contains text, response code should be = <code>200<\/code>. If body == nil, then response code should be <code>404<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p>If <code>dns-01<\/code><\/p>\n<pre><code class=\"lua\">function yourProc(key, value)     -- your code -- end<\/code><\/pre>\n<p>The procedure will be called when a DNS record of type <code>TXT<\/code> needs to be set. At the time of the call, the module will pass the key name <code>key<\/code> and its value <code>value<\/code>, which must be recorded in DNS.<br \/> The procedure is called twice &#8212; once to set the entry, the second time to cancel the setting (nil will be passed in the <code>value<\/code> parameter).<br \/> An example implementation of this type of validation is beyond the scope of this article.<\/p>\n<h3>An example of using the module<\/h3>\n<p>The example uses an external module &#8212; <a href=\"https:\/\/github.com\/tarantool\/http\" rel=\"noopener noreferrer nofollow\">http.server<\/a>.<\/p>\n<pre><code class=\"lua\">    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)<\/code><\/pre>\n<h3>Possible problems<\/h3>\n<p>If there is a problem, pay attention to the <a href=\"https:\/\/letsencrypt.org\/ru\/docs\/rate-limits\/\" rel=\"noopener noreferrer nofollow\">limits<\/a> of the service. For example, Let&#8217;s Encrypt issues no more than 5 free certificates per domain per week. There are limits on the number of requests &#8212; during debugging on a live service, they are easy to exceed.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/646899\/\"> https:\/\/habr.com\/ru\/articles\/646899\/<\/a><br \/><\/br><\/br><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-387298","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/387298","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=387298"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/387298\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=387298"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=387298"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=387298"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}