Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@cocreate/acme

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cocreate/acme - npm Package Compare versions

Comparing version 1.0.0 to 1.1.0

14

CHANGELOG.md

@@ -0,1 +1,15 @@

# [1.1.0](https://github.com/CoCreate-app/CoCreate-acme/compare/v1.0.0...v1.1.0) (2023-12-31)
### Bug Fixes
* filenames ([23735c4](https://github.com/CoCreate-app/CoCreate-acme/commit/23735c44f30cdd848959a3046ac40665302f38b6))
* Wraped in class to use with modules ([4ff5bea](https://github.com/CoCreate-app/CoCreate-acme/commit/4ff5beabf9aee2b53d41839d3c6f276344726cb3))
### Features
* emit certificateCreated ([eb5cdc6](https://github.com/CoCreate-app/CoCreate-acme/commit/eb5cdc6142c7bfd15f548bbdaf908c33e3f6c19f))
* store certificates using crud so other nodes can reused ([aea5f15](https://github.com/CoCreate-app/CoCreate-acme/commit/aea5f1557f358934f6b08c9f94a734af80a56f6e))
# 1.0.0 (2023-12-30)

@@ -2,0 +16,0 @@

2

package.json
{
"name": "@cocreate/acme",
"version": "1.0.0",
"version": "1.1.0",
"description": "An intergration with ACME and CoCreateJS.",

@@ -5,0 +5,0 @@ "keywords": [

@@ -1,131 +0,274 @@

const { Client } = require('acme-client');
const { Client, forge } = require('acme-client');
const fs = require('fs');
const email = 'mailto:ssl@cocreate.app';
const certificates = new Map()
const email = 'ssl@cocreate.app';
const keyPath = 'certificates/';
let client
const hosts = {}
async function init() {
if (!fs.existsSync(keyPath)) {
fs.mkdirSync(keyPath, { recursive: true }); // Create the directory if it doesn't exist
// Run this once per server to generate random constants
const DAYS = Math.floor(Math.random() * 7); // Random days between 0-6
const HOURS = Math.floor(Math.random() * 24); // Random hours between 0-23
const MINUTES = Math.floor(Math.random() * 60); // Random minutes between 0-59
// Store these constants in a configuration file, environment variable, or database
class CoCreateAcme {
constructor(crud) {
this.crud = crud
this.init().catch(err => {
console.error('Error initializing ACME client:', err);
// TODO: Handle initialization error (possibly retry or exit)
});
}
const accountKeyPath = keyPath + 'account.pem';
async init() {
if (!fs.existsSync(keyPath)) {
fs.mkdirSync(keyPath, { recursive: true }); // Create the directory if it doesn't exist
}
let accountKey;
let isNewAccount = false; // Flag to check if the account is new
const accountKeyPath = keyPath + 'account.pem';
// Check if the account key exists and load it; otherwise, create a new one
if (!fs.existsSync(accountKeyPath)) {
accountKey = await Client.forge.createPrivateKey();
fs.writeFileSync(accountKeyPath, accountKey); // Store the account key
fs.chmodSync(accountKeyPath, '400')
let accountKey = '';
let isNewAccount = false; // Flag to check if the account is new
isNewAccount = true; // New account, so will need to register it
} else {
// Load the existing account key
accountKey = fs.readFileSync(accountKeyPath, 'utf8');
}
// Check if the account key exists and load it; otherwise, create a new one
if (!fs.existsSync(accountKeyPath)) {
fs.writeFileSync(accountKeyPath, accountKey); // Store the account key
accountKey = await forge.createPrivateKey();
isNewAccount = true; // New account, so will need to register it
fs.writeFileSync(accountKeyPath, accountKey); // Store the account key
// fs.chmodSync(accountKeyPath, '400')
} else {
accountKey = fs.readFileSync(accountKeyPath, 'utf8');
}
// Initialize the ACME client with the account key
client = new Client({
directoryUrl: Client.directory.letsencrypt.staging,
accountKey: accountKey
});
// Initialize the ACME client with the account key
client = new Client({
directoryUrl: 'https://acme-staging-v02.api.letsencrypt.org/directory',
accountKey: accountKey
});
// Register the new account if it was just created
if (isNewAccount) {
await client.createAccount({
termsOfServiceAgreed: true,
contact: [email]
});
// Register the new account if it was just created
if (isNewAccount) {
try {
// Attempt to create an account
await client.createAccount({
termsOfServiceAgreed: true,
contact: ['mailto:' + email]
});
console.log("ACME account created successfully!");
} catch (error) {
fs.unlinkSync(accountKeyPath)
// Handle errors that occur during account creation
console.error("Error creating ACME account:", error.message);
// Depending on the type of error, you might want to retry, log the error, alert someone, etc.
}
}
}
}
async function requestCertificate(host, wildcard = false) {
/* Place your domain(s) here */
const domains = wildcard ? [host, `*.${host}`] : [host, `www.${host}`];
async requestCertificate(host, organization_id, wildcard = false) {
try {
/* Create certificate request */
const [key, csr] = await Client.forge.createCsr({
commonName: domains[0],
altNames: domains
});
const self = this
/* Request certificate */
const cert = await client.auto({
csr,
email, // Replace with your email
termsOfServiceAgreed: true,
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
/* Log the URL and content for the HTTP-01 challenge */
if (challenge.type === 'http-01') {
const challengeUrl = `http://${authz.identifier.value}/.well-known/acme-challenge/${challenge.token}`;
const keyAuth = keyAuthorization;
const hostKeyPath = keyPath + host + '/';
if (!fs.existsSync(hostKeyPath)) {
fs.mkdirSync(hostKeyPath, { recursive: true });
}
const domains = wildcard ? [host, `*.${host}`] : [host];
console.log('Please create a file accessible on:');
console.log(challengeUrl);
console.log('With the content:');
console.log(keyAuth);
/* Create certificate request */
const [key, csr] = await forge.createCsr({
commonName: domains[0],
altNames: domains
});
let file = {
"content-type": "text/plain",
"directory": "acme-challenge",
"host": [
authz.identifier.value
],
"name": challenge.token,
"organization_id": "652c8d62679eca03e0b116a7",
"path": "/.well-known/acme-challenge/",
"pathname": `/.well-known/acme-challenge/${challenge.token}`,
"public": "true",
"src": keyAuth
let challenge_id = ''
/* Request certificate */
const cert = await client.auto({
csr,
email: [email], // Replace with your email
termsOfServiceAgreed: true,
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
if (challenge.type === 'http-01') {
const httpChallenge = await self.crud.send({
method: 'object.create',
array: 'files',
object: {
"content-type": "text/plain",
"directory": "acme-challenge",
"host": [
authz.identifier.value
],
"name": challenge.token,
"organization_id": "652c8d62679eca03e0b116a7",
"path": "/.well-known/acme-challenge/",
"pathname": `/.well-known/acme-challenge/${challenge.token}`,
"public": "true",
"src": keyAuthorization
},
organization_id,
});
if (httpChallenge && httpChallenge.object && httpChallenge.object[0])
challenge_id = httpChallenge.object[0]._id
else
console.error('error creating challenge url')
} else if (challenge.type === 'dns-01') {
// Calculate the DNS TXT record value
const dnsRecordName = `_acme-challenge.${authz.identifier.value}`;
const dnsRecordValue = await client.getChallengeKeyAuthorization(challenge);
console.log(`Add this TXT record to your DNS:`);
console.log(`Name: ${dnsRecordName}`);
console.log(`Value: ${dnsRecordValue}`);
// Here, implement the logic to add the TXT record to your DNS
// await updateDnsTxtRecord(dnsRecordName, dnsRecordValue); // Hypothetical function to update DNS
}
},
challengeRemoveFn: async (authz, challenge, keyAuthorization) => {
/* Clean up challenge response here if necessary */
console.log(`Challenge removed for token: ${challenge.token}`);
if (challenge.type === 'http-01') {
self.crud.send({
method: 'object.delete',
array: 'files',
object: {
_id: challenge_id
},
organization_id,
});
} else if (challenge.type === 'dns-01') {
// await removeDnsTxtRecord(challenge); // A hypothetical function to clean up DNS
}
}
} else if (challenge.type === 'dns-01') {
// Calculate the DNS TXT record value
const dnsRecordName = `_acme-challenge.${authz.identifier.value}`;
const dnsRecordValue = await client.getChallengeKeyAuthorization(challenge);
});
console.log(`Add this TXT record to your DNS:`);
console.log(`Name: ${dnsRecordName}`);
console.log(`Value: ${dnsRecordValue}`);
let expires = await forge.readCertificateInfo(cert);
expires = expires.notAfter;
certificates.set(host, expires)
// Here, implement the logic to add the TXT record to your DNS
// await updateDnsTxtRecord(dnsRecordName, dnsRecordValue); // Hypothetical function to update DNS
}
/* Save the certificate and key */
fs.writeFileSync(hostKeyPath + 'fullchain.pem', cert);
// fs.chmodSync(keyPath + 'fullchain.pem', '444')
},
challengeRemoveFn: async (authz, challenge, keyAuthorization) => {
/* Clean up challenge response here if necessary */
console.log(`Challenge removed for token: ${challenge.token}`);
if (challenge.type === 'http-01') {
/* Clean up challenge response here if necessary */
} else if (challenge.type === 'dns-01') {
// await removeDnsTxtRecord(challenge); // A hypothetical function to clean up DNS
fs.writeFileSync(hostKeyPath + 'private-key.pem', key);
// fs.chmodSync(keyPath + 'private-key.pem', '400')
process.emit('certificateCreated', host)
let safeKey = host.replace(/\./g, '_');
let organization = await this.crud.send({
method: 'object.update',
array: 'organizations',
object: {
_id: organization_id,
['ssl.' + safeKey]: { cert, key: key.toString('utf-8') }
},
organization_id,
});
console.log('Successfully created certificate!');
return true
} catch (error) {
delete hosts[host]
return false
}
}
async getCertificate(host, organization_id) {
const hostKeyPath = keyPath + host + '/';
let organization = await this.crud.send({
method: 'object.read',
array: 'organizations',
object: {
_id: organization_id
},
organization_id,
});
if (organization && organization.object && organization.object[0]) {
if (!organization.object[0].host || !organization.object[0].host.includes(host))
return false
let safeKey = host.replace(/\./g, '_');
if (organization.object[0].ssl && organization.object[0].ssl[safeKey]) {
let cert = organization.object[0].ssl[safeKey].cert
let key = organization.object[0].ssl[safeKey].key
if (cert && key) {
let expires = await forge.readCertificateInfo(cert);
expires = expires.notAfter;
if (this.isValid(expires)) {
certificates.set(host, expires)
if (!fs.existsSync(hostKeyPath)) {
fs.mkdirSync(hostKeyPath, { recursive: true });
}
fs.writeFileSync(hostKeyPath + 'fullchain.pem', cert);
// fs.chmodSync(keyPath + 'fullchain.pem', '444')
fs.writeFileSync(hostKeyPath + 'private-key.pem', key);
// fs.chmodSync(keyPath + 'private-key.pem', '400')
// TODO: emit change so that nginx can reload
return true
}
}
}
}
return await this.requestCertificate(host, organization_id, false)
}
async checkCertificate(host, organization_id) {
let hostname = host.split(':')[0]
if (hostname === 'localhost' || hostname === '127.0.0.1')
return true
let expires = certificates.get(host)
if (expires && this.isValid(expires)) {
return true
}
});
/* Save the certificate and key */
fs.writeFileSync(keyPath + 'certificate.pem', cert);
fs.chmodSync(keyPath + 'certificate.pem', '444')
const hostKeyPath = keyPath + host + '/';
if (fs.existsSync(hostKeyPath + 'fullchain.pem')) {
expires = fs.readFileSync(hostKeyPath + 'fullchain.pem', 'utf8');
expires = await forge.readCertificateInfo(expires);
expires = expires.notAfter;
if (this.isValid(expires)) {
certificates.set(host, expires)
return true
}
}
fs.writeFileSync(keyPath + 'private-key.pem', key);
fs.chmodSync(keyPath + 'certificate.pem', '400')
if (!hosts[host])
hosts[host] = this.getCertificate(host, organization_id)
return hosts[host]
}
console.log('Successfully created certificate!');
}
isValid(expires) {
let currentDate = new Date();
currentDate.setDate(currentDate.getDate() + DAYS);
currentDate.setHours(currentDate.getHours() + HOURS);
currentDate.setMinutes(currentDate.getMinutes() + MINUTES);
if (expires && currentDate < expires) {
return true; // SSL is still valid, no need to renew
}
}
init().catch(err => {
console.error('Error initializing ACME client:', err);
// TODO: Handle initialization error (possibly retry or exit)
});
}
module.exports = { requestCertificate }
// requestCertificate(host).catch(err => {
// console.error('Error creating certificate:', err);
// });
module.exports = CoCreateAcme;
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc