mirror of https://github.com/certd/certd
🔱: [acme] sync upgrade with 3 commits [trident-sync]
Bump v5.3.0 Example for dns-01pull/29/head
parent
80cd1bfc8e
commit
960f61d158
|
@ -1,6 +1,6 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## v5.3.0
|
## v5.3.0 (2024-02-05)
|
||||||
|
|
||||||
* `added` Support and tests for satisfying `tls-alpn-01` challenges
|
* `added` Support and tests for satisfying `tls-alpn-01` challenges
|
||||||
* `changed` Replace `jsrsasign` with `@peculiar/x509` for certificate and CSR generation and parsing
|
* `changed` Replace `jsrsasign` with `@peculiar/x509` for certificate and CSR generation and parsing
|
||||||
|
|
|
@ -132,7 +132,7 @@ catch (e) {
|
||||||
### acmeClient.createAccount([data]) ⇒ <code>Promise.<object></code>
|
### acmeClient.createAccount([data]) ⇒ <code>Promise.<object></code>
|
||||||
Create a new account
|
Create a new account
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.3
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.3
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<object></code> - Account
|
**Returns**: <code>Promise.<object></code> - Account
|
||||||
|
@ -161,7 +161,7 @@ const account = await client.createAccount({
|
||||||
### acmeClient.updateAccount([data]) ⇒ <code>Promise.<object></code>
|
### acmeClient.updateAccount([data]) ⇒ <code>Promise.<object></code>
|
||||||
Update existing account
|
Update existing account
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.3.2
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.3.2
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<object></code> - Account
|
**Returns**: <code>Promise.<object></code> - Account
|
||||||
|
@ -182,7 +182,7 @@ const account = await client.updateAccount({
|
||||||
### acmeClient.updateAccountKey(newAccountKey, [data]) ⇒ <code>Promise.<object></code>
|
### acmeClient.updateAccountKey(newAccountKey, [data]) ⇒ <code>Promise.<object></code>
|
||||||
Update account private key
|
Update account private key
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.3.5
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.3.5
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<object></code> - Account
|
**Returns**: <code>Promise.<object></code> - Account
|
||||||
|
@ -203,7 +203,7 @@ const result = await client.updateAccountKey(newAccountKey);
|
||||||
### acmeClient.createOrder(data) ⇒ <code>Promise.<object></code>
|
### acmeClient.createOrder(data) ⇒ <code>Promise.<object></code>
|
||||||
Create a new order
|
Create a new order
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.4
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.4
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<object></code> - Order
|
**Returns**: <code>Promise.<object></code> - Order
|
||||||
|
@ -227,7 +227,7 @@ const order = await client.createOrder({
|
||||||
### acmeClient.getOrder(order) ⇒ <code>Promise.<object></code>
|
### acmeClient.getOrder(order) ⇒ <code>Promise.<object></code>
|
||||||
Refresh order object from CA
|
Refresh order object from CA
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.4
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.4
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<object></code> - Order
|
**Returns**: <code>Promise.<object></code> - Order
|
||||||
|
@ -246,7 +246,7 @@ const result = await client.getOrder(order);
|
||||||
### acmeClient.finalizeOrder(order, csr) ⇒ <code>Promise.<object></code>
|
### acmeClient.finalizeOrder(order, csr) ⇒ <code>Promise.<object></code>
|
||||||
Finalize order
|
Finalize order
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.4
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.4
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<object></code> - Order
|
**Returns**: <code>Promise.<object></code> - Order
|
||||||
|
@ -268,7 +268,7 @@ const result = await client.finalizeOrder(order, csr);
|
||||||
### acmeClient.getAuthorizations(order) ⇒ <code>Promise.<Array.<object>></code>
|
### acmeClient.getAuthorizations(order) ⇒ <code>Promise.<Array.<object>></code>
|
||||||
Get identifier authorizations from order
|
Get identifier authorizations from order
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.5
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.5
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<Array.<object>></code> - Authorizations
|
**Returns**: <code>Promise.<Array.<object>></code> - Authorizations
|
||||||
|
@ -292,7 +292,7 @@ authorizations.forEach((authz) => {
|
||||||
### acmeClient.deactivateAuthorization(authz) ⇒ <code>Promise.<object></code>
|
### acmeClient.deactivateAuthorization(authz) ⇒ <code>Promise.<object></code>
|
||||||
Deactivate identifier authorization
|
Deactivate identifier authorization
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.5.2
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.2
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<object></code> - Authorization
|
**Returns**: <code>Promise.<object></code> - Authorization
|
||||||
|
@ -312,7 +312,7 @@ const result = await client.deactivateAuthorization(authz);
|
||||||
### acmeClient.getChallengeKeyAuthorization(challenge) ⇒ <code>Promise.<string></code>
|
### acmeClient.getChallengeKeyAuthorization(challenge) ⇒ <code>Promise.<string></code>
|
||||||
Get key authorization for ACME challenge
|
Get key authorization for ACME challenge
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-8.1
|
https://datatracker.ietf.org/doc/html/rfc8555#section-8.1
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<string></code> - Key authorization
|
**Returns**: <code>Promise.<string></code> - Key authorization
|
||||||
|
@ -353,7 +353,7 @@ await client.verifyChallenge(authz, challenge);
|
||||||
### acmeClient.completeChallenge(challenge) ⇒ <code>Promise.<object></code>
|
### acmeClient.completeChallenge(challenge) ⇒ <code>Promise.<object></code>
|
||||||
Notify CA that challenge has been completed
|
Notify CA that challenge has been completed
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.5.1
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.1
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<object></code> - Challenge
|
**Returns**: <code>Promise.<object></code> - Challenge
|
||||||
|
@ -373,7 +373,7 @@ const result = await client.completeChallenge(challenge);
|
||||||
### acmeClient.waitForValidStatus(item) ⇒ <code>Promise.<object></code>
|
### acmeClient.waitForValidStatus(item) ⇒ <code>Promise.<object></code>
|
||||||
Wait for ACME provider to verify status on a order, authorization or challenge
|
Wait for ACME provider to verify status on a order, authorization or challenge
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.5.1
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.1
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<object></code> - Valid order, authorization or challenge
|
**Returns**: <code>Promise.<object></code> - Valid order, authorization or challenge
|
||||||
|
@ -389,7 +389,7 @@ const challenge = { ... };
|
||||||
await client.waitForValidStatus(challenge);
|
await client.waitForValidStatus(challenge);
|
||||||
```
|
```
|
||||||
**Example**
|
**Example**
|
||||||
Wait for valid authoriation status
|
Wait for valid authorization status
|
||||||
```js
|
```js
|
||||||
const authz = { ... };
|
const authz = { ... };
|
||||||
await client.waitForValidStatus(authz);
|
await client.waitForValidStatus(authz);
|
||||||
|
@ -405,7 +405,7 @@ await client.waitForValidStatus(order);
|
||||||
### acmeClient.getCertificate(order, [preferredChain]) ⇒ <code>Promise.<string></code>
|
### acmeClient.getCertificate(order, [preferredChain]) ⇒ <code>Promise.<string></code>
|
||||||
Get certificate from ACME order
|
Get certificate from ACME order
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.4.2
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.4.2
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
**Returns**: <code>Promise.<string></code> - Certificate
|
**Returns**: <code>Promise.<string></code> - Certificate
|
||||||
|
@ -432,7 +432,7 @@ const certificate = await client.getCertificate(order, 'DST Root CA X3');
|
||||||
### acmeClient.revokeCertificate(cert, [data]) ⇒ <code>Promise</code>
|
### acmeClient.revokeCertificate(cert, [data]) ⇒ <code>Promise</code>
|
||||||
Revoke certificate
|
Revoke certificate
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.6
|
https://datatracker.ietf.org/doc/html/rfc8555#section-7.6
|
||||||
|
|
||||||
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<dd><p>Get a JSON Web Key derived from a RSA or ECDSA key</p>
|
<dd><p>Get a JSON Web Key derived from a RSA or ECDSA key</p>
|
||||||
<p><a href="https://datatracker.ietf.org/doc/html/rfc7517">https://datatracker.ietf.org/doc/html/rfc7517</a></p>
|
<p><a href="https://datatracker.ietf.org/doc/html/rfc7517">https://datatracker.ietf.org/doc/html/rfc7517</a></p>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><a href="#splitPemChain">splitPemChain(chainPem)</a> ⇒ <code>array</code></dt>
|
<dt><a href="#splitPemChain">splitPemChain(chainPem)</a> ⇒ <code>Array.<string></code></dt>
|
||||||
<dd><p>Split chain of PEM encoded objects from string into array</p>
|
<dd><p>Split chain of PEM encoded objects from string into array</p>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><a href="#getPemBodyAsB64u">getPemBodyAsB64u(pem)</a> ⇒ <code>string</code></dt>
|
<dt><a href="#getPemBodyAsB64u">getPemBodyAsB64u(pem)</a> ⇒ <code>string</code></dt>
|
||||||
|
@ -42,6 +42,13 @@ If multiple certificates are chained, the first will be read</p>
|
||||||
<dt><a href="#createCsr">createCsr(data, [keyPem])</a> ⇒ <code>Promise.<Array.<buffer>></code></dt>
|
<dt><a href="#createCsr">createCsr(data, [keyPem])</a> ⇒ <code>Promise.<Array.<buffer>></code></dt>
|
||||||
<dd><p>Create a Certificate Signing Request</p>
|
<dd><p>Create a Certificate Signing Request</p>
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt><a href="#createAlpnCertificate">createAlpnCertificate(authz, keyAuthorization, [keyPem])</a> ⇒ <code>Promise.<Array.<buffer>></code></dt>
|
||||||
|
<dd><p>Create a self-signed ALPN certificate for TLS-ALPN-01 challenges</p>
|
||||||
|
<p><a href="https://datatracker.ietf.org/doc/html/rfc8737">https://datatracker.ietf.org/doc/html/rfc8737</a></p>
|
||||||
|
</dd>
|
||||||
|
<dt><a href="#isAlpnCertificateAuthorizationValid">isAlpnCertificateAuthorizationValid(certPem, keyAuthorization)</a> ⇒ <code>boolean</code></dt>
|
||||||
|
<dd><p>Validate that a ALPN certificate contains the expected key authorization</p>
|
||||||
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<a name="crypto"></a>
|
<a name="crypto"></a>
|
||||||
|
@ -138,11 +145,11 @@ const jwk = acme.crypto.getJwk(privateKey);
|
||||||
```
|
```
|
||||||
<a name="splitPemChain"></a>
|
<a name="splitPemChain"></a>
|
||||||
|
|
||||||
## splitPemChain(chainPem) ⇒ <code>array</code>
|
## splitPemChain(chainPem) ⇒ <code>Array.<string></code>
|
||||||
Split chain of PEM encoded objects from string into array
|
Split chain of PEM encoded objects from string into array
|
||||||
|
|
||||||
**Kind**: global function
|
**Kind**: global function
|
||||||
**Returns**: <code>array</code> - Array of PEM objects including headers
|
**Returns**: <code>Array.<string></code> - Array of PEM objects including headers
|
||||||
|
|
||||||
| Param | Type | Description |
|
| Param | Type | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
@ -219,14 +226,14 @@ Create a Certificate Signing Request
|
||||||
| data | <code>object</code> | |
|
| data | <code>object</code> | |
|
||||||
| [data.keySize] | <code>number</code> | Size of newly created RSA private key modulus in bits, default: `2048` |
|
| [data.keySize] | <code>number</code> | Size of newly created RSA private key modulus in bits, default: `2048` |
|
||||||
| [data.commonName] | <code>string</code> | FQDN of your server |
|
| [data.commonName] | <code>string</code> | FQDN of your server |
|
||||||
| [data.altNames] | <code>array</code> | SAN (Subject Alternative Names), default: `[]` |
|
| [data.altNames] | <code>Array.<string></code> | SAN (Subject Alternative Names), default: `[]` |
|
||||||
| [data.country] | <code>string</code> | 2 letter country code |
|
| [data.country] | <code>string</code> | 2 letter country code |
|
||||||
| [data.state] | <code>string</code> | State or province |
|
| [data.state] | <code>string</code> | State or province |
|
||||||
| [data.locality] | <code>string</code> | City |
|
| [data.locality] | <code>string</code> | City |
|
||||||
| [data.organization] | <code>string</code> | Organization name |
|
| [data.organization] | <code>string</code> | Organization name |
|
||||||
| [data.organizationUnit] | <code>string</code> | Organizational unit name |
|
| [data.organizationUnit] | <code>string</code> | Organizational unit name |
|
||||||
| [data.emailAddress] | <code>string</code> | Email address |
|
| [data.emailAddress] | <code>string</code> | Email address |
|
||||||
| [keyPem] | <code>string</code> | PEM encoded CSR private key |
|
| [keyPem] | <code>buffer</code> \| <code>string</code> | PEM encoded CSR private key |
|
||||||
|
|
||||||
**Example**
|
**Example**
|
||||||
Create a Certificate Signing Request
|
Create a Certificate Signing Request
|
||||||
|
@ -265,3 +272,42 @@ const certificateKey = await acme.crypto.createPrivateEcdsaKey();
|
||||||
const [, certificateRequest] = await acme.crypto.createCsr({
|
const [, certificateRequest] = await acme.crypto.createCsr({
|
||||||
commonName: 'test.example.com'
|
commonName: 'test.example.com'
|
||||||
}, certificateKey);
|
}, certificateKey);
|
||||||
|
<a name="createAlpnCertificate"></a>
|
||||||
|
|
||||||
|
## createAlpnCertificate(authz, keyAuthorization, [keyPem]) ⇒ <code>Promise.<Array.<buffer>></code>
|
||||||
|
Create a self-signed ALPN certificate for TLS-ALPN-01 challenges
|
||||||
|
|
||||||
|
https://datatracker.ietf.org/doc/html/rfc8737
|
||||||
|
|
||||||
|
**Kind**: global function
|
||||||
|
**Returns**: <code>Promise.<Array.<buffer>></code> - [privateKey, certificate]
|
||||||
|
|
||||||
|
| Param | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| authz | <code>object</code> | Identifier authorization |
|
||||||
|
| keyAuthorization | <code>string</code> | Challenge key authorization |
|
||||||
|
| [keyPem] | <code>buffer</code> \| <code>string</code> | PEM encoded CSR private key |
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
Create a ALPN certificate
|
||||||
|
```js
|
||||||
|
const [alpnKey, alpnCertificate] = await acme.crypto.createAlpnCertificate(authz, keyAuthorization);
|
||||||
|
```
|
||||||
|
**Example**
|
||||||
|
Create a ALPN certificate with ECDSA private key
|
||||||
|
```js
|
||||||
|
const alpnKey = await acme.crypto.createPrivateEcdsaKey();
|
||||||
|
const [, alpnCertificate] = await acme.crypto.createAlpnCertificate(authz, keyAuthorization, alpnKey);
|
||||||
|
<a name="isAlpnCertificateAuthorizationValid"></a>
|
||||||
|
|
||||||
|
## isAlpnCertificateAuthorizationValid(certPem, keyAuthorization) ⇒ <code>boolean</code>
|
||||||
|
Validate that a ALPN certificate contains the expected key authorization
|
||||||
|
|
||||||
|
**Kind**: global function
|
||||||
|
**Returns**: <code>boolean</code> - True when valid
|
||||||
|
|
||||||
|
| Param | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| certPem | <code>buffer</code> \| <code>string</code> | PEM encoded certificate |
|
||||||
|
| keyAuthorization | <code>string</code> | Expected challenge key authorization |
|
||||||
|
|
||||||
|
|
|
@ -209,7 +209,7 @@ Create a Certificate Signing Request
|
||||||
| data | <code>object</code> | |
|
| data | <code>object</code> | |
|
||||||
| [data.keySize] | <code>number</code> | Size of newly created private key, default: `2048` |
|
| [data.keySize] | <code>number</code> | Size of newly created private key, default: `2048` |
|
||||||
| [data.commonName] | <code>string</code> | |
|
| [data.commonName] | <code>string</code> | |
|
||||||
| [data.altNames] | <code>array</code> | default: `[]` |
|
| [data.altNames] | <code>Array.<string></code> | default: `[]` |
|
||||||
| [data.country] | <code>string</code> | |
|
| [data.country] | <code>string</code> | |
|
||||||
| [data.state] | <code>string</code> | |
|
| [data.state] | <code>string</code> | |
|
||||||
| [data.locality] | <code>string</code> | |
|
| [data.locality] | <code>string</code> | |
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# dns-01
|
||||||
|
|
||||||
|
The greatest benefit of `dns-01` is that it is the only challenge type that can be used to issue ACME wildcard certificates, however it also has a few downsides. Your DNS provider needs to offer some sort of API you can use to automate adding and removing the required `TXT` DNS records. Additionally, solving DNS challenges will be much slower than the other challenge types because of DNS propagation delays.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
When solving `dns-01` challenges, you prove ownership of a domain by serving a specific payload within a specific DNS `TXT` record from the domains authoritative nameservers. The ACME authority provides the client with a token that, along with a thumbprint of your account key, is used to generate a `base64url` encoded `SHA256` digest. This payload is then placed as a `TXT` record under DNS name `_acme-challenge.$YOUR_DOMAIN`.
|
||||||
|
|
||||||
|
Once the order is finalized, the ACME authority will lookup your domains DNS record to verify that the payload is correct. `CNAME` and `NS` records are followed, should you wish to delegate challenge response to another DNS zone or record.
|
||||||
|
|
||||||
|
## Pros and cons
|
||||||
|
|
||||||
|
* Only challenge type that can be used to issue wildcard certificates
|
||||||
|
* Your DNS provider needs to supply an API that can be used
|
||||||
|
* DNS propagation time may be slow
|
||||||
|
* Useful in instances where both port 80 and 443 are unavailable
|
||||||
|
|
||||||
|
## External links
|
||||||
|
|
||||||
|
* [https://letsencrypt.org/docs/challenge-types/#dns-01-challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge)
|
||||||
|
* [https://datatracker.ietf.org/doc/html/rfc8555#section-8.4](https://datatracker.ietf.org/doc/html/rfc8555#section-8.4)
|
|
@ -0,0 +1,92 @@
|
||||||
|
/**
|
||||||
|
* Example using dns-01 challenge to generate certificates
|
||||||
|
*
|
||||||
|
* NOTE: This example is incomplete as the DNS challenge response implementation
|
||||||
|
* will be specific to your DNS providers API.
|
||||||
|
*
|
||||||
|
* NOTE: This example does not order certificates on-demand, as solving dns-01
|
||||||
|
* will likely be too slow for it to make sense. Instead, it orders a wildcard
|
||||||
|
* certificate on init before starting the HTTPS server as a demonstration.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const https = require('https');
|
||||||
|
const acme = require('./../../');
|
||||||
|
|
||||||
|
const HTTPS_SERVER_PORT = 443;
|
||||||
|
const WILDCARD_DOMAIN = 'example.com';
|
||||||
|
|
||||||
|
function log(m) {
|
||||||
|
process.stdout.write(`${(new Date()).toISOString()} ${m}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main
|
||||||
|
*/
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
/**
|
||||||
|
* Initialize ACME client
|
||||||
|
*/
|
||||||
|
|
||||||
|
log('Initializing ACME client');
|
||||||
|
const client = new acme.Client({
|
||||||
|
directoryUrl: acme.directory.letsencrypt.staging,
|
||||||
|
accountKey: await acme.crypto.createPrivateKey()
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order wildcard certificate
|
||||||
|
*/
|
||||||
|
|
||||||
|
log(`Creating CSR for ${WILDCARD_DOMAIN}`);
|
||||||
|
const [key, csr] = await acme.crypto.createCsr({
|
||||||
|
commonName: WILDCARD_DOMAIN,
|
||||||
|
altNames: [`*.${WILDCARD_DOMAIN}`]
|
||||||
|
});
|
||||||
|
|
||||||
|
log(`Ordering certificate for ${WILDCARD_DOMAIN}`);
|
||||||
|
const cert = await client.auto({
|
||||||
|
csr,
|
||||||
|
email: 'test@example.com',
|
||||||
|
termsOfServiceAgreed: true,
|
||||||
|
challengePriority: ['dns-01'],
|
||||||
|
challengeCreateFn: (authz, challenge, keyAuthorization) => {
|
||||||
|
/* TODO: Implement this */
|
||||||
|
log(`[TODO] Add TXT record key=_acme-challenge.${authz.identifier.value} value=${keyAuthorization}`);
|
||||||
|
},
|
||||||
|
challengeRemoveFn: (authz, challenge, keyAuthorization) => {
|
||||||
|
/* TODO: Implement this */
|
||||||
|
log(`[TODO] Remove TXT record key=_acme-challenge.${authz.identifier.value} value=${keyAuthorization}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
log(`Certificate for ${WILDCARD_DOMAIN} created successfully`);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTPS server
|
||||||
|
*/
|
||||||
|
|
||||||
|
const requestListener = (req, res) => {
|
||||||
|
log(`HTTP 200 ${req.headers.host}${req.url}`);
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end('Hello world\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
const httpsServer = https.createServer({
|
||||||
|
key,
|
||||||
|
cert
|
||||||
|
}, requestListener);
|
||||||
|
|
||||||
|
httpsServer.listen(HTTPS_SERVER_PORT, () => {
|
||||||
|
log(`HTTPS server listening on port ${HTTPS_SERVER_PORT}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
log(`[FATAL] ${e.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
})();
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "acme-client",
|
"name": "acme-client",
|
||||||
"description": "Simple and unopinionated ACME client",
|
"description": "Simple and unopinionated ACME client",
|
||||||
"author": "nmorsman",
|
"author": "nmorsman",
|
||||||
"version": "5.2.0",
|
"version": "5.3.0",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"types": "types/index.d.ts",
|
"types": "types/index.d.ts",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
Loading…
Reference in New Issue