smartcert/ts/index.ts

200 lines
7.0 KiB
TypeScript
Raw Normal View History

2016-06-22 11:22:09 +00:00
import * as plugins from "./cert.plugins";
2016-06-18 13:59:03 +00:00
import * as paths from "./cert.paths";
2016-07-22 00:11:04 +00:00
import * as helpers from "./cert.helpers"
2016-06-18 13:59:03 +00:00
2016-07-13 10:09:34 +00:00
export interface ICertConstructorOptions {
2016-07-12 16:00:08 +00:00
cfEmail: string,
cfKey: string,
2016-07-13 10:09:34 +00:00
sslDir?: string,
2016-07-12 16:00:08 +00:00
gitOriginRepo?: string,
testMode?: boolean
2016-07-22 00:11:04 +00:00
};
2016-07-12 16:00:08 +00:00
2016-06-18 13:59:03 +00:00
export class Cert {
2016-06-28 07:32:01 +00:00
private _cfEmail: string;
private _cfKey: string;
private _sslDir: string;
2016-07-04 06:41:58 +00:00
private _gitOriginRepo: string;
2016-07-21 12:58:05 +00:00
private _testMode: boolean;
2016-07-22 00:11:04 +00:00
domainsCurrentlyRequesting: plugins.lik.Stringmap = new plugins.lik.Stringmap();
2016-07-04 06:41:58 +00:00
certificatesPresent: Certificate[];
certificatesValid: Certificate[];
2016-07-22 00:11:04 +00:00
2016-07-21 12:58:05 +00:00
/**
* Constructor for Cert object
*/
2016-07-22 00:11:04 +00:00
constructor(optionsArg: ICertConstructorOptions) {
2016-06-28 07:32:01 +00:00
this._cfEmail = optionsArg.cfEmail;
this._cfKey = optionsArg.cfKey;
this._sslDir = optionsArg.sslDir;
2016-07-04 02:56:49 +00:00
this._gitOriginRepo = optionsArg.gitOriginRepo;
2016-07-04 06:41:58 +00:00
this._testMode = optionsArg.testMode;
// write hook config
2016-06-22 11:22:09 +00:00
let config = {
2016-06-28 07:32:01 +00:00
cfEmail: this._cfEmail,
cfKey: this._cfKey
2016-06-22 11:22:09 +00:00
}
2016-07-04 06:41:58 +00:00
plugins.smartfile.memory.toFsSync(
JSON.stringify(config),
plugins.path.join(__dirname, "assets/config.json")
);
2016-07-13 10:09:34 +00:00
// setup sslDir
if (!this._sslDir) this._sslDir = paths.defaultSslDir;
2016-07-04 06:41:58 +00:00
// setup Git
if (this._gitOriginRepo) {
2016-07-04 02:56:49 +00:00
plugins.smartgit.init(this._sslDir);
2016-07-04 06:41:58 +00:00
plugins.smartgit.remote.add(this._sslDir, "origin", this._gitOriginRepo);
2016-07-04 02:56:49 +00:00
this.sslGitOriginPull();
}
2016-07-04 06:41:58 +00:00
// setup leSh config;
let leShConfigString;
if (this._testMode) {
leShConfigString = `CA="https://acme-staging.api.letsencrypt.org/directory"\n`;
} else {
leShConfigString = " ";
};
plugins.smartfile.memory.toFsSync(
leShConfigString,
paths.leShConfig
);
2016-07-21 12:58:05 +00:00
plugins.shelljs.exec("chmod 700 " + paths.letsencryptSh);
plugins.shelljs.exec("chmod 700 " + paths.certHook);
2016-07-04 02:56:49 +00:00
};
2016-07-21 12:58:05 +00:00
/**
* Pulls already requested certificates from git origin
*/
2016-07-04 02:56:49 +00:00
sslGitOriginPull = () => {
2016-07-04 06:41:58 +00:00
if (this._gitOriginRepo) {
plugins.smartgit.pull(this._sslDir, "origin", "master");
2016-07-04 02:56:49 +00:00
}
};
2016-07-21 12:58:05 +00:00
/**
* Pushes all new requested certificates to git origin
*/
2016-07-04 02:56:49 +00:00
sslGitOriginAddCommitPush = () => {
2016-07-04 06:41:58 +00:00
if (this._gitOriginRepo) {
2016-07-04 02:56:49 +00:00
plugins.smartgit.add.addAll(this._sslDir);
2016-07-04 06:41:58 +00:00
plugins.smartgit.commit(this._sslDir, "added new SSL certificates and deleted obsolete ones.");
plugins.smartgit.push(this._sslDir, "origin", "master");
2016-07-04 02:56:49 +00:00
}
2016-06-22 11:22:09 +00:00
};
2016-07-21 12:58:05 +00:00
/**
* gets a ssl cert for a given domain
*/
2016-07-04 06:41:58 +00:00
getDomainCert(domainNameArg: string, optionsArg: { force: boolean } = { force: false }) {
2016-06-22 11:22:09 +00:00
let done = plugins.q.defer();
2016-07-22 00:11:04 +00:00
// make sure no one else requires the same domain at the same time
helpers.accountsKeyPresent().then(() => {
if (!this.domainsCurrentlyRequesting.checkString(domainNameArg)) {
this.domainsCurrentlyRequesting.addString(domainNameArg);
if (!checkDomainsStillValid(domainNameArg, this._sslDir) || optionsArg.force) {
plugins.smartfile.fs.ensureDir(paths.certDir);
plugins.beautylog.info(`getting cert for ${domainNameArg}`);
plugins.shelljs.exec(
`bash -c "${paths.letsencryptSh} -c --no-lock -f ${paths.leShConfig} -d ${domainNameArg} -t dns-01 -k ${paths.certHook} -o ${paths.certDir}"`,
{
silent: true,
async: true
},
(codeArg, stdoutArg) => {
console.log(stdoutArg);
let fetchedCertsArray: string[] = plugins.smartfile.fs.listFoldersSync(paths.certDir);
if (fetchedCertsArray.indexOf(domainNameArg) != -1) {
updateSslDirSync(this._sslDir, domainNameArg);
plugins.smartfile.fs.removeSync(plugins.path.join(paths.certDir, domainNameArg));
}
this.domainsCurrentlyRequesting.removeString(domainNameArg);
done.resolve();
}
);
} else {
plugins.beautylog.info("certificate for " + domainNameArg + " is still valid! Not fetching new one!");
this.domainsCurrentlyRequesting.removeString(domainNameArg);
2016-07-21 12:58:05 +00:00
done.resolve();
2016-07-22 00:11:04 +00:00
};
} else {
plugins.beautylog.warn(`${domainNameArg} is already requesting`);
};
});
2016-06-22 11:22:09 +00:00
return done.promise;
2016-06-18 13:59:03 +00:00
};
2016-07-04 06:41:58 +00:00
cleanOldCertificates() {
};
2016-06-18 13:59:03 +00:00
}
2016-06-28 03:53:49 +00:00
export class Certificate {
2016-06-23 01:46:37 +00:00
domainName: string;
creationDate: Date;
expiryDate: Date;
constructor() {
2016-06-18 13:59:03 +00:00
};
}
2016-06-28 07:32:01 +00:00
interface certConfig {
2016-07-04 06:41:58 +00:00
domainName: string;
created: number;
expires: number;
2016-06-28 07:32:01 +00:00
}
2016-07-04 06:41:58 +00:00
let checkDomainsStillValid = (domainNameArg: string, sslDirArg: string): boolean => {
2016-07-22 00:11:04 +00:00
let domainConfigPath = plugins.path.join(sslDirArg, domainNameArg, "config.json");
if (plugins.smartfile.fs.fileExistsSync(domainConfigPath)) {
2016-07-04 06:41:58 +00:00
let domainConfig = plugins.smartfile.fs.toObjectSync(
domainConfigPath,
"json"
);
if (Date.now() >= ((domainConfig.expires - 604800) * 1000)) {
return false;
} else {
return true;
}
} else {
return false;
}
2016-06-23 01:46:37 +00:00
}
2016-07-04 06:41:58 +00:00
let updateSslDirSync = (sslDirArg: string, domainNameArg: string) => {
2016-06-28 07:32:01 +00:00
plugins.smartfile.fs.ensureDirSync(sslDirArg);
2016-07-04 06:41:58 +00:00
let domainCertFolder = plugins.path.join(paths.certDir, domainNameArg)
if (plugins.smartfile.fs.listFoldersSync(paths.certDir).indexOf(domainNameArg) != -1) {
2016-06-28 07:32:01 +00:00
plugins.smartfile.fs.copySync(
2016-07-04 06:41:58 +00:00
plugins.path.join(domainCertFolder, "fullchain.pem"),
plugins.path.join(sslDirArg, domainNameArg, "fullchain.pem")
2016-06-28 07:32:01 +00:00
);
plugins.smartfile.fs.copySync(
2016-07-04 06:41:58 +00:00
plugins.path.join(domainCertFolder, "privkey.pem"),
plugins.path.join(sslDirArg, domainNameArg, "privkey.pem")
2016-06-28 07:32:01 +00:00
);
// create cert config
let certRegex = /.*\-([0-9]*)\.pem/;
2016-07-04 06:41:58 +00:00
let certFileNameWithTime: string = plugins.smartfile.fs.listFilesSync(domainCertFolder, certRegex)[0];
2016-06-28 07:32:01 +00:00
let certTime = parseInt(certRegex.exec(certFileNameWithTime)[1]);
2016-07-04 06:41:58 +00:00
let certConfig: certConfig = {
2016-06-28 07:32:01 +00:00
domainName: domainNameArg,
created: certTime,
expires: certTime + 7776000
};
2016-07-04 06:41:58 +00:00
plugins.smartfile.memory.toFsSync(
2016-06-28 07:32:01 +00:00
JSON.stringify(certConfig),
2016-07-04 06:41:58 +00:00
plugins.path.join(sslDirArg, domainNameArg, "config.json")
2016-06-28 07:32:01 +00:00
);
};
}
2016-07-04 02:56:49 +00:00
const enum gitSyncDirection {
toOrigin,
fromOrigin
}
2016-07-04 06:41:58 +00:00
let updateGitOrigin = (syncDirectionArg: gitSyncDirection) => {
2016-07-04 02:56:49 +00:00
};
2016-06-23 01:46:37 +00:00
2016-07-04 02:56:49 +00:00
updateGitOrigin(gitSyncDirection.toOrigin);