Skip to content

Instantly share code, notes, and snippets.

@melgish
Created March 26, 2022 15:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save melgish/f046c3f680e8047830812bcd2119c1f8 to your computer and use it in GitHub Desktop.
Save melgish/f046c3f680e8047830812bcd2119c1f8 to your computer and use it in GitHub Desktop.
Code to transform traefik's acme.json into certificate for node
/**
* 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