acme-client
Advanced tools
Comparing version 3.2.0 to 3.2.1
@@ -1,4 +0,9 @@ | ||
**Notice: [On November 1st, 2019 Let's Encrypt will remove support for unauthenticated GETs from the v2 API](https://community.letsencrypt.org/t/acme-v2-scheduled-deprecation-of-unauthenticated-resource-gets/74380). Please update to `acme-client >= v3.2.0` or `>= v2.3.1` before this date to avoid being affected by this API change.** | ||
**Notice:** [On November 1st, 2020 Let's Encrypt will remove support for unauthenticated GETs from the v2 API](https://community.letsencrypt.org/t/acme-v2-scheduled-deprecation-of-unauthenticated-resource-gets/74380). Please update to `acme-client >= v3.2.0` or `>= v2.3.1` before this date to avoid being affected by this API change. | ||
## v3.2.1 (2019-11-14) | ||
* `added` New option `skipChallengeVerification` added to `auto()` to bypass internal challenge verification | ||
## v3.2.0 (2019-08-26) | ||
@@ -5,0 +10,0 @@ |
@@ -125,3 +125,3 @@ ## Objects | ||
| [data.keySize] | <code>number</code> | Size of newly created private key, default: `2048` | | ||
| [data.commonName] | <code>string</code> | default: `localhost` | | ||
| [data.commonName] | <code>string</code> | | | ||
| [data.altNames] | <code>array</code> | default: `[]` | | ||
@@ -128,0 +128,0 @@ | [data.country] | <code>string</code> | | |
@@ -133,3 +133,3 @@ ## Objects | ||
| [data.keySize] | <code>number</code> | Size of newly created private key, default: `2048` | | ||
| [data.commonName] | <code>string</code> | default: `localhost` | | ||
| [data.commonName] | <code>string</code> | | | ||
| [data.altNames] | <code>array</code> | default: `[]` | | ||
@@ -136,0 +136,0 @@ | [data.country] | <code>string</code> | | |
@@ -87,3 +87,3 @@ /** | ||
const challenges = { authz }; | ||
const { challenges } = authz; | ||
@@ -90,0 +90,0 @@ /* Just select any challenge */ |
@@ -5,3 +5,3 @@ { | ||
"author": "nmorsman", | ||
"version": "3.2.0", | ||
"version": "3.2.1", | ||
"main": "src/index.js", | ||
@@ -15,3 +15,3 @@ "license": "MIT", | ||
"debug": "^4.1.1", | ||
"node-forge": "^0.8.4", | ||
"node-forge": "^0.9.1", | ||
"openssl-wrapper": "^0.3.0", | ||
@@ -28,3 +28,3 @@ "tempfile": "^3.0.0" | ||
"mocha": "^6.1.4", | ||
"nock": "^10.0.6", | ||
"nock": "^11.7.0", | ||
"uuid": "^3.3.3" | ||
@@ -31,0 +31,0 @@ }, |
@@ -7,18 +7,30 @@ # acme-client [![CircleCI](https://circleci.com/gh/publishlab/node-acme-client.svg?style=svg)](https://circleci.com/gh/publishlab/node-acme-client) | ||
ACME specification: [https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md](https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md) | ||
- RFC 8555 - Automatic Certificate Management Environment (ACME): [https://tools.ietf.org/html/rfc8555](https://tools.ietf.org/html/rfc8555) | ||
- Boulder divergences from ACME: [https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md](https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md) | ||
Information on how the Boulder/Let's Encrypt API diverges from the ACME spec: | ||
[https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md](https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md) | ||
### ACME compatibility | ||
| acme-client | API | Style | | ||
| ------------- | --------- | --------- | | ||
| >= v2.x | ACMEv2 | Promise | | ||
| == v1.x | ACMEv1 | callback | | ||
| acme-client | API | Style | Node.js | | ||
| ------------- | --------- | --------- | ------- | | ||
| v3.x | ACMEv2 | Promise | >= v8 | | ||
| v2.x | ACMEv2 | Promise | >= v4 | | ||
| v1.x | ACMEv1 | callback | >= v4 | | ||
**Notice: [On November 1st, 2019 Let's Encrypt will remove support for unauthenticated GETs from the v2 API](https://community.letsencrypt.org/t/acme-v2-scheduled-deprecation-of-unauthenticated-resource-gets/74380). Please update to `acme-client >= v3.2.0` or `>= v2.3.1` before this date to avoid being affected by this API change.** | ||
### Table of contents | ||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [Cryptography](#cryptography) | ||
- [acme.forge](#acmeforge) | ||
- [acme.openssl](#acmeopenssl) | ||
- [Auto mode](#auto-mode) | ||
- [Internal challenge verification](#internal-challenge-verification) | ||
- [API](#api) | ||
- [HTTP client defaults](#http-client-defaults) | ||
- [Debugging](#debugging) | ||
- [License](#license) | ||
## Installation | ||
@@ -30,5 +42,3 @@ | ||
**With the release of `acme-client v3.*` Node v4 and v6 are no longer supported.** | ||
## Usage | ||
@@ -61,5 +71,6 @@ | ||
### `acme.forge` -- [docs/forge.md](docs/forge.md) | ||
### `acme.forge` | ||
*Recommended when `node >= v10.12.0` or OpenSSL CLI dependency can not be met.* | ||
- Should be used when `node >= v10.12.0` or OpenSSL CLI dependency can not be met | ||
- API documentation: [docs/forge.md](docs/forge.md) | ||
@@ -85,5 +96,7 @@ Uses [node-forge](https://www.npmjs.com/package/node-forge), a pure JavaScript implementation of the TLS protocol. | ||
### `acme.openssl` -- [docs/openssl.md](docs/openssl.md) | ||
### `acme.openssl` | ||
*Recommended when `node < v10.12.0` and OpenSSL CLI dependency can be met.* | ||
- Can be used when `node < v10.12.0` and OpenSSL CLI dependency can be met | ||
- API documentation: [docs/openssl.md](docs/openssl.md) | ||
- __Warning:__ Will most likely be deprecated some time in the future | ||
@@ -132,2 +145,19 @@ Uses [openssl-wrapper](https://www.npmjs.com/package/openssl-wrapper) to execute commands using the OpenSSL CLI. | ||
### Internal challenge verification | ||
When ordering a certificate using auto mode, `acme-client` will first validate that challenges are satisfied internally before completing the challenge at the ACME provider. | ||
In some cases (firewalls, etc) this internal challenge verification might not be possible to complete. | ||
If internal challenge validation needs to travel through an HTTP proxy, see [HTTP client defaults](#http-client-defaults). | ||
To completely disable `acme-client`s internal challenge verification, enable `skipChallengeVerification`: | ||
```js | ||
await client.auto({ | ||
..., | ||
skipChallengeVerification: true | ||
}) | ||
``` | ||
## API | ||
@@ -159,7 +189,7 @@ | ||
## HTTP defaults | ||
## HTTP client defaults | ||
This module uses [axios](https://github.com/axios/axios) when communicating with the ACME API, and exposes the instance through `.axios`. | ||
This module uses [axios](https://github.com/axios/axios) when communicating with the ACME HTTP API, and exposes the client instance through `.axios`. | ||
Should you, for example, need to change the default axios configuration to route requests through an HTTP proxy, this can be achieved as follows: | ||
For example, should you need to change the default axios configuration to route requests through an HTTP proxy, this can be achieved as follows: | ||
@@ -177,4 +207,4 @@ ```js | ||
* [https://github.com/axios/axios#request-config](https://github.com/axios/axios#request-config) | ||
* [https://github.com/axios/axios#custom-instance-defaults](https://github.com/axios/axios#custom-instance-defaults) | ||
- [https://github.com/axios/axios#request-config](https://github.com/axios/axios#request-config) | ||
- [https://github.com/axios/axios#custom-instance-defaults](https://github.com/axios/axios#custom-instance-defaults) | ||
@@ -181,0 +211,0 @@ |
@@ -13,2 +13,3 @@ /** | ||
termsOfServiceAgreed: false, | ||
skipChallengeVerification: false, | ||
challengePriority: ['http-01', 'dns-01'], | ||
@@ -112,5 +113,13 @@ challengeCreateFn: async () => { throw new Error('Missing challengeCreateFn()'); }, | ||
/* Verify challenge and wait for valid status */ | ||
debug(`[auto] [${d}] Verifying challenge and waiting for valid status`); | ||
await client.verifyChallenge(authz, challenge); | ||
/* Challenge verification */ | ||
if (userOpts.skipChallengeVerification === true) { | ||
debug(`[auto] [${d}] Skipping challenge verification since skipChallengeVerification=true`); | ||
} | ||
else { | ||
debug(`[auto] [${d}] Running challenge verification`); | ||
await client.verifyChallenge(authz, challenge); | ||
} | ||
/* Complete challenge and wait for valid status */ | ||
debug(`[auto] [${d}] Completing challenge with ACME provider and waiting for valid status`); | ||
await client.completeChallenge(challenge); | ||
@@ -117,0 +126,0 @@ await client.waitForValidStatus(challenge); |
@@ -307,3 +307,2 @@ /** | ||
/** | ||
@@ -456,2 +455,3 @@ * https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#dns-challenge | ||
* @param {boolean} [opts.termsOfServiceAgreed] Agree to Terms of Service, default: `false` | ||
* @param {boolean} [opts.skipChallengeVerification] Skip internal challenge verification before notifying ACME provider, default: `false` | ||
* @param {string[]} [opts.challengePriority] Array defining challenge type priority, default: `['http-01', 'dns-01']` | ||
@@ -458,0 +458,0 @@ * @returns {Promise<string>} Certificate |
@@ -60,3 +60,11 @@ /** | ||
async function challengeNoopFn() { | ||
return true; | ||
} | ||
async function challengeThrowFn() { | ||
throw new Error('oops'); | ||
} | ||
/** | ||
@@ -68,3 +76,9 @@ * Initialize client | ||
const accountKey = await acme.forge.createPrivateKey(); | ||
testClient = new acme.Client({ directoryUrl, accountKey }); | ||
testClient = new acme.Client({ | ||
directoryUrl, | ||
accountKey, | ||
backoffMin: 1000, | ||
backoffMax: 5000 | ||
}); | ||
}); | ||
@@ -74,2 +88,67 @@ | ||
/** | ||
* Invalid challenge response | ||
*/ | ||
it('should throw on invalid challenge response', async () => { | ||
const [, csr] = await acme.forge.createCsr({ | ||
commonName: `${uuid()}.example.com` | ||
}); | ||
await assert.isRejected(testClient.auto({ | ||
csr, | ||
termsOfServiceAgreed: true, | ||
challengeCreateFn: challengeNoopFn, | ||
challengeRemoveFn: challengeNoopFn | ||
}), /^authorization not found/i); | ||
}); | ||
it('should throw on invalid challenge response with opts.skipChallengeVerification=true', async () => { | ||
const [, csr] = await acme.forge.createCsr({ | ||
commonName: `${uuid()}.example.com` | ||
}); | ||
await assert.isRejected(testClient.auto({ | ||
csr, | ||
termsOfServiceAgreed: true, | ||
skipChallengeVerification: true, | ||
challengeCreateFn: challengeNoopFn, | ||
challengeRemoveFn: challengeNoopFn | ||
})); | ||
}); | ||
/** | ||
* Challenge function exceptions | ||
*/ | ||
it('should throw on challengeCreate exception', async () => { | ||
const [, csr] = await acme.forge.createCsr({ | ||
commonName: `${uuid()}.example.com` | ||
}); | ||
await assert.isRejected(testClient.auto({ | ||
csr, | ||
termsOfServiceAgreed: true, | ||
challengeCreateFn: challengeThrowFn, | ||
challengeRemoveFn: challengeNoopFn | ||
}), /^oops$/); | ||
}); | ||
it('should not throw on challengeRemove exception', async () => { | ||
const [, csr] = await acme.forge.createCsr({ | ||
commonName: `${uuid()}.example.com` | ||
}); | ||
const cert = await testClient.auto({ | ||
csr, | ||
termsOfServiceAgreed: true, | ||
challengeCreateFn, | ||
challengeRemoveFn: challengeThrowFn | ||
}); | ||
assert.isString(cert); | ||
}); | ||
/** | ||
* Order certificates | ||
@@ -128,3 +207,19 @@ */ | ||
it('should order certificate with opts.skipChallengeVerification=true', async () => { | ||
const [, csr] = await acme.forge.createCsr({ | ||
commonName: `${uuid()}.example.com` | ||
}); | ||
const cert = await testClient.auto({ | ||
csr, | ||
termsOfServiceAgreed: true, | ||
skipChallengeVerification: true, | ||
challengeCreateFn, | ||
challengeRemoveFn | ||
}); | ||
assert.isString(cert); | ||
}); | ||
/** | ||
@@ -131,0 +226,0 @@ * Read certificates |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
143610
2810
217
+ Addednode-forge@0.9.2(transitive)
- Removednode-forge@0.8.5(transitive)
Updatednode-forge@^0.9.1