@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 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.9",
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": "f36b6e382484ba8d07fa1718c438b097ad04c8da"
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
@@ -30,6 +30,7 @@ class AcmeApi {
30
30
  }
31
31
  }
32
32
  }
33
+ console.log(locationUrl, mapping);
33
34
  return locationUrl;
34
35
  }
35
36
 
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
- log(`开始challenge,共${allChallengePromises.length}组`);
237
- let i = 0;
238
- // eslint-disable-next-line no-restricted-syntax
239
- for (const challengePromises of allChallengePromises) {
240
- i += 1;
241
- log(`开始第${i}组`);
242
- if (opts.signal && opts.signal.aborted) {
243
- throw new Error('用户取消');
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
- try {
247
- // eslint-disable-next-line no-await-in-loop
248
- await runPromisePa(challengePromises);
249
- }
250
- catch (e) {
251
- log(`证书申请失败${e.message}`);
252
- throw e;
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
- finally {
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
- // eslint-disable-next-line no-await-in-loop
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: 5,
28
- retryDefaultDelay: 5,
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
- const validator = response.config.retryValidateStatus;
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
- throw new AxiosError(
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
@@ -558,6 +558,7 @@ class AcmeClient {
558
558
 
559
559
  const verifyFn = async (abort) => {
560
560
  if (this.opts.signal && this.opts.signal.aborted) {
561
+ abort();
561
562
  throw new Error('用户取消');
562
563
  }
563
564
 
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 axios1 = require('./axios');
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
@@ -39,6 +39,7 @@ exports.forge = require('./crypto/forge');
39
39
  */
40
40
 
41
41
  exports.axios = require('./axios');
42
+ exports.agents = require('./agents');
42
43
 
43
44
  /**
44
45
  * Logger
package/src/logger.js CHANGED
@@ -22,7 +22,7 @@ exports.setLogger = (fn) => {
22
22
  * @param {string} msg Message
23
23
  */
24
24
 
25
- exports.log = (msg) => {
26
- debug(msg);
27
- logger(msg);
25
+ exports.log = (...msg) => {
26
+ debug(...msg);
27
+ logger(...msg);
28
28
  };
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
  */