@certd/acme-client 1.26.9 → 1.26.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/agents.js +101 -0
- package/src/api.js +1 -0
- package/src/auto.js +52 -18
- package/src/axios.js +27 -8
- package/src/client.js +1 -0
- package/src/http.js +1 -13
- package/src/index.js +1 -0
- package/src/logger.js +3 -3
- package/types/index.d.ts +2 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Simple and unopinionated ACME client",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "nmorsman",
|
|
6
|
-
"version": "1.26.
|
|
6
|
+
"version": "1.26.11",
|
|
7
7
|
"main": "src/index.js",
|
|
8
8
|
"types": "types/index.d.ts",
|
|
9
9
|
"license": "MIT",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"asn1js": "^3.0.5",
|
|
21
21
|
"axios": "^1.7.2",
|
|
22
22
|
"debug": "^4.3.5",
|
|
23
|
+
"http-proxy-agent": "^7.0.2",
|
|
23
24
|
"https-proxy-agent": "^7.0.5",
|
|
24
25
|
"node-forge": "^1.3.1"
|
|
25
26
|
},
|
|
@@ -59,5 +60,5 @@
|
|
|
59
60
|
"bugs": {
|
|
60
61
|
"url": "https://github.com/publishlab/node-acme-client/issues"
|
|
61
62
|
},
|
|
62
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "3a78cb9929fd63bb72f0e00f4389e775c926c789"
|
|
63
64
|
}
|
package/src/agents.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const nodeHttp = require('node:http');
|
|
2
|
+
const https = require('node:https');
|
|
3
|
+
const { HttpProxyAgent } = require('http-proxy-agent');
|
|
4
|
+
const { HttpsProxyAgent } = require('https-proxy-agent');
|
|
5
|
+
const { log } = require('./logger');
|
|
6
|
+
|
|
7
|
+
function createAgent(opts = {}) {
|
|
8
|
+
let httpAgent;
|
|
9
|
+
let
|
|
10
|
+
httpsAgent;
|
|
11
|
+
const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy;
|
|
12
|
+
if (httpProxy) {
|
|
13
|
+
log(`acme use httpProxy:${httpProxy}`);
|
|
14
|
+
httpAgent = new HttpProxyAgent(httpProxy, opts);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
httpAgent = new nodeHttp.Agent(opts);
|
|
18
|
+
}
|
|
19
|
+
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
|
|
20
|
+
if (httpsProxy) {
|
|
21
|
+
log(`acme use httpsProxy:${httpsProxy}`);
|
|
22
|
+
httpsAgent = new HttpsProxyAgent(httpsProxy, opts);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
httpsAgent = new https.Agent(opts);
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
httpAgent,
|
|
29
|
+
httpsAgent,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let defaultAgents = createAgent();
|
|
34
|
+
|
|
35
|
+
function getGlobalAgents() {
|
|
36
|
+
return defaultAgents;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function setGlobalProxy(opts) {
|
|
40
|
+
log('acme setGlobalProxy:', opts);
|
|
41
|
+
if (opts.httpProxy) {
|
|
42
|
+
process.env.HTTP_PROXY = opts.httpProxy;
|
|
43
|
+
}
|
|
44
|
+
if (opts.httpsProxy) {
|
|
45
|
+
process.env.HTTPS_PROXY = opts.httpsProxy;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
defaultAgents = createAgent();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class HttpError extends Error {
|
|
52
|
+
constructor(error) {
|
|
53
|
+
super(error || error.message);
|
|
54
|
+
if (!error) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (error.message.indexOf('ssl3_get_record:wrong version number') >= 0) {
|
|
59
|
+
this.message = 'http协议错误,服务端要求http协议,请检查是否使用了https请求';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.name = error.name;
|
|
63
|
+
this.code = error.code;
|
|
64
|
+
this.cause = error.cause;
|
|
65
|
+
|
|
66
|
+
if (error.response) {
|
|
67
|
+
this.status = error.response.status;
|
|
68
|
+
this.statusText = error.response.statusText;
|
|
69
|
+
this.response = {
|
|
70
|
+
data: error.response.data,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let url = '';
|
|
75
|
+
if (error.config) {
|
|
76
|
+
this.request = {
|
|
77
|
+
baseURL: error.config.baseURL,
|
|
78
|
+
url: error.config.url,
|
|
79
|
+
method: error.config.method,
|
|
80
|
+
params: error.config.params,
|
|
81
|
+
data: error.config.data,
|
|
82
|
+
};
|
|
83
|
+
url = error.config.baseURL + error.config.url;
|
|
84
|
+
}
|
|
85
|
+
if (url) {
|
|
86
|
+
this.message = `${this.message}:${url}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
delete error.response;
|
|
90
|
+
delete error.config;
|
|
91
|
+
delete error.request;
|
|
92
|
+
// logger.error(error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = {
|
|
97
|
+
setGlobalProxy,
|
|
98
|
+
createAgent,
|
|
99
|
+
getGlobalAgents,
|
|
100
|
+
HttpError,
|
|
101
|
+
};
|
package/src/api.js
CHANGED
package/src/auto.js
CHANGED
|
@@ -182,12 +182,19 @@ module.exports = async (client, userOpts) => {
|
|
|
182
182
|
|
|
183
183
|
authorizations.forEach((authz) => {
|
|
184
184
|
const d = authz.identifier.value;
|
|
185
|
+
log(`authorization:domain = ${d}, value = ${JSON.stringify(authz)}`);
|
|
186
|
+
|
|
187
|
+
if (authz.status === 'valid') {
|
|
188
|
+
log(`[auto] [${d}] Authorization already has valid status, no need to complete challenges`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
185
191
|
let setd = false;
|
|
186
192
|
// eslint-disable-next-line no-restricted-syntax
|
|
187
193
|
for (const group of domainSets) {
|
|
188
194
|
if (!group[d]) {
|
|
189
195
|
group[d] = authz;
|
|
190
196
|
setd = true;
|
|
197
|
+
break;
|
|
191
198
|
}
|
|
192
199
|
}
|
|
193
200
|
if (!setd) {
|
|
@@ -197,6 +204,8 @@ module.exports = async (client, userOpts) => {
|
|
|
197
204
|
}
|
|
198
205
|
});
|
|
199
206
|
|
|
207
|
+
// log(`domainSets:${JSON.stringify(domainSets)}`);
|
|
208
|
+
|
|
200
209
|
const allChallengePromises = [];
|
|
201
210
|
// eslint-disable-next-line no-restricted-syntax
|
|
202
211
|
for (const domainSet of domainSets) {
|
|
@@ -233,28 +242,52 @@ module.exports = async (client, userOpts) => {
|
|
|
233
242
|
return Promise.all(results);
|
|
234
243
|
}
|
|
235
244
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
+
try {
|
|
246
|
+
log(`开始challenge,共${allChallengePromises.length}组`);
|
|
247
|
+
let i = 0;
|
|
248
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
249
|
+
for (const challengePromises of allChallengePromises) {
|
|
250
|
+
i += 1;
|
|
251
|
+
log(`开始第${i}组`);
|
|
252
|
+
if (opts.signal && opts.signal.aborted) {
|
|
253
|
+
throw new Error('用户取消');
|
|
254
|
+
}
|
|
245
255
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
256
|
+
try {
|
|
257
|
+
// eslint-disable-next-line no-await-in-loop
|
|
258
|
+
await runPromisePa(challengePromises);
|
|
259
|
+
}
|
|
260
|
+
catch (e) {
|
|
261
|
+
log(`证书申请失败${e.message}`);
|
|
262
|
+
throw e;
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
if (client.opts.sslProvider !== 'google') {
|
|
266
|
+
// letsencrypt 如果同时检出两个TXT记录,会以第一个为准,就会校验失败,所以需要提前删除
|
|
267
|
+
// zerossl 此方式测试无问题
|
|
268
|
+
log(`清理challenge痕迹,length:${clearTasks.length}`);
|
|
269
|
+
try {
|
|
270
|
+
// eslint-disable-next-line no-await-in-loop
|
|
271
|
+
await runAllPromise(clearTasks);
|
|
272
|
+
}
|
|
273
|
+
catch (e) {
|
|
274
|
+
log('清理challenge失败');
|
|
275
|
+
log(e);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
253
279
|
}
|
|
254
|
-
|
|
280
|
+
}
|
|
281
|
+
finally {
|
|
282
|
+
if (client.opts.sslProvider === 'google') {
|
|
283
|
+
// google 相同的域名txt记录是一样的,不能提前删除,否则校验失败,报错如下
|
|
284
|
+
// Error: The TXT record retrieved from _acme-challenge.bbc.handsfree.work.
|
|
285
|
+
// at the time the challenge was validated did not contain JshHVu7dt_DT6uYILWhokHefFVad2Q6Mw1L-fNZFcq8
|
|
286
|
+
// (the base64url-encoded SHA-256 digest of RlJZNBR0LWnxNK_xd2zqtYVvCiNJOKJ3J1NmCjU_9BjaUJgL3k-qSpIhQ-uF4FBS.NRyqT8fRiq6THzzrvkgzgR5Xai2LsA2SyGLAq_wT3qc).
|
|
287
|
+
// See https://tools.ietf.org/html/rfc8555#section-8.4 for more information.
|
|
255
288
|
log(`清理challenge痕迹,length:${clearTasks.length}`);
|
|
256
289
|
try {
|
|
257
|
-
|
|
290
|
+
// eslint-disable-next-line no-await-in-loop
|
|
258
291
|
await runAllPromise(clearTasks);
|
|
259
292
|
}
|
|
260
293
|
catch (e) {
|
|
@@ -263,6 +296,7 @@ module.exports = async (client, userOpts) => {
|
|
|
263
296
|
}
|
|
264
297
|
}
|
|
265
298
|
}
|
|
299
|
+
|
|
266
300
|
log('challenge结束');
|
|
267
301
|
|
|
268
302
|
// log('[auto] Waiting for challenge valid status');
|
package/src/axios.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Axios instance
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
4
|
const axios = require('axios');
|
|
6
5
|
const { parseRetryAfterHeader } = require('./util');
|
|
7
6
|
const { log } = require('./logger');
|
|
8
7
|
const pkg = require('./../package.json');
|
|
8
|
+
const Agents = require('./agents');
|
|
9
9
|
|
|
10
10
|
const { AxiosError } = axios;
|
|
11
11
|
|
|
@@ -24,8 +24,8 @@ instance.defaults.acmeSettings = {
|
|
|
24
24
|
httpsChallengePort: 443,
|
|
25
25
|
tlsAlpnChallengePort: 443,
|
|
26
26
|
|
|
27
|
-
retryMaxAttempts:
|
|
28
|
-
retryDefaultDelay:
|
|
27
|
+
retryMaxAttempts: 3,
|
|
28
|
+
retryDefaultDelay: 3,
|
|
29
29
|
};
|
|
30
30
|
// instance.defaults.proxy = {
|
|
31
31
|
// host: '192.168.34.139',
|
|
@@ -56,19 +56,26 @@ function isRetryableError(error) {
|
|
|
56
56
|
|
|
57
57
|
/* https://github.com/axios/axios/blob/main/lib/core/settle.js */
|
|
58
58
|
function validateStatus(response) {
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
if (!response) {
|
|
60
|
+
return new Error('Response is undefined');
|
|
61
|
+
}
|
|
62
|
+
let validator = null;
|
|
63
|
+
if (response.config) {
|
|
64
|
+
validator = response.config.retryValidateStatus;
|
|
65
|
+
}
|
|
61
66
|
if (!response.status || !validator || validator(response.status)) {
|
|
62
67
|
return response;
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
|
|
70
|
+
const err = new AxiosError(
|
|
66
71
|
`Request failed with status code ${response.status}`,
|
|
67
72
|
(Math.floor(response.status / 100) === 4) ? AxiosError.ERR_BAD_REQUEST : AxiosError.ERR_BAD_RESPONSE,
|
|
68
73
|
response.config,
|
|
69
74
|
response.request,
|
|
70
75
|
response,
|
|
71
76
|
);
|
|
77
|
+
|
|
78
|
+
throw new Agents.HttpError(err);
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
/* Pass all responses through the error interceptor */
|
|
@@ -76,8 +83,17 @@ instance.interceptors.request.use((config) => {
|
|
|
76
83
|
if (!('retryValidateStatus' in config)) {
|
|
77
84
|
config.retryValidateStatus = config.validateStatus;
|
|
78
85
|
}
|
|
79
|
-
|
|
80
86
|
config.validateStatus = () => false;
|
|
87
|
+
|
|
88
|
+
const agents = Agents.getGlobalAgents();
|
|
89
|
+
// if (config.skipSslVerify) {
|
|
90
|
+
// logger.info('跳过SSL验证');
|
|
91
|
+
// agents = createAgent({ rejectUnauthorized: false } as any);
|
|
92
|
+
// }
|
|
93
|
+
// delete config.skipSslVerify;
|
|
94
|
+
config.httpsAgent = agents.httpsAgent;
|
|
95
|
+
config.httpAgent = agents.httpAgent;
|
|
96
|
+
config.proxy = false; // 必须 否则还会走一层代理,
|
|
81
97
|
return config;
|
|
82
98
|
});
|
|
83
99
|
|
|
@@ -86,7 +102,7 @@ instance.interceptors.response.use(null, async (error) => {
|
|
|
86
102
|
const { config, response } = error;
|
|
87
103
|
|
|
88
104
|
if (!config) {
|
|
89
|
-
return Promise.reject(error);
|
|
105
|
+
return Promise.reject(new Agents.HttpError(error));
|
|
90
106
|
}
|
|
91
107
|
|
|
92
108
|
/* Pick up errors we want to retry */
|
|
@@ -115,6 +131,9 @@ instance.interceptors.response.use(null, async (error) => {
|
|
|
115
131
|
}
|
|
116
132
|
}
|
|
117
133
|
|
|
134
|
+
if (!response) {
|
|
135
|
+
return Promise.reject(new Agents.HttpError(error));
|
|
136
|
+
}
|
|
118
137
|
/* Validate and return response */
|
|
119
138
|
return validateStatus(response);
|
|
120
139
|
});
|
package/src/client.js
CHANGED
package/src/http.js
CHANGED
|
@@ -3,21 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const { createHmac, createSign, constants: { RSA_PKCS1_PADDING } } = require('crypto');
|
|
6
|
-
const { HttpsProxyAgent } = require('https-proxy-agent');
|
|
7
6
|
const { getJwk } = require('./crypto');
|
|
8
7
|
const { log } = require('./logger');
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
|
|
12
|
-
let httpsAgent = null;
|
|
13
|
-
if (httpsProxy) {
|
|
14
|
-
httpsAgent = new HttpsProxyAgent(httpsProxy);
|
|
15
|
-
log(`use https_proxy:${httpsProxy}`);
|
|
16
|
-
}
|
|
17
|
-
const axios = axios1.create({
|
|
18
|
-
proxy: false,
|
|
19
|
-
httpsAgent,
|
|
20
|
-
});
|
|
8
|
+
const axios = require('./axios');
|
|
21
9
|
|
|
22
10
|
/**
|
|
23
11
|
* ACME HTTP client
|
package/src/index.js
CHANGED
package/src/logger.js
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export type UrlMapping={
|
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
export interface ClientOptions {
|
|
40
|
+
sslProvider:string;
|
|
40
41
|
directoryUrl: string;
|
|
41
42
|
accountKey: PrivateKeyBuffer | PrivateKeyString;
|
|
42
43
|
accountUrl?: string;
|
|
@@ -192,6 +193,7 @@ export const forge: CryptoLegacyInterface;
|
|
|
192
193
|
|
|
193
194
|
export const axios: AxiosInstance;
|
|
194
195
|
|
|
196
|
+
export const agents: any;
|
|
195
197
|
/**
|
|
196
198
|
* Logger
|
|
197
199
|
*/
|