Created
March 26, 2022 15:10
-
-
Save melgish/f046c3f680e8047830812bcd2119c1f8 to your computer and use it in GitHub Desktop.
Code to transform traefik's acme.json into certificate for node
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Extract specific key and certificate from traefik acme.json file | |
*/ | |
const fs = require('fs/promises'); | |
/** | |
* Decodes found certificate | |
* @param {{certificate: string, key: string}} data Data to decode | |
* @returns {{certificate: string, key: string}} decoded data | |
*/ | |
const decode = ({ certificate, key }) => ({ | |
certificate: Buffer.from(certificate, 'base64').toString('ascii'), | |
key: Buffer.from(key, 'base64').toString('ascii'), | |
}); | |
/** | |
* Locate best matching certificate in the supplied file. | |
* @param {string} acmeFile file to search | |
* @param {string} domain domain to search for | |
* @returns {undefined | {certificate: string, key: string}} | |
* @throws on file or parse error. | |
*/ | |
const find = async (acmeFile, domain) => { | |
// Build lookup to map domain to it's certificate. Expects JSON : | |
// { | |
// "provider1": { | |
// "Certificates": [ | |
// { | |
// "certificate": "ZHoG3nz3coraa8A...", | |
// "key": "JQkFBS0NBBOUZWUzN...", | |
// "domain": { | |
// "main": "example.com", | |
// "sans": ["other.example.com"] | |
// } | |
// }, | |
// {...} | |
// ] | |
// }, | |
// "provider2": { ... } | |
// } | |
const text = await fs.readFile(acmeFile, 'utf-8'); | |
const certs = Object.values(JSON.parse(text)) | |
.reduce((out, d) => out.concat(d.Certificates), []) | |
.reduce((out, d) => { | |
const { | |
certificate, | |
domain: { main, sans = [] }, | |
key, | |
} = d; | |
// Add main certificate to lookup. | |
out[main] = { certificate, key }; | |
// Add alternate names to lookup. | |
sans.forEach((san) => (out[san] = out[main])); | |
return out; | |
}, []); | |
// Use lookup to locate best match. | |
if (certs[domain]) { | |
// Exact Match | |
return decode(certs[domain]); | |
} | |
// Look for wildcard match on parent domain. | |
domain = `*.${domain.split('.').slice(1).join('.')}`; | |
if (certs[domain]) { | |
// Wildcard match | |
return decode(certs[domain]); | |
} | |
// Wildcards only work one level deep, therefore not found | |
}; | |
/** | |
* Search supplied acme.json file for domain certificate with wildcard | |
* fallback. | |
* @param {string} acmeFile qualified path to file | |
* @param {string} domain domain to search for | |
* @returns { key: string, certificate: string } if found, otherwise undefine | |
*/ | |
export const getCertificate = async (acmeFile, domain) => { | |
return find(acmeFile, domain); | |
}; | |
// getCertificate('./acme.json', 'my.example.com').then(console.debug); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment