publishlab / node-acme-client Goto Github PK
View Code? Open in Web Editor NEWSimple and unopinionated ACME client for Node.js
License: MIT License
Simple and unopinionated ACME client for Node.js
License: MIT License
Thanks for the library.
I've been working on acme-middleware as a middleware for expressjs.
Today, I came back to node-acme-client
and found out it is now updated to version 4.0.0
.
Do you maintain a changelog somewhere?
That'd help to know if I have to update to the latest version, and what need to be updated.
Thank you!
Issue has been abandoned
Not a core part of the ACME protocol: Unnecessary to have it in an ACME client
Please can a new function be implemented called ACME.verify()
.
It will have the following properties:
certificate
(string
):
return
value:
true
is returned.false
is returned.certificate
:
''
(throws an error: no certificate specified).Possible Implementation
const fs = require('fs');
const forge = require('./crypto/forge');
module.exports = async function(certificate) {
if (certificate) {
if (fs.existsSync(certificate)) {
let cert = fs.readFileSync(certificate);
let expiry = (new Date((await forge.readCertificateInfo(cert)).notAfter)).valueOf();
let time = (new Date()).valueOf();
if (expiry > time) {
return true;
} else {
return false;
}
} else {
throw new Error('certificate does not exist');
}
} else {
throw new Error('no certificate specified');
}
}
Possible Usage
const fs = require('fs');
const acme = require('acme-client');
if (acme.verify('path/to/cert.pem')) {
var key = fs.readFileSync('path/to/key.pem');
var cert = fs.readFileSync('path/to/cert.pem');
} else {
// generate key and certificate using acme-client
fs.writeFileSync('path/to/key.pem', key);
fs.writeFileSync('path/to/cert.pem', cert);
}
// use key and certificate
By the way, thank you so much for such a great package!
I really appreciate the sophisticated code, clear documentation and constant bug fixes (all of the other major ACME clients are either really complicated or have serious bugs).
I use
const client = new acme.Client({
directoryUrl: acme.directory.letsencrypt.staging,
accountKey: greenlockacc.acckey,//key pem encoded.
accountUrl: greenlockacc.client.api.accountUrl//string
});
But i recive
(node:73378) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type object
at Function.from (buffer.js:293:9)
at new AcmeClient (/Volumes/Container disk2/nodejsmain/node_modules/acme-client/src/client.js:47:38)
at next (/Volumes/Container disk2/nodejsmain/resources/module/workercontroll/workercontroll.js:157:36)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
(node:73378) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:73378) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
No idea what I'm doing wrong..
I have already checkt the typs of the values. there acre correct.
When the OpenSSL CLI is unavailable (or presumably otherwise has a problem), the resulting error cannot be caught promise-style.
const acme = require('acme-client');
acme.openssl.createPrivateKey().then(() => {
}).catch((e) => {
console.error('CAUGHT', e);
});
The resulting output:
events.js:167
throw er; // Unhandled 'error' event
^Error: spawn openssl ENOENT
at Process.ChildProcess._handle.onexit (internal/child_process.js:229:19)
at onErrorNT (internal/child_process.js:406:16)
at process._tickCallback (internal/process/next_tick.js:63:19)
at Function.Module.runMain (internal/modules/cjs/loader.js:746:11)
at startup (internal/bootstrap/node.js:240:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:564:3)
Emitted 'error' event at:
at Process.ChildProcess._handle.onexit (internal/child_process.js:235:12)
at onErrorNT (internal/child_process.js:406:16)
[... lines matching original stack trace ...]
at bootstrapNodeJSCore (internal/bootstrap/node.js:564:3)
Hopefully, this can be a non-issue once #3 is wrapped up. Thank you for your efforts!
Let's encrypt added ECDSA support in staging.
https://community.letsencrypt.org/t/ecdsa-issuance-available-in-staging-march-24/147839
It would be nice to have the option to generate a ECDSA certificate.
I am trying to create acme account based on the examples provided, but I end up receiving this error. Any idea on how to solve it ?
TypeError [ERR_INVALID_OPT_VALUE]: The value "jwk" is invalid for option "format"
at parseKeyFormat (internal/crypto/keys.js:147:9)
at parseKeyFormatAndType (internal/crypto/keys.js:182:18)
at parseKeyEncoding (internal/crypto/keys.js:208:7)
at parsePublicKeyEncoding (internal/crypto/keys.js:244:10)
at PublicKeyObject.export (internal/crypto/keys.js:116:11)
at getJwk (/Users/adithya/Documents/adithya/acme-ssl/node_modules/acme-client/src/crypto/index.js:157:54)
at HttpClient.getJwk (/Users/adithya/Documents/adithya/acme-ssl/node_modules/acme-client/src/http.js:97:24)
at HttpClient.createSignedBody (/Users/adithya/Documents/adithya/acme-ssl/node_modules/acme-client/src/http.js:234:26)
at HttpClient.signedRequest (/Users/adithya/Documents/adithya/acme-ssl/node_modules/acme-client/src/http.js:299:27)
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
code: 'ERR_INVALID_OPT_VALUE'
}
Here is the code.
const acme = require('acme-client');
const fs = require('fs');
const util = require('util');
fs.readFileAsync = util.promisify(fs.readFile);
fs.writeFileAsync = util.promisify(fs.writeFile);
let privateKey;
let client;
let createNewAccount = false;
fs.readFileAsync('./key')
.then(content => {
if(content.length){
privateKey = content;
createNewAccount = false;
return
}else{
return acme.crypto.createPrivateKey()
}
})
.then(content => {
if(content){
privateKey = content.toString();
fs.writeFileAsync('./key', privateKey);
return privateKey;
}else{
return content
}
})
.then(() => {
client = new acme.Client({
directoryUrl: acme.directory.letsencrypt.staging,
accountKey: privateKey
});
})
.then(() => {
return client.createAccount({
termsOfServiceAgreed: true
});
})
.catch(e => {
console.log(e)
})
Just as a heads up. No immediate action should be required.
see: https://letsencrypt.org/2020/12/21/extending-android-compatibility.html
What about the alternate chain? Today, some ACME clients are able to instead request an alternate chain, if their user has configured it. We currently provide the option of getting the chain: Subscriber Certificate < – R3 < – ISRG Root X1 We will continue to offer this same chain as an alternate. However, note that most ACME clients don’t yet have a way to select this alternate chain (for example, Certbot selects chains by looking to see if they contain a given Issuer Name, but this chain doesn’t contain any Issuer Names which the high compatibility chain above doesn’t). We’ll be working with ACME client developers to create more flexible chain selection mechanisms going forward.
When the directory URL returning a non-success status, the thrown error is rather misleading (it has nothing to do with dns resolution as one might be lead to think). For example:
# call client.createOrder()
acme-client HTTP request: get https://acme-v02.api.letsencrypt.org/directory +0ms
acme-client RESP 501 get https://acme-v02.api.letsencrypt.org/directory +12ms
# throws Error: Could not resolve URL for API resource: "newOrder"
That 501 here is caused by a axios bug (fixed in axios/axios@e426910) related to https proxy handling. I'd suggest to include the URL and statuscode in the thrown error message in such cases (e.g. when statuscode is not between 200 and 299).
I am trying to revoke a certificate that was issued using client.auto()
using client.revokeCertificate()
, but it throws the following error:
Error: JWS verification error
at AcmeApi.apiRequest (node_modules/.pnpm/[email protected]/node_modules/acme-client/src/api.js:56:19)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async AcmeClient.revokeCertificate (node_modules/.pnpm/[email protected]/node_modules/acme-client/src/client.js:681:22)
The reproduction code is below (it is a staging certificate, no security issue posting it here):
import * as acme from 'acme-client'
const accountUrl =
'https://acme-staging-v02.api.letsencrypt.org/acme/acct/59183794'
const certificate = `-----BEGIN CERTIFICATE-----
MIIFbDCCBFSgAwIBAgITAPog0TD3lAcsQWrmy6prUar91TANBgkqhkiG9w0BAQsF
ADBZMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXKFNUQUdJTkcpIExldCdzIEVuY3J5
cHQxKDAmBgNVBAMTHyhTVEFHSU5HKSBBcnRpZmljaWFsIEFwcmljb3QgUjMwHhcN
MjIwNjMwMTE0NzA0WhcNMjIwOTI4MTE0NzAzWjAmMSQwIgYDVQQDExtjZXJ0LXRl
c3QuaW50ZXJuYWwuc3Z0di5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDPXwdEoHKGPhQmlK3yB3DeVjCn6r2XDyv/0bJpf83Qez3I4PYu5WUCz+tZ
hf/tz3FVW7IntcyIGxEZDKHFS2cO5D36zV8PixFBy7i02ioxaic2Kl/fVZ7GTPmX
jXO5pD3sRmyX6PN70r8TwBZAQ+rzM4BjR4PBEWTmBQe/oCln5fhr1Yj/vL3E/bVO
7CGBTP+0BoGQfDeY6nT0YIpYByDRu7VVYL1RVyLq7/wqDphzsw/uYDIxCbCAFOSG
zbo/7A1V6fw3y0G3s22YA+zxkNyBiznJTSo2e8hesdYUVNqgs0MMBvzefWmYogS9
N9RImGN+IUCYUygfeH//wMqzHZ6HAgMBAAGjggJeMIICWjAOBgNVHQ8BAf8EBAMC
BaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAw
HQYDVR0OBBYEFGOSCPl36dS1aexxbcguNJQeqz6eMB8GA1UdIwQYMBaAFN5yekjf
McOmUN+fhSPfVzdLXS5lMF0GCCsGAQUFBwEBBFEwTzAlBggrBgEFBQcwAYYZaHR0
cDovL3N0Zy1yMy5vLmxlbmNyLm9yZzAmBggrBgEFBQcwAoYaaHR0cDovL3N0Zy1y
My5pLmxlbmNyLm9yZy8wJgYDVR0RBB8wHYIbY2VydC10ZXN0LmludGVybmFsLnN2
dHYub3JnMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYI
KwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHW
eQIEAgSB9QSB8gDwAHYAKHYaGJAn++880NYaAY12sFBXKcenQRvMvfYE9F1CYVMA
AAGBtKaUsgAABAMARzBFAiABNygKB8S9DXEMn8ZX29GbRm2/FtlZttjUK52a3j1J
QAIhAOvD9OzQLtT8VcgKExFXZ41xEku7jZqVKQIgIhoq+xy6AHYAsMyD5aX5fWuv
fAnMKEkEhyrH6IsTLGNQt8b9JuFsbHcAAAGBtKaUrQAABAMARzBFAiEAmGGv5Kyb
cyftxolYuQpg7/QLbKrx0dxlm8rWhGQXr5QCIFDNHxS3FnYr8ntgtHu5hxbcgfYA
utZXFPLJqsgRFzX9MA0GCSqGSIb3DQEBCwUAA4IBAQBS5ns4Ma4/u5+nfCQjKZmB
wWQ396Y0vWO4DaKuICVf7Vm+mNexxo4IT1aJ/XebyjiJDtQvoEQa/h0w83p/PoQS
ms7JwRNMCMAjSECbt9mtYjZ7rFQt5zFfbz406PaZOWkRsyTTWdboImpKpb6wsqMC
MnijSywozFidfEbwlGc4zKSVEM63/kOMPpXQFwovvpO0IKwTLzIpznUqLKTzAwLj
T8gcakqLhwypN33GwpGTkFdgte+kwnQHNGFRdIkyoxJMc3g+MZmSR9VxzJqNFPc0
WWwodfKziK7Nr6vtFcH1KoghVI6mc40p2Hz6eFNehZVMdlHzQ3dmE1FphVTBJZA/
-----END CERTIFICATE-----`
const keyPem = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAz18HRKByhj4UJpSt8gdw3lYwp+q9lw8r/9GyaX/N0Hs9yOD2
LuVlAs/rWYX/7c9xVVuyJ7XMiBsRGQyhxUtnDuQ9+s1fD4sRQcu4tNoqMWonNipf
31Wexkz5l41zuaQ97EZsl+jze9K/E8AWQEPq8zOAY0eDwRFk5gUHv6ApZ+X4a9WI
/7y9xP21TuwhgUz/tAaBkHw3mOp09GCKWAcg0bu1VWC9UVci6u/8Kg6Yc7MP7mAy
MQmwgBTkhs26P+wNVen8N8tBt7NtmAPs8ZDcgYs5yU0qNnvIXrHWFFTaoLNDDAb8
3n1pmKIEvTfUSJhjfiFAmFMoH3h//8DKsx2ehwIDAQABAoIBAQCbADLUjwFLajdx
pwxlrj4lUlNID+Pqd4Kx0rICvW6/eH70GYKqBvj7oyINZmWoAbh6zvFZUUAyM9kR
2ilI6VV9Agl53+xAF/isL3Q+ahvDl877Y60JqFvnaT3ek/J+Pk+yvekJmyvzz9DX
GGsJWjrR0hsTGi9XLoM2k2f5SePpLdmF8W9BTDpX8jqLmFM2qbEHLgpxjEgE5RN5
219xgWpmaIlRTCFkpENgLPc9mKki0+1JO4OXM91yTps+iIfm2xio1P81qfDMrTjG
eTw2zESqGJNNBcBSKy6WCQTse0rGRlpIru6DMIQmYvh2UOxGq0srW+DuKE8Y9izf
A5s+Fi1hAoGBAOpeNhUVMEXzHlmQYLmgoz9OXI0ic9OT4PBNBxUksQYleWFJh13i
VBQa50pg5fHQZrggl/PDui9+E4D0vZbXV/YM4871mvXYxy8zt1hVBXcKRTsO8suF
ozvhIKhbzVCKSgBVBsRMccYBPX96qxecKHc8k2FakWBRn9bSFM4zFgKJAoGBAOKC
65bPvfsbJfLJXkUb884rEzVD+GQY3omqcFSTSpYsmrYo5eQAmWNt8MM0zdnS5r/Q
1HqTBitnw/cQEAq+vUDaZFFDGjgqZXfudAMetfWmHNadirlfWsFkWTwS8BV3flw2
VLABJP0ONGqJuxlNELTQwEfFasA4Qvf64hryw5SPAoGAeAANCPZ0ZRx6abl3DRSn
AX8J/GmrjKWnAuMtwTGsQk1lvN59JSEBk9nCm17J3eqq7ZQiuXl6F18idWDawiU4
sI8hBZLT6RU5x31fXycJSU5E7FxXDMY8MiivJAT9N0PXaBwg/tl1V8DC0Ebq24fl
YEfv3mUVEpA4mzGt3XZiyqECgYAH1rgIdBADDKF3kXhts9XwIJV9lmiWLjYlVXyl
sKM4JzsDas7p4Qtt4XQfjUhCbYYGxVdVFpl0Dxc9CZVEie1KvUQTe9sAbpcsW1gw
c3OgmKsO0kzmtWISp0JfTdh5JbV9w3OS07dP+pndxf5vlbcqSr8cvLxBArFum4QP
oq8aCQKBgH94w+qtgtGtV+JtSD54rsamOTd3IqgYL96syADu9jPLg5L+NSyTKKve
zEJtHXqNtQ6unOlXzIXa2m7X8NaxuReQIMDsrcX2u2oKq1picwGhqjYMsYpGyvtl
yivAFRa3rlpDQuumVTtfW832R563cT0CwtGW39Xp3jdSumq+tmkF
-----END RSA PRIVATE KEY-----`
async function main() {
const client = new acme.Client({
accountUrl,
directoryUrl: acme.directory.letsencrypt.staging,
accountKey: keyPem,
})
await client.revokeCertificate(certificate, {
reason: 5, // cessationOfOperation; https://datatracker.ietf.org/doc/html/rfc5280#section-5.3.1
})
}
main().catch((e) => {
console.error('Fatal:', e)
process.exit(1)
})
What am I doing wrong?
It would be nice if there will be ability to receive an exception in methods like getCertificates if status != 'valid'
In a lot of cases, it is better to fail the script than to hang it up.
Is it okay if I'll send a pull request with the wait
parameter, which will be true
by default, but will add the ability to turn it to false?
Does this support ACMEv2 and wildcard certificates?
At Let's Encrypt, we're planning to stop supporting SHA-1 self-signatures on CSRs (in part because Go is removing support for those). In a review of our logs, node-acme-client showed up as a popular client that was sending CSRs with SHA-1 self-signatures. It looks like the problem is here:
node-acme-client/src/crypto/forge.js
Lines 439 to 440 in 6d5ce60
You don't set a specific signature algorithm, so node-forge defaults to SHA-1:
I recommend adding a second argument containing the appropriate OID for sha256WithRSAEncryption
.
From https://letsencrypt.org/docs/challenge-types/:
Our implementation of the HTTP-01 challenge follows redirects, up to 10 redirects deep. It only accepts redirects to “http:” or “https:”, and only to ports 80 or 443. It does not accept redirects to IP addresses. When redirected to an HTTPS URL, it does not validate certificates (since this challenge is intended to bootstrap valid certificates, it may encounter self-signed or expired certificates along the way).
I haven't tested this, but I think the code to change would be
node-acme-client/src/verify.js
Line 28 in 4335c1e
Current code:
const resp = await axios.get(challengeUrl);
Modified code:
const resp = await axios.get(challengeUrl, { httpsAgent: new https.Agent({rejectUnauthorized: false} });
=== npm audit security report ===
Manual Review
Some vulnerabilities require your attention to resolve
Visit https://go.npm.me/audit-guide for additional guidance
High Server-Side Request Forgery
Package axios
Patched in >=0.21.1
Dependency of acme-client
Path acme-client > axios
More info https://npmjs.com/advisories/1594
Quick question, do you have any plans or bandwidth to add support for ES256 or EdDSA account keys? (the RFC 8555 spec says ES256 is now mandatory for servers)
BTWs thanks for building a low level client, literally every other client only has an auto function, which breaks on multi-domain dns cert orders.
When the XMLHttpRequest
global is defined, axios
uses it over node's API which presents an issue in environments like jest
which by default sets this global as part of its jsdom
environment and which in turn triggers a Error: Headers User-Agent forbidden
coming from this module because it attempts to set the User-Agent which is forbidden in browsers.
jest
can be configured to not use jsdom
but I feel that using node-fetch or a wrapper over Axios would be a better choice for a http client becuse this module should never run in the browser so does not need that part of Axios.
This is great package.
There is an issue involving DNS rebinding.
Some routers will not allow a client on the local network to access a server on the local network via an externally managed domain name/IP-address. There is a hack called DNS rebinding that they are trying to thwart.
With the http-01 challenge, the verifyChallenge method in the package attempts to access the site and verify the authorization token and key before moving on to requesting the certificate from LetsEncrypt.
This works fine if the router does not block the request, and in fact some routers are spotty on enforcing this so sometimes it gets through.
All that said, it might make sense to note this in the examples/documentation for users of the package that they can skip this step.
There might also be a need to add a noVerify option to the auto process interface.
Thanks for producing such a great piece of code.
"Error: Authorization not found in DNS TXT records for test.managed-test.de"
When I do a dig _acme-challenge.test.managed-test.de @mydnsserver TXT I get the correct TXT record.
I've read the documentation about debugging, but since I'm doing this on a node-red installation I'm totally not sure what I'm supposed to do where. Any help very much appreciated.
Hey guys, need to know that how we can renew certificates with this package?
getAuthorizations()
augments the response of the ACME server with the url
property, but that property is not included in the return type.
If the rfc8555
types should reflect the official specification which makes sense the return type could be adjusted to: rfc8555.Authorization & { url: string }
.
Heya, excuse this basic question I'm fairly new to implementing SSL like this.
I've managed to get everything working with Route53 on AWS.
When I do
const certificate = await client.auto({
...
Then log out the certificate you get 3 back, which I guess are cert, key, privateKey?
Which ones which please?
export interface Challenge extends HasStatus {
type: string;
url: string;
status: string;
validated?: string;
error?: object;
token?: string
}
Hi, great job 👍
How can I renew my certificates? Is calling auto
method to issue and renew certificates the right way?
Should I store CSR? Do I need it to renew my certificates?
Issue has been abandoned - revised issue: #28
Dependent upon feature request #28
Please can you add another option to the client.auto()
function called renewUponExpiry
.
It will have the following properties:
boolean
data type.
false
: certificate will be renewed every time the client.auto()
function is executed.
return
value: default (certificate is returned).true
: certificate will only be renewed when it expires.
return
value:
cache
option if the certificate is still valid.cache
option has expired.false
.I’m having a little trouble using the library. I hope it’s okay to ask for help here.
I’m trying to invoke createOrder()
, but it fails with the following stack trace:
Error: error:09091064:PEM routines:PEM_read_bio_ex:bad base64 decode
at Sign.sign (internal/crypto/sig.js:84:29)
at HttpClient.createSignedBody (C:\\Rest\node_modules\acme-client\src\http.js:195:50)
at HttpClient.signedRequest (C:\\Rest\node_modules\acme-client\src\http.js:220:33)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async AcmeApi.apiRequest (C:\\Rest\node_modules\acme-client\src\api.js:51:22)
at async AcmeClient.createOrder (C:\\Rest\node_modules\acme-client\src\client.js:202:22)
at async issueRoute53 (webpack:///./src/Modules/Issue.ts?:37:19)
at async catchPromise (webpack:///./src/CatchPromise.ts?:7:22)
The error message suggests that the issue lies with the accountKey
. The PEM string may not be in the correct format, although I have corrected some errors in the format, and am not sure whether there are any more.
In an abstract form, my code looks something like this:
'use strict';
const Acme = require('acme-client');
const PEM_LINE_LENGTH = 64;
run({domain: 'example.com', email: '[email protected]'});
async function run({domain, email})
{
const { accountKey, accountUrl } = await createAccount({email});
await createOrder({accountKey, accountUrl, domain});
};
async function createAccount({email})
{
const acme = new Acme.Client
(
{
directoryUrl: Acme.directory.letsencrypt.staging,
accountKey: (await Acme.forge.createCsr({}))[0]
}
);
const account = await acme.createAccount
(
{
contact: [ `mailto:${email}` ],
termsOfServiceAgreed: true
}
);
return { accountKey: account.key.n, accountUrl: acme.getAccountUrl() };
};
async function createOrder({accountKey, accountUrl, domain})
{
const accountKeyPem = generateAccountKeyPem({accountKey});
const acme = new Acme.Client
(
{
directoryUrl: Acme.directory.letsencrypt.staging,
accountKey: accountKeyPem,
accountUrl
}
);
const order = await acme.createOrder
(
{
identifiers:
[
{
type: 'dns',
value: domain
}
]
}
);
console.log(order);
};
function generateAccountKeyPem({accountKey})
{
let pem = '';
pem += '-----BEGIN RSA PRIVATE KEY-----\n';
for (let index = 0; index < accountKey.length - 1; index += PEM_LINE_LENGTH)
{
pem += accountKey.slice(index, index + PEM_LINE_LENGTH) + '\n';
};
pem += '-----END RSA PRIVATE KEY-----\n';
return pem;
};
It would be much appreciated if anyone is able to provide assistance.
using the auto() method, my challenge fails each time with this error...
Error: Parse error reading JWS
at AcmeApi._callee$ (/node_modules/acme-client/lib/api.js:126:39)
at tryCatch (/node_modules/regenerator-runtime/runtime.js:62:40)
at Generator.invoke [as _invoke] (/node_modules/regenerator-runtime/runtime.js:296:22)
at Generator.prototype.(anonymous function) [as next] (/node_modules/regenerator-runtime/runtime.js:114:21)
at step (/node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
at /node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13
at <anonymous>
The issue appears to be in creating the challenge, rather than an issue with the challenge failing. I tried to delve deeper down the stack but found it difficult to follow through the 'regenerator-runtime' library
Thanks in advance!
Possibly related:
Let's Encrypt:
Nonces are not shared between instances of Boulder, they are only valid for the Boulder instance that issued them to begin with. So if two curl requests are load balanced to different Boulder instances, a seemingly valid nonce would fail.
TravisCI:
However, with our current NAT each TCP/UDP connection may be established through a different NAT host than any previous connections.
I wanted to let you know I have published a package using node-acme-client.
http-reverse-proxy-ts
It would have been much harder without your prior effort.
Thanks.
Trying to use this package as illustrated in this example
https://github.com/publishlab/node-acme-client/blob/master/examples/auto.js
But seem to keep hitting this issue. Can't figure it out,
Error: unable to load X509 request 140026476918424:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:701:Expecting: CERTIFICATE REQUEST at ChildProcess.<anonymous> (/node_modules/openssl-wrapper/lib/index.js:91:13) at Object.apply (/node_modules/harmony-reflect/reflect.js:2064:37) at ChildProcess.emit (events.js:180:13)
Running on an AWS EC2 Ubuntu, Can post the code if required, but essentially it's just a copy of the linked example
I'm using this module to do ACMEv2 challenges for a lot of domains and sometimes I run into this error:
Error: Error creating new order :: too many currently pending authorizations: see https://letsencrypt.org/docs/rate-limits/
at AcmeApi.apiRequest (node_modules/acme-client/src/api.js:54:19)
at runMicrotasks (<anonymous>)
at async AcmeClient.createOrder (node_modules/acme-client/src/client.js:281:22)
My code is pretty much exactly following the example from https://github.com/publishlab/node-acme-client/blob/master/examples/api.js.
What I gather, this is a client-caused error with too many open (e.g. unfulfilled) authorization requests. My requests fail for various reasons that are not in my control and I wonder if this module could either automatically cancel/destroy failed authorization request or expose a method to do so (that could be called in case of a challenge error).
From https://community.letsencrypt.org/t/error-429-too-many-pending-authorizations/27273/7:
if you get an authz for one requested domain but fail to get it for another, make sure you proactively destroy the first authz before giving up
According to https://community.letsencrypt.org/t/too-many-currently-pending-authorizations/64571 it sounds like the issue should be "mitigated" by using ACME v2, but I certainly still see it.
The url here should be .well-known/acme-challenge/
rather than .well-known/acme-challenges/
:
https://github.com/publishlab/node-acme-client/blob/master/examples/auto.js#L27
Thanks for a great library!
acme-client requires OpenSSL to be installed and available in $PATH.
https://github.com/digitalbazaar/forge works pretty great. I've used it to replace OpenSSL needs for cross platform nodejs apps which needed certificate management (key generation, CSR and signing).
It would be nice if this library wouldn't depend on CLI tools and it would make it more cross platform than it already is.
If you are open to changing to node forge, I can help with this.
Dear Node-Acme-Client,
While testing and using your tool, I came across some irregularities. The documentation and ACME Guide specifies several different responses to Challenge requests. This might be confusing. At one point requesting a challenge with order of type dns returns a http-01 challenge, while at another point requesting a challenge with type dns returns a dns-01 and http-01 challenge to choose from.
In reality it seems as if requesting a challenge using an order of type dns, currently returns a dns-01 challenge, which seems expected to occur. However, since it is not in accordance to the documentation this requires some fixes. Furthermore, in reality the resolving challenge might also be of type http-01, as I currently cannot explain, how some of my challenges were successful, although differentiating from other tests.
Perhaps the tool is not working as expected or someone was messing with live configuration at Lets Encrypt. Perhaps this could be verified again, as I am unsure of whether the tool is working as expected.
with kind regards,
Cob @ CobbleVision
Update: This behavior is reproducible for wildcard domains, which seem to be limited to dns challenges.
After successfully creating account, order, CSR and a DNS TXT record for my domain, I'm trying to verify the internal challenge, but I fail with
"Error: Unable to verify ACME challenge, URL not found"
These are the data objects I can access at this point:
order: object
status: "pending"
expires: "2021-05-25T12:29:19Z"
identifiers: array[1]
0: object
type: "dns"
value: "test.managed-test.de"
authorizations: array[1]
0: "https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/---"
finalize: "https://acme-staging-v02.api.letsencrypt.org/acme/finalize/---/---"
url: "https://acme-staging-v02.api.letsencrypt.org/acme/order/---/---"
authorizations: array[1]
0: object
identifier: object
type: "dns"
value: "test.managed-test.de"
status: "pending"
expires: "2021-05-25T12:29:19Z"
challenges: array[1]
0: object
type: "dns-01"
status: "pending"
url: "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/---"
token: "---"
url: "https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/---"
But both
const verify = await client.verifyChallenge(order.authorizations[0], authorizations[0].challenges[0]);
and
const verify = await client.verifyChallenge( authorizations[0], authorizations[0].challenges[0]);
give the aforementioned error.
Which bothers me in itself - what URL, aren't we doing dns here? I know I at least do...
So anybody have any enlightening words for me?
In the CertificateInfo
interface, the properties notBefore
and notAfter
are declared as strings.
At runtime, though, the properties seem to be Date
objects.
Hey guys, first of all thanks a lot for building such a useful library and for managing it so actively. I am using this package for a project where I have built an integration to generate SSL certificates (it is a kind of hosting service).
I have observed an issue that verifyChallenge don't really timeout in cases where DNS challenge has not been fulfilled.
So for example, I create an order for example.com and decided to go for DNS challenge but submitted a request to verifyChallange without actually creating a TXT record. In this case, verifyChallange function waits for a lot - like several minutes before generating an error/response.
I am proposing to change "hard" packages dependency:
towards to npm "optionalDependencies" where implementator decide about the need to install them.
Best regards.
I believe the code at line 87 of example/api.js is incorrect.
The current line is:
const challenges = { authz };
I suspect the line should read:
const { challenges } = authz;
First of all I'd like to thank you for recently implementing externalAccountBinding! I was very excited to implement ZeroSSL as a backup to Let's Encrypt only to be a bit disappointed it didn't work with client.auto();
The error from ZeroSSL's ACME happens when ordering a certificate: One or more identifiers are duplicated
After some debugging it seems that forge.readCsrDomains will add the common name to the alt names even if you specify it to be empty as shown below.
const [key, csr] = await forge.createCsr({ commonName: 'example.com', altNames: [], });
This breaks ZeroSSL because the line below (auto.js line 64) results in an array with duplicate domain names.
const domains = [csrDomains.commonName].concat(csrDomains.altNames);
Iam getting stuck while finalizing order. I would appreciate any help ^^
Heres the line which is throwing error (/acme-client/src/api.js:57):
throw new Error(util.formatResponseError(resp));
Error:
Error: Error finalizing order :: signature algorithm not supported
Heres how my CSR and orderObject looks like:
Could you implement an option, where you can specify which type of challenges you want to get with your order? (I know this is already kind of implemented in automode with opts.challengePriority)
This would also avoid this error:
Unable to produce key authorization, unknown challenge type: tls-alpn-01
Hey there, I'm trying to use auto.js, essentially as is, to test getting a cert but I keep running into an exception.
Would write "pCWJ_5UHalASCBfjQk5HlQhuqbRsK61UTn8McdY3dIo.hOYObyEDbWAyjJ5yIJ0h2Kf2GjEb-SegaCGzhrtyHA4" to path "/Users/max/src/github.com/acme-www/.well-known/acme-challenge/pCWJ_5UHalASCBfjQk5HlQhuqbRsK61UTn8McdY3dIo"
(node:39313) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received undefined
at maybeCallback (fs.js:142:9)
at Object.writeFile (fs.js:1175:14)
at Object.challengeCreateFn (/Users/max/src/github.com/smallstep/node-acme-test/test.js:35:25)
at _callee3$ (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/acme-client/lib/auto.js:211:61)
From previous event:
at _callee4$ (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/acme-client/lib/auto.js:267:40)
at tryCatch (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/regenerator-runtime/runtime.js:62:40)
at Generator.invoke [as _invoke] (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/regenerator-runtime/runtime.js:296:22)
at Generator.prototype.<computed> [as next] (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/regenerator-runtime/runtime.js:114:21)
at step (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
at /Users/max/src/github.com/smallstep/node-acme-test/node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13
(node:39313) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:39313) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.```
from the line that is writing the http challenge file. It's been a minute since I've used node.js, so I'm sure it's an issue on my end. All I did was uncomment the line of code that was doing the `await fs.writeFile(filePath, fileContents);`. I also tried `return ...` at the head of that line and still the same error.
Appreciate the time/help!
It would be useful if the library supported grabbing the certificate using an alternative chain based on the alternate
link header. See:
https://community.letsencrypt.org/t/transition-to-isrgs-root-delayed-until-jan-11-2021/125516
https://github.com/certbot/certbot/blob/master/certbot/CHANGELOG.md#160---2020-07-07
using the auto() method, my challenge fails each time with this error...
Error: Parse error reading JWS
at AcmeApi._callee$ (/node_modules/acme-client/lib/api.js:126:39)
at tryCatch (/node_modules/regenerator-runtime/runtime.js:62:40)
at Generator.invoke [as _invoke] (/node_modules/regenerator-runtime/runtime.js:296:22)
at Generator.prototype.(anonymous function) [as next] (/node_modules/regenerator-runtime/runtime.js:114:21)
at step (/node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
at /node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13
at <anonymous>
The issue appears to be in creating the challenge, rather than an issue with the challenge failing. I tried to delve deeper down the stack but found it difficult to follow through the 'regenerator-runtime' library
Thanks in advance!
Hi 👋
I think everything with this module works well.
One of my hosts cannot set the DNS programmatically but when I use this module it requires HTTP and DNS challenge. Is it configurable?
I'm calling auto like this:
client.auto({
csr: res.readCertCsr, // cert csr buffer
email: csrOpts.emailAddress,
termsOfServiceAgreed: true,
challengeCreateFn
challengeRemoveFn
})
.then(acmeAutoSuccess)
.catch(acmeAutoFailure)
I tried using challengePriority: ["http-01"]
as an option but it wont limit to HTTP.
Maybe you know better than I that both are required and I just have to deal with it.
Thanks for the amazing library!!
In http-01 validation, when debugging response from http:///.well-known/acme-challenge/ , it contains an extra line break \n with content hence resulting the error Authorization not found in HTTP response from....
Also, I've checked my keyAuth content again and again and it doesn't have any linebreaks. Shouldn't it be handled there in the verify.js?
Hi there,
is it possible to bump axios again? The required security fix is trivial, see: axios/axios#3980
but it is hard for me to know, whether the accompanying changes would break acme-client...
Or is it not necessary, because in acme-client's usage of axios the vulnerability will never be executed anyway (which is, what I think...)
Thanks for any thoughts!
Michael.
Issue has been abandoned - revised issue: #30
Abandoned due to flaw: If the old certificate was retrieved, it would not match the newly generated key and would result in an error
Please can you add another option to the client.auto()
function called renewIfExpired
.
It will have the following properties:
string
data type.
return
value:
null
(new certificate is always generated).Possible Implementation
if (renewIfExpired) {
if (fs.existsSync(renewIfExpired)) {
var cert = fs.readFileSync(renewIfExpired);
var expiry = (new Date((await ACME.forge.readCertificateInfo(cert)).notAfter)).valueOf();
var time = (new Date()).valueOf();
if (expiry >= time) {
return cert;
}
} else {
// ERROR: file does not exist
}
}
// Continue certificate generation
By the way, thank you so much for such a great package!
I really appreciate the sophisticated code, clear documentation and constant bug fixes (all of the other major ACME clients are either really complicated or have serious bugs).
My apologies if this is a stupid question. But i am trying to register SSL certificates with LE using your library, and using the client.auto() function.
Each time i create the client i am using the same accountKey (one i generated and saved to my local file system) but i hit the rate limit for new accounts, I was assuming that if i create the client with the same account key it would keep ordering certificates using the same account is this not correct?
var accountKey = 'NEVERCHANGINGACCOUNTKEY';
//Create a client
client = new acme.Client({
directoryUrl: directoryURL,
accountKey: accountKey,
});
//Create a CSR
acme.openssl.createCsr({
// commonName: '*.' + domain,
commonName: domain,
// altNames: ['www.' +domain],
}).then(function(csr) {
return next(null, accountKey, csr);
}, function(err) {
return next(err);
});
client.auto({
csr: certCsr,
email: '[email protected]',
termsOfServiceAgreed: true,
challengeCreateFn: createChallenge,
challengeRemoveFn: removeChallenge,
}).then(challengeComplete, challengeFailed);
This is the error when the challenge is created
Error: Error creating new account :: too many registrations for this IP: see https://letsencrypt.org/docs/rate-limits/
at AcmeApi._callee$ (/fluro/server/node_modules/acme-client/lib/api.js:126:39)
Heads up - new install today gives me this error:
TypeError: Cannot assign to read only property 'toJSON' of object 'Error'
at Function.assign ()
at Object.inherits (/node_modules/axios/lib/utils.js:362:19)
axios just updated a bunch of things so it's likely the acme-client is using something they've deprecated.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.