start transition to letsencrypt

This commit is contained in:
Philipp Kunz 2016-10-22 16:21:25 +02:00
parent 738c249a3e
commit 5c7bf24c56
6 changed files with 102 additions and 126 deletions

View File

@ -28,11 +28,12 @@
"homepage": "https://gitlab.com/pushrocks/cert#readme", "homepage": "https://gitlab.com/pushrocks/cert#readme",
"dependencies": { "dependencies": {
"@types/minimatch": "2.x.x", "@types/minimatch": "2.x.x",
"@types/q": "0.x.x", "@types/q": "0.0.32",
"@types/shelljs": "0.x.x", "@types/shelljs": "0.x.x",
"beautylog": "^6.0.0", "beautylog": "^6.0.0",
"cflare": "0.0.10", "cflare": "0.0.10",
"fs-extra": "^0.30.0", "fs-extra": "^0.30.0",
"letsencrypt": "^2.1.8",
"lik": "^1.0.24", "lik": "^1.0.24",
"q": "^1.4.1", "q": "^1.4.1",
"shelljs": "^0.7.4", "shelljs": "^0.7.4",

View File

@ -1,6 +1,7 @@
import * as plugins from "./cert.plugins"; import * as q from 'q'
import * as paths from "./cert.paths"; import * as plugins from './cert.plugins'
import * as helpers from "./cert.classes.cert.helpers" import * as paths from './cert.paths'
import * as helpers from './cert.classes.cert.helpers'
export interface ICertConstructorOptions { export interface ICertConstructorOptions {
cfEmail: string, cfEmail: string,
@ -8,27 +9,27 @@ export interface ICertConstructorOptions {
sslDir?: string, sslDir?: string,
gitOriginRepo?: string, gitOriginRepo?: string,
testMode?: boolean testMode?: boolean
}; }
export class Cert { export class Cert {
private _cfEmail: string; domainCertRequestMap: plugins.lik.Stringmap = new plugins.lik.Stringmap()
private _cfKey: string; certificatesPresent: Certificate[]
private _sslDir: string; certificatesValid: Certificate[]
private _gitOriginRepo: string; private _cfEmail: string
private _testMode: boolean; private _cfKey: string
domainCertRequestMap: plugins.lik.Stringmap = new plugins.lik.Stringmap(); private _sslDir: string
certificatesPresent: Certificate[]; private _gitOriginRepo: string
certificatesValid: Certificate[]; private _testMode: boolean
/** /**
* Constructor for Cert object * Constructor for Cert object
*/ */
constructor(optionsArg: ICertConstructorOptions) { constructor(optionsArg: ICertConstructorOptions) {
this._cfEmail = optionsArg.cfEmail; this._cfEmail = optionsArg.cfEmail
this._cfKey = optionsArg.cfKey; this._cfKey = optionsArg.cfKey
this._sslDir = optionsArg.sslDir; this._sslDir = optionsArg.sslDir
this._gitOriginRepo = optionsArg.gitOriginRepo; this._gitOriginRepo = optionsArg.gitOriginRepo
this._testMode = optionsArg.testMode; this._testMode = optionsArg.testMode
// write hook config // write hook config
let config = { let config = {
cfEmail: this._cfEmail, cfEmail: this._cfEmail,
@ -36,129 +37,111 @@ export class Cert {
} }
plugins.smartfile.memory.toFsSync( plugins.smartfile.memory.toFsSync(
JSON.stringify(config), JSON.stringify(config),
plugins.path.join(__dirname, "assets/config.json") plugins.path.join(__dirname, 'assets/config.json')
); )
// setup sslDir // setup sslDir
if (!this._sslDir) this._sslDir = paths.defaultSslDir; if (!this._sslDir) this._sslDir = paths.defaultSslDir
// setup Git // setup Git
if (this._gitOriginRepo) { if (this._gitOriginRepo) {
plugins.smartgit.init(this._sslDir); plugins.smartgit.init(this._sslDir)
plugins.smartgit.remote.add(this._sslDir, "origin", this._gitOriginRepo); plugins.smartgit.remote.add(this._sslDir, 'origin', this._gitOriginRepo)
this.sslGitOriginPull(); this.sslGitOriginPull()
}
} }
// 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
);
plugins.shelljs.exec("chmod 700 " + paths.letsencryptSh);
plugins.shelljs.exec("chmod 700 " + paths.certHook);
plugins.shelljs.exec(
`bash -c "${paths.letsencryptSh} -c --no-lock -f ${paths.leShConfig} -d notthere.notthere -t dns-01 -k ${paths.certHook} -o ${paths.certDir}"`,
{
silent: true
});
};
/** /**
* Pulls already requested certificates from git origin * Pulls already requested certificates from git origin
*/ */
sslGitOriginPull = () => { sslGitOriginPull = () => {
if (this._gitOriginRepo) { if (this._gitOriginRepo) {
plugins.smartgit.pull(this._sslDir, "origin", "master"); plugins.smartgit.pull(this._sslDir, 'origin', 'master')
}
} }
};
/** /**
* Pushes all new requested certificates to git origin * Pushes all new requested certificates to git origin
*/ */
sslGitOriginAddCommitPush = () => { sslGitOriginAddCommitPush = () => {
if (this._gitOriginRepo) { if (this._gitOriginRepo) {
plugins.smartgit.add.addAll(this._sslDir); plugins.smartgit.add.addAll(this._sslDir)
plugins.smartgit.commit(this._sslDir, "added new SSL certificates and deleted obsolete ones."); plugins.smartgit.commit(this._sslDir, 'added new SSL certificates and deleted obsolete ones.')
plugins.smartgit.push(this._sslDir, "origin", "master"); plugins.smartgit.push(this._sslDir, 'origin', 'master')
}
} }
};
/** /**
* gets a ssl cert for a given domain * gets a ssl cert for a given domain
*/ */
getDomainCert(domainNameArg: string, optionsArg: { force: boolean } = { force: false }) { getDomainCert(domainNameArg: string, optionsArg: { force: boolean } = { force: false }) {
let done = plugins.q.defer(); let done = q.defer()
let domainStringData = new plugins.smartstring.Domain(domainNameArg); let domainStringData = new plugins.smartstring.Domain(domainNameArg)
let sameZoneRequesting: boolean = this.domainCertRequestMap.checkMinimatch("*" + domainStringData.zoneName) let sameZoneRequesting: boolean = this.domainCertRequestMap.checkMinimatch('*' + domainStringData.zoneName)
// make sure no one else requires the same domain at the same time // make sure no one else requires the same domain at the same time
if (!this.domainCertRequestMap.checkString(domainNameArg)) { if (!this.domainCertRequestMap.checkString(domainNameArg)) {
this.domainCertRequestMap.addString(domainNameArg); this.domainCertRequestMap.addString(domainNameArg)
if (!helpers.checkDomainsStillValid(domainNameArg, this._sslDir) || optionsArg.force) { if (!helpers.checkDomainsStillValid(domainNameArg, this._sslDir) || optionsArg.force) {
if (!sameZoneRequesting) { if (!sameZoneRequesting) {
this.sslGitOriginPull(); this.sslGitOriginPull()
plugins.smartfile.fs.ensureDir(paths.certDir); plugins.smartfile.fs.ensureDir(paths.certDir)
plugins.beautylog.info(`getting cert for ${domainNameArg}`); plugins.beautylog.info(`getting cert for ${domainNameArg}`)
plugins.shelljs.exec( plugins.shelljs.exec(
`bash -c "${paths.letsencryptSh} -c --no-lock -f ${paths.leShConfig} -d ${domainNameArg} -t dns-01 -k ${paths.certHook} -o ${paths.certDir}"`, `bash -c "${paths.letsencryptSh} -c --no-lock -f ${paths.leShConfig} -d ${domainNameArg} -t dns-01 -k ${paths.certHook} -o ${paths.certDir}"`,
{ {
silent: true silent: true
}, },
(codeArg, stdoutArg) => { (codeArg, stdoutArg) => {
if (codeArg == 0) { if (codeArg === 0) {
console.log(stdoutArg); console.log(stdoutArg)
let fetchedCertsArray: string[] = plugins.smartfile.fs.listFoldersSync(paths.certDir); let fetchedCertsArray: string[] = plugins.smartfile.fs.listFoldersSync(paths.certDir)
if (fetchedCertsArray.indexOf(domainNameArg) != -1) { if (fetchedCertsArray.indexOf(domainNameArg) != -1) {
helpers.updateSslDirSync(this._sslDir, domainNameArg); helpers.updateSslDirSync(this._sslDir, domainNameArg)
plugins.smartfile.fs.removeSync(plugins.path.join(paths.certDir, domainNameArg)); plugins.smartfile.fs.removeSync(plugins.path.join(paths.certDir, domainNameArg))
this.sslGitOriginAddCommitPush(); this.sslGitOriginAddCommitPush()
} else { } else {
plugins.beautylog.error(`Couldn't copy final certificate for ${domainNameArg}!`); plugins.beautylog.error(`Couldn't copy final certificate for ${domainNameArg}!`)
};
done.resolve();
} else {
plugins.beautylog.warn(`${domainNameArg} scheduled for retry. Waiting 1 minute!`);
helpers.scheduleRetry(domainNameArg, this).then(done.resolve);
} }
this.domainCertRequestMap.removeString(domainNameArg); done.resolve()
}
);
} else { } else {
plugins.beautylog.info(`${domainNameArg} is waiting for domains names of same zone to finish`); plugins.beautylog.warn(`${domainNameArg} scheduled for retry. Waiting 1 minute!`)
this.domainCertRequestMap.removeString(domainNameArg); helpers.scheduleRetry(domainNameArg, this).then(done.resolve)
}
this.domainCertRequestMap.removeString(domainNameArg)
}
)
} else {
plugins.beautylog.info(`${domainNameArg} is waiting for domains names of same zone to finish`)
this.domainCertRequestMap.removeString(domainNameArg)
this.domainCertRequestMap.registerUntilTrue( this.domainCertRequestMap.registerUntilTrue(
() => { () => {
return !this.domainCertRequestMap.checkMinimatch("*" + domainStringData.zoneName); return !this.domainCertRequestMap.checkMinimatch('*' + domainStringData.zoneName)
}, },
() => { () => {
this.getDomainCert(domainNameArg).then(done.resolve); this.getDomainCert(domainNameArg).then(done.resolve)
} }
); )
} }
} else { } else {
plugins.beautylog.info("certificate for " + domainNameArg + " is still valid! Not fetching new one!"); plugins.beautylog.info('certificate for ' + domainNameArg + ' is still valid! Not fetching new one!')
this.domainCertRequestMap.removeString(domainNameArg); this.domainCertRequestMap.removeString(domainNameArg)
done.resolve(); done.resolve()
}; }
} else { } else {
plugins.beautylog.warn(`${domainNameArg} is already requesting`); plugins.beautylog.warn(`${domainNameArg} is already requesting`)
}; }
return done.promise; return done.promise
}; }
cleanOldCertificates() { cleanOldCertificates() {
}; }
} }
export class Certificate { export class Certificate {
domainName: string; domainName: string
creationDate: Date; creationDate: Date
expiryDate: Date; expiryDate: Date
constructor() { constructor() {
}; }
}; }

View File

@ -1,6 +1,3 @@
#!/usr/bin/env node
// the shebang line above makes sure this script will get interpreted by node
import * as plugins from "./cert.plugins"; import * as plugins from "./cert.plugins";
import * as paths from "./cert.paths"; import * as paths from "./cert.paths";

View File

@ -1,13 +1,28 @@
import "typings-global"; import 'typings-global'
export import beautylog = require("beautylog"); import * as beautylog from 'beautylog'
export import cflare = require("cflare"); import * as cflare from 'cflare'
export let fs = require("fs-extra"); let fs = require('fs-extra')
export import lik = require("lik"); let letsencrypt = require('letsencrypt')
export import path = require("path"); import * as lik from 'lik'
export import q = require("q"); import * as path from 'path'
export import shelljs = require("shelljs"); import * as q from 'q'
export import smartcli = require("smartcli"); import * as shelljs from 'shelljs'
export import smartfile = require("smartfile"); import * as smartcli from 'smartcli'
export import smartgit = require("smartgit"); import * as smartfile from 'smartfile'
export import smartstring = require("smartstring"); import * as smartgit from 'smartgit'
import * as smartstring from 'smartstring'
export {
beautylog,
cflare,
fs,
letsencrypt,
lik,
path,
q,
shelljs,
smartcli,
smartfile,
smartgit,
smartstring
}

View File

@ -1,23 +0,0 @@
import * as plugins from "./cert.plugins";
import * as paths from "./cert.paths";
export let startInstall = () => {
let done = plugins.q.defer();
plugins.beautylog.info("installing letsencrypt.sh locally...");
plugins.fs.ensureDir(plugins.path.join(__dirname, "assets/"));
plugins.smartfile.remote.toFs(
"https://raw.githubusercontent.com/lukas2511/letsencrypt.sh/master/letsencrypt.sh",
paths.letsencryptSh
).then(() => {
plugins.beautylog.success("Done!");
done.resolve();
});
return done.promise;
};
let smartcli = new plugins.smartcli.Smartcli();
smartcli.addCommand({
commandName:"install"
}).then(startInstall);
smartcli.startParse();

3
tslint.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "tslint-config-standard"
}