35 Commits

Author SHA1 Message Date
55d450be69 0.0.13 2016-07-06 03:23:54 +02:00
ae2ade8680 updated test 2016-07-06 03:23:51 +02:00
d7ec7e254e 0.0.12 2016-07-06 03:10:49 +02:00
4bd7aee8ea add typings to package.json 2016-07-06 03:10:36 +02:00
bf711c2bb6 update tests 2016-07-04 21:57:14 +02:00
eb9e6679c4 0.0.11 2016-07-04 09:00:31 +02:00
96c801cdbd now fully working 2016-07-04 08:41:58 +02:00
ee844fd348 implement Git Repo Sync 2016-07-04 04:56:49 +02:00
d06c7059bb update dependency typings 2016-07-01 06:26:10 +02:00
d36f2f0c33 update for latest npmts compatibility 2016-07-01 06:18:48 +02:00
4bcf925b28 0.0.10 2016-06-29 22:55:50 +02:00
484e75ac34 nwo creating configfile per certificate all right 2016-06-28 10:34:59 +02:00
ad792f32dd now has sslDir implemented 2016-06-28 09:32:01 +02:00
05b7c7ab45 improve logs and cooldown 2016-06-28 05:53:49 +02:00
d94a097443 start Certificate class logic 2016-06-23 04:37:18 +02:00
645e46dc01 start implementing validity check 2016-06-23 03:46:37 +02:00
5c87851ab8 remove some unused deps 2016-06-22 14:58:46 +02:00
0fd3bd262b start implementation of sslDir and gitOrigin 2016-06-22 13:55:38 +02:00
732e35d295 compile 2016-06-22 13:35:15 +02:00
e9135c42af 0.0.9 2016-06-22 13:22:21 +02:00
a850243c59 now working 2016-06-22 13:22:09 +02:00
8dc75feb8d update structure 2016-06-21 21:08:04 +02:00
ac7160c3a3 add should devDeps 2016-06-21 20:04:44 +02:00
1cdcd332d4 install step now working 2016-06-21 20:02:57 +02:00
5e10df8e5a update REAMDE 2016-06-18 16:14:57 +02:00
e67677d57d 0.0.6 2016-06-18 16:03:58 +02:00
d5b5e8d612 update Readme 2016-06-18 16:03:46 +02:00
bbee7923e4 more structure 2016-06-18 15:59:03 +02:00
04be5f88eb 0.0.5 2016-06-18 15:42:53 +02:00
3c626908f1 compile 2016-06-18 15:42:49 +02:00
821b2f95b0 fix repo link 2016-06-18 15:42:09 +02:00
c0753c185f 0.0.4 2016-06-18 15:13:06 +02:00
dad659e2ed fix description 2016-06-18 15:13:02 +02:00
d1f1ae271a 0.0.3 2016-06-18 15:09:48 +02:00
7bd18cb927 fix branches for ci 2016-06-18 15:09:44 +02:00
25 changed files with 701 additions and 39 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
node_modules node_modules
docs/ docs/
coverage/ coverage/
.nogit/
dist/assets/
test/assets/

View File

@ -1,35 +1,26 @@
image: hosttoday/ht-docker-node:npmts image: hosttoday/ht-docker-node:npmts
stages: stages:
- test - test1
- test2
- release - release
testLEGACY:
stage: test
script:
- npmci test legacy
only:
- master
tags:
- docker
- lossless
testLTS: testLTS:
stage: test stage: test1
script: script:
- npmci test lts - npmci test lts
only: only:
- master - tags
tags: tags:
- docker - docker
- lossless - lossless
testSTABLE: testSTABLE:
stage: test stage: test2
script: script:
- npmci test stable - npmci test stable
only: only:
- master - tags
tags: tags:
- docker - docker
- lossless - lossless
@ -39,6 +30,6 @@ release:
script: script:
- npmci publish npm - npmci publish npm
only: only:
- master - tags
tags: tags:
- lossless - lossless

View File

@ -6,11 +6,24 @@ Easily obain SSL certificates from LetsEncrypt. Supports DNS-01 challenge. TypeS
```typescript ```typescript
import {Cert} from "cert"; import {Cert} from "cert";
myCert = new Cert({ let myCert = new Cert({
cfEmail = "some@cloudflare.email", cfEmail: "some@cloudflare.email",
cfKey = "someCloudflareApiKey", cfKey: "someCloudflareApiKey",
outputPath = "someOutputPath" // NOTE: if you already have certificates, make sure you put them in here, so cert only requires the missing ones sslDir: "someOutputPath", // NOTE: if you already have certificates, make sure you put them in here, so cert only requires the missing ones
gitOriginRepo: "git@githhub.com/someuser/somereopo" // good for persistence in highly volatile environments like docker
}); });
myCert.getDomainCert("example.com"); myCert.getDomainCert("example.com");
``` ```
### sslDir
to use the certificates it is important to understand what the structure of the ssl directory looks like.
### using a git origin repo.
Often times you want to keep track of certificates in order to keep them
even if the point of initial certificate request is gone. Imagine you have a dockerenvironement
and you keep starting new container versions for the same domain. YOu ideally want to use a proxy
that handles SSL managemet for you. But even the proxy needs to be updated from time to time.
So you need some kind of persistence between versions. This is why you can sync up all certificates to a git repo over ssh
Just make sure your id_rsa is in place for the node user and is allowed for the origin repo.

65
dist/cert.hook.js vendored Executable file
View File

@ -0,0 +1,65 @@
#!/usr/bin/env node
"use strict";
var plugins = require("./cert.plugins");
var paths = require("./cert.paths");
var smartcli = new plugins.smartcli.Smartcli();
var config = plugins.smartfile.fs.toObjectSync(paths.config);
var cflare = new plugins.cflare.CflareAccount();
cflare.auth({
email: config.cfEmail,
key: config.cfKey
});
var setChallenge = function (domainNameArg, challengeArg) {
var done = plugins.q.defer();
plugins.beautylog.log("setting challenge for " + domainNameArg);
cflare.createRecord(prefixName(domainNameArg), "TXT", challengeArg).then(function () {
plugins.beautylog.ok("Challenge has been set!");
plugins.beautylog.info("We need to cool down to let DNS propagate to edge locations!");
cooldown().then(function () {
done.resolve();
});
});
return done.promise;
};
var cleanChallenge = function (domainNameArg) {
var done = plugins.q.defer();
plugins.beautylog.log("cleaning challenge for " + domainNameArg);
cflare.removeRecord(prefixName(domainNameArg), "TXT");
return done.promise;
};
var cooldown = function () {
var done = plugins.q.defer();
var cooldowntime = 40000;
var passedTime = 0;
plugins.beautylog.log("Cooling down! " + (cooldowntime / 1000).toString() + " seconds left");
var coolDownCounter = function () {
setTimeout(function () {
if (cooldowntime <= passedTime) {
plugins.beautylog.ok("Cooled down!");
done.resolve();
}
else {
passedTime = passedTime + 5000;
plugins.beautylog.log("Cooling down! " + ((cooldowntime - passedTime) / 1000).toString() + " seconds left");
coolDownCounter();
}
}, 5000);
};
coolDownCounter();
return done.promise;
};
var prefixName = function (domainNameArg) {
return "_acme-challenge." + domainNameArg;
};
smartcli.addCommand({
commandName: "deploy_challenge"
}).then(function (argv) {
setChallenge(argv._[1], argv._[3]);
});
smartcli.addCommand({
commandName: "clean_challenge"
}).then(function (argv) {
cleanChallenge(argv._[1]);
});
smartcli.startParse();
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2VydC5ob29rLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2VydC5ob29rLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBR0EsSUFBWSxPQUFPLFdBQU0sZ0JBQWdCLENBQUMsQ0FBQTtBQUMxQyxJQUFZLEtBQUssV0FBTSxjQUFjLENBQUMsQ0FBQTtBQUV0QyxJQUFJLFFBQVEsR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUM7QUFFL0MsSUFBSSxNQUFNLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsWUFBWSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUM3RCxJQUFJLE1BQU0sR0FBRyxJQUFJLE9BQU8sQ0FBQyxNQUFNLENBQUMsYUFBYSxFQUFFLENBQUM7QUFDaEQsTUFBTSxDQUFDLElBQUksQ0FBQztJQUNSLEtBQUssRUFBRSxNQUFNLENBQUMsT0FBTztJQUNyQixHQUFHLEVBQUUsTUFBTSxDQUFDLEtBQUs7Q0FDcEIsQ0FBQyxDQUFDO0FBRUgsSUFBSSxZQUFZLEdBQUcsVUFBQyxhQUFxQixFQUFFLFlBQW9CO0lBQzNELElBQUksSUFBSSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDN0IsT0FBTyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsd0JBQXdCLEdBQUcsYUFBYSxDQUFDLENBQUM7SUFDaEUsTUFBTSxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLEVBQUUsS0FBSyxFQUFFLFlBQVksQ0FBQyxDQUFDLElBQUksQ0FBQztRQUNyRSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO1FBQ2hELE9BQU8sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLDhEQUE4RCxDQUFDLENBQUM7UUFDdkYsUUFBUSxFQUFFLENBQUMsSUFBSSxDQUFDO1lBQ1osSUFBSSxDQUFDLE9BQU8sRUFB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

6
dist/cert.paths.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
export declare let certHook: string;
export declare let config: string;
export declare let leShConfig: string;
export declare let letsencryptSh: string;
export declare let certDir: string;
export declare let assetDir: string;

11
dist/cert.paths.js vendored Normal file
View File

@ -0,0 +1,11 @@
"use strict";
var plugins = require("./cert.plugins");
// files
exports.certHook = plugins.path.join(__dirname, "cert.hook.js");
exports.config = plugins.path.join(__dirname, "assets/config.json");
exports.leShConfig = plugins.path.join(__dirname, "assets/leshconfig.json");
exports.letsencryptSh = plugins.path.join(__dirname, "assets/letsencrypt.sh");
//dirs
exports.certDir = plugins.path.join(__dirname, "/assets/certs");
exports.assetDir = plugins.path.join(__dirname, "/assets/");
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2VydC5wYXRocy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NlcnQucGF0aHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLElBQVksT0FBTyxXQUFNLGdCQUFnQixDQUFDLENBQUE7QUFFMUMsUUFBUTtBQUNHLGdCQUFRLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFDLGNBQWMsQ0FBQyxDQUFDO0FBQ3ZELGNBQU0sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUMsb0JBQW9CLENBQUMsQ0FBQztBQUMzRCxrQkFBVSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBQyx3QkFBd0IsQ0FBQyxDQUFDO0FBQ25FLHFCQUFhLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFDLHVCQUF1QixDQUFDLENBQUM7QUFFaEYsTUFBTTtBQUNLLGVBQU8sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUMsZUFBZSxDQUFDLENBQUM7QUFDdkQsZ0JBQVEsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUMsVUFBVSxDQUFDLENBQUMifQ==

11
dist/cert.plugins.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
import "typings-global";
export import beautylog = require("beautylog");
export import cflare = require("cflare");
export declare let fs: any;
export import path = require("path");
export import q = require("q");
export declare let shelljs: any;
export import smartcli = require("smartcli");
export import smartfile = require("smartfile");
export import smartgit = require("smartgit");
export import smartstring = require("smartstring");

13
dist/cert.plugins.js vendored Normal file
View File

@ -0,0 +1,13 @@
"use strict";
require("typings-global");
exports.beautylog = require("beautylog");
exports.cflare = require("cflare");
exports.fs = require("fs-extra");
exports.path = require("path");
exports.q = require("q");
exports.shelljs = require("shelljs");
exports.smartcli = require("smartcli");
exports.smartfile = require("smartfile");
exports.smartgit = require("smartgit");
exports.smartstring = require("smartstring");
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2VydC5wbHVnaW5zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2VydC5wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxRQUFPLGdCQUFnQixDQUFDLENBQUE7QUFDVixpQkFBUyxXQUFXLFdBQVcsQ0FBQyxDQUFDO0FBQ2pDLGNBQU0sV0FBVyxRQUFRLENBQUMsQ0FBQztBQUM5QixVQUFFLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0FBQ3RCLFlBQUksV0FBVyxNQUFNLENBQUMsQ0FBQztBQUN2QixTQUFDLFdBQVcsR0FBRyxDQUFDLENBQUM7QUFDcEIsZUFBTyxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUMxQixnQkFBUSxXQUFXLFVBQVUsQ0FBQyxDQUFDO0FBQy9CLGlCQUFTLFdBQVcsV0FBVyxDQUFDLENBQUM7QUFDakMsZ0JBQVEsV0FBVyxVQUFVLENBQUMsQ0FBQztBQUMvQixtQkFBVyxXQUFXLGFBQWEsQ0FBQyxDQUFDIn0=

3
dist/hook.js vendored
View File

@ -1,3 +0,0 @@
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJob29rLmpzIiwic291cmNlc0NvbnRlbnQiOltdfQ==

30
dist/index.d.ts vendored
View File

@ -0,0 +1,30 @@
/// <reference types="q" />
import * as plugins from "./cert.plugins";
export declare class Cert {
private _cfEmail;
private _cfKey;
private _sslDir;
private _gitOriginRepo;
private _testMode;
certificatesPresent: Certificate[];
certificatesValid: Certificate[];
constructor(optionsArg: {
cfEmail: string;
cfKey: string;
sslDir: string;
gitOriginRepo?: string;
testMode?: boolean;
});
sslGitOriginPull: () => void;
sslGitOriginAddCommitPush: () => void;
getDomainCert(domainNameArg: string, optionsArg?: {
force: boolean;
}): plugins.q.Promise<{}>;
cleanOldCertificates(): void;
}
export declare class Certificate {
domainName: string;
creationDate: Date;
expiryDate: Date;
constructor();
}

125
dist/index.js vendored

File diff suppressed because one or more lines are too long

3
dist/install.d.ts vendored
View File

@ -0,0 +1,3 @@
/// <reference types="q" />
import * as plugins from "./cert.plugins";
export declare let startInstall: () => plugins.q.Promise<{}>;

22
dist/install.js vendored
View File

@ -1,3 +1,19 @@
"use strict";
var plugins = require("./cert.plugins");
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJpbnN0YWxsLmpzIiwic291cmNlc0NvbnRlbnQiOltdfQ== var paths = require("./cert.paths");
exports.startInstall = function () {
var 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(function () {
plugins.beautylog.success("Done!");
done.resolve();
});
return done.promise;
};
var smartcli = new plugins.smartcli.Smartcli();
smartcli.addCommand({
commandName: "install"
}).then(exports.startInstall);
smartcli.startParse();
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdGFsbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2luc3RhbGwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLElBQVksT0FBTyxXQUFNLGdCQUFnQixDQUFDLENBQUE7QUFDMUMsSUFBWSxLQUFLLFdBQU0sY0FBYyxDQUFDLENBQUE7QUFFM0Isb0JBQVksR0FBRztJQUN0QixJQUFJLElBQUksR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzdCLE9BQU8sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLHNDQUFzQyxDQUFDLENBQUM7SUFFL0QsT0FBTyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUM7SUFDOUQsT0FBTyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUN6QixrRkFBa0YsRUFDbEYsS0FBSyxDQUFDLGFBQWEsQ0FDdEIsQ0FBQyxJQUFJLENBQUM7UUFDSCxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNuQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDbkIsQ0FBQyxDQUFDLENBQUM7SUFDSCxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQztBQUN4QixDQUFDLENBQUM7QUFFRixJQUFJLFFBQVEsR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUM7QUFDL0MsUUFBUSxDQUFDLFVBQVUsQ0FBQztJQUNoQixXQUFXLEVBQUMsU0FBUztDQUN4QixDQUFDLENBQUMsSUFBSSxDQUFDLG9CQUFZLENBQUMsQ0FBQztBQUN0QixRQUFRLENBQUMsVUFBVSxFQUFFLENBQUMifQ==

View File

@ -1,14 +1,18 @@
{ {
"name": "cert", "name": "cert",
"version": "0.0.2", "version": "0.0.13",
"description": "automatic cert generation for coreos clusters", "description": "Easily obain SSL certificates from LetsEncrypt. Supports DNS-01 challenge. TypeScript ready.",
"main": "dist/index.js", "main": "dist/index.js",
"typings": "dist/index.d.ts",
"scripts": { "scripts": {
"test": "(npmts)" "test": "(npm run cleanTest && npmts)",
"cleanTest": "(rm -rf ./test/assets)",
"install": "node dist/install.js install",
"compile": "(npmts --notest)"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/pushrocks/cert.git" "url": "git+https://gitlab.com/pushrocks/cert.git"
}, },
"keywords": [ "keywords": [
"coreos", "coreos",
@ -19,13 +23,28 @@
"author": "Lossless GmbH", "author": "Lossless GmbH",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/pushrocks/cert/issues" "url": "https://gitlab.com/pushrocks/cert/issues"
}, },
"homepage": "https://github.com/pushrocks/cert#readme", "homepage": "https://gitlab.com/pushrocks/cert#readme",
"dependencies": { "dependencies": {
"letsencrypt": "^1.4.4" "@types/minimatch": "^2.0.26-alpha",
"@types/q": "0.0.25-alpha",
"@types/shelljs": "^0.3.25-alpha",
"beautylog": "^5.0.13",
"cflare": "0.0.9",
"fs-extra": "^0.30.0",
"q": "^1.4.1",
"shelljs": "^0.7.0",
"smartcli": "^1.0.4",
"smartfile": "^4.0.10",
"smartgit": "0.1.8",
"smartstring": "^2.0.10",
"typings-global": "^1.0.6"
}, },
"devDependencies": { "devDependencies": {
"npmts-g": "^5.2.6" "npmts-g": "^5.2.6",
"qenv": "^1.0.8",
"should": "^9.0.2",
"typings-test": "^1.0.1"
} }
} }

3
qenv.yml Normal file
View File

@ -0,0 +1,3 @@
vars:
- CF_EMAIL
- CF_KEY

2
test/test.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
import "typings-test";
import "should";

39
test/test.js Normal file
View File

@ -0,0 +1,39 @@
"use strict";
require("typings-test");
require("should");
var qenv_1 = require("qenv");
var path = require("path");
var install_1 = require("../dist/install");
var cert = require("../dist/index");
var testQenv = new qenv_1.Qenv(process.cwd(), process.cwd() + "/.nogit");
var testCert;
describe("cert", function () {
describe("install", function () {
it("should download letsencrypt.sh", function (done) {
this.timeout(5000);
install_1.startInstall().then(function () {
done();
});
});
});
describe("Cert", function () {
it("should create a new Cert object from class", function () {
this.timeout(20000);
testCert = new cert.Cert({
cfEmail: process.env.CF_EMAIL,
cfKey: process.env.CF_KEY,
sslDir: path.join(process.cwd(), "test/assets"),
gitOriginRepo: "git@gitlab.com:sandboxzone/sandbox-sslorigin.git",
testMode: true
});
testCert.should.be.instanceof(cert.Cert);
});
it("should get a valid certificate", function (done) {
this.timeout(120000);
testCert.getDomainCert("hello2.bleu.de").then(function () {
done();
});
});
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLFFBQU8sY0FBYyxDQUFDLENBQUE7QUFDdEIsUUFBTyxRQUFRLENBQUMsQ0FBQTtBQUNoQixxQkFBbUIsTUFBTSxDQUFDLENBQUE7QUFDMUIsSUFBTyxJQUFJLFdBQVcsTUFBTSxDQUFDLENBQUM7QUFFOUIsd0JBQTJCLGlCQUFpQixDQUFDLENBQUE7QUFDN0MsSUFBWSxJQUFJLFdBQU0sZUFBZSxDQUFDLENBQUE7QUFHdEMsSUFBSSxRQUFRLEdBQUcsSUFBSSxXQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFLE9BQU8sQ0FBQyxHQUFHLEVBQUUsR0FBRyxTQUFTLENBQUMsQ0FBQztBQUVsRSxJQUFJLFFBQWtCLENBQUM7QUFFdkIsUUFBUSxDQUFDLE1BQU0sRUFBQztJQUNaLFFBQVEsQ0FBQyxTQUFTLEVBQUM7UUFDZixFQUFFLENBQUMsZ0NBQWdDLEVBQUMsVUFBUyxJQUFJO1lBQzdDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDbkIsc0JBQVksRUFBRSxDQUFDLElBQUksQ0FBQztnQkFDaEIsSUFBSSxFQUFFLENBQUM7WUFDWCxDQUFDLENBQUMsQ0FBQTtRQUNOLENBQUMsQ0FBQyxDQUFBO0lBQ04sQ0FBQyxDQUFDLENBQUE7SUFDRixRQUFRLENBQUMsTUFBTSxFQUFDO1FBQ1osRUFBRSxDQUFDLDRDQUE0QyxFQUFDO1lBQzVDLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDcEIsUUFBUSxHQUFHLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQztnQkFDckIsT0FBTyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUTtnQkFDN0IsS0FBSyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTTtnQkFDekIsTUFBTSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFDLGFBQWEsQ0FBQztnQkFDOUMsYUFBYSxFQUFDLGtEQUFrRDtnQkFDaEUsUUFBUSxFQUFDLElBQUk7YUFDaEIsQ0FBQyxDQUFDO1lBQ0gsUUFBUSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QyxDQUFDLENBQUMsQ0FBQTtRQUNGLEVBQUUsQ0FBQyxnQ0FBZ0MsRUFBQyxVQUFTLElBQUk7WUFDN0MsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUNyQixRQUFRLENBQUMsYUFBYSxDQUFDLGdCQUFnQixDQUFDLENBQUMsSUFBSSxDQUFDO2dCQUMxQyxJQUFJLEVBQUUsQ0FBQztZQUNYLENBQUMsQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxDQUFDLENBQUE7SUFDTixDQUFDLENBQUMsQ0FBQTtBQUNOLENBQUMsQ0FBQyxDQUFDIn0=

42
test/test.ts Normal file
View File

@ -0,0 +1,42 @@
import "typings-test";
import "should";
import {Qenv} from "qenv";
import path = require("path");
import {startInstall} from "../dist/install";
import * as cert from "../dist/index";
let testQenv = new Qenv(process.cwd(), process.cwd() + "/.nogit");
let testCert:cert.Cert;
describe("cert",function(){
describe("install",function(){
it("should download letsencrypt.sh",function(done){
this.timeout(5000);
startInstall().then(() => {
done();
})
})
})
describe("Cert",function(){
it("should create a new Cert object from class",function(){
this.timeout(20000);
testCert = new cert.Cert({
cfEmail: process.env.CF_EMAIL,
cfKey: process.env.CF_KEY,
sslDir: path.join(process.cwd(),"test/assets"),
gitOriginRepo:"git@gitlab.com:sandboxzone/sandbox-sslorigin.git",
testMode:true
});
testCert.should.be.instanceof(cert.Cert);
})
it("should get a valid certificate",function(done){
this.timeout(120000);
testCert.getDomainCert("hello2.bleu.de").then(() => {
done();
});
})
})
});

73
ts/cert.hook.ts Normal file
View File

@ -0,0 +1,73 @@
#!/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 paths from "./cert.paths";
let smartcli = new plugins.smartcli.Smartcli();
let config = plugins.smartfile.fs.toObjectSync(paths.config);
let cflare = new plugins.cflare.CflareAccount();
cflare.auth({
email: config.cfEmail,
key: config.cfKey
});
let setChallenge = (domainNameArg: string, challengeArg: string) => {
let done = plugins.q.defer();
plugins.beautylog.log("setting challenge for " + domainNameArg);
cflare.createRecord(prefixName(domainNameArg), "TXT", challengeArg).then(() => {
plugins.beautylog.ok("Challenge has been set!");
plugins.beautylog.info("We need to cool down to let DNS propagate to edge locations!");
cooldown().then(() => {
done.resolve();
});
});
return done.promise;
}
let cleanChallenge = (domainNameArg) => {
let done = plugins.q.defer();
plugins.beautylog.log("cleaning challenge for " + domainNameArg);
cflare.removeRecord(prefixName(domainNameArg), "TXT");
return done.promise;
}
let cooldown = () => {
let done = plugins.q.defer();
let cooldowntime = 40000;
let passedTime = 0;
plugins.beautylog.log("Cooling down! " + (cooldowntime/1000).toString() + " seconds left");
let coolDownCounter = () => {
setTimeout(() => {
if(cooldowntime <= passedTime){
plugins.beautylog.ok("Cooled down!");
done.resolve();
} else {
passedTime = passedTime + 5000;
plugins.beautylog.log("Cooling down! " + ((cooldowntime - passedTime)/1000).toString() + " seconds left");
coolDownCounter();
}
}, 5000);
}
coolDownCounter();
return done.promise;
}
let prefixName = (domainNameArg: string): string => {
return "_acme-challenge." + domainNameArg;
}
smartcli.addCommand({
commandName: "deploy_challenge"
}).then((argv) => {
setChallenge(argv._[1], argv._[3]);
});
smartcli.addCommand({
commandName: "clean_challenge"
}).then((argv) => {
cleanChallenge(argv._[1]);
});
smartcli.startParse();

11
ts/cert.paths.ts Normal file
View File

@ -0,0 +1,11 @@
import * as plugins from "./cert.plugins";
// files
export let certHook = plugins.path.join(__dirname,"cert.hook.js");
export let config = plugins.path.join(__dirname,"assets/config.json");
export let leShConfig = plugins.path.join(__dirname,"assets/leshconfig.json");
export let letsencryptSh = plugins.path.join(__dirname,"assets/letsencrypt.sh");
//dirs
export let certDir = plugins.path.join(__dirname,"/assets/certs");
export let assetDir = plugins.path.join(__dirname,"/assets/");

12
ts/cert.plugins.ts Normal file
View File

@ -0,0 +1,12 @@
import "typings-global";
export import beautylog = require("beautylog");
export import cflare = require("cflare");
export let fs = require("fs-extra");
export import path = require("path");
export import q = require("q");
export let shelljs = require("shelljs");
export import smartcli = require("smartcli");
export import smartfile = require("smartfile");
export import smartgit = require("smartgit");
export import smartstring = require("smartstring");

View File

View File

@ -0,0 +1,160 @@
import * as plugins from "./cert.plugins";
import * as paths from "./cert.paths";
export class Cert {
private _cfEmail: string;
private _cfKey: string;
private _sslDir: string;
private _gitOriginRepo: string;
private _testMode: boolean
certificatesPresent: Certificate[];
certificatesValid: Certificate[];
constructor(optionsArg: {
cfEmail: string,
cfKey: string,
sslDir: string,
gitOriginRepo?: string,
testMode?: boolean
}) {
this._cfEmail = optionsArg.cfEmail;
this._cfKey = optionsArg.cfKey;
this._sslDir = optionsArg.sslDir;
this._gitOriginRepo = optionsArg.gitOriginRepo;
this._testMode = optionsArg.testMode;
// write hook config
let config = {
cfEmail: this._cfEmail,
cfKey: this._cfKey
}
plugins.smartfile.memory.toFsSync(
JSON.stringify(config),
plugins.path.join(__dirname, "assets/config.json")
);
// setup Git
if (this._gitOriginRepo) {
plugins.smartgit.init(this._sslDir);
plugins.smartgit.remote.add(this._sslDir, "origin", this._gitOriginRepo);
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
);
};
sslGitOriginPull = () => {
if (this._gitOriginRepo) {
plugins.smartgit.pull(this._sslDir, "origin", "master");
}
};
sslGitOriginAddCommitPush = () => {
if (this._gitOriginRepo) {
plugins.smartgit.add.addAll(this._sslDir);
plugins.smartgit.commit(this._sslDir, "added new SSL certificates and deleted obsolete ones.");
plugins.smartgit.push(this._sslDir, "origin", "master");
}
};
getDomainCert(domainNameArg: string, optionsArg: { force: boolean } = { force: false }) {
let done = plugins.q.defer();
this.sslGitOriginPull();
if (!checkDomainsStillValid(domainNameArg, this._sslDir) || optionsArg.force) {
plugins.shelljs.exec("chmod 700 " + paths.letsencryptSh);
plugins.shelljs.exec("chmod 700 " + paths.certHook);
plugins.smartfile.fs.ensureDir(paths.certDir);
plugins.shelljs.exec(
`bash -c "${paths.letsencryptSh} -c -f ${paths.leShConfig} -d ${domainNameArg} -t dns-01 -k ${paths.certHook} -o ${paths.certDir}"`
);
let fetchedCertsArray: string[] = plugins.smartfile.fs.listFoldersSync(paths.certDir);
if (fetchedCertsArray.indexOf(domainNameArg) != -1) {
updateSslDirSync(this._sslDir, domainNameArg);
}
this.sslGitOriginAddCommitPush();
done.resolve();
} else {
plugins.beautylog.info("certificate for " + domainNameArg + " is still valid! Not fetching new one!");
done.resolve();
};
return done.promise;
};
cleanOldCertificates() {
};
}
export class Certificate {
domainName: string;
creationDate: Date;
expiryDate: Date;
constructor() {
};
}
interface certConfig {
domainName: string;
created: number;
expires: number;
}
let checkDomainsStillValid = (domainNameArg: string, sslDirArg: string): boolean => {
let domainConfigPath = plugins.path.join(sslDirArg, domainNameArg,"config.json");
if (plugins.smartfile.fs.fileExistsSync(domainConfigPath)){
let domainConfig = plugins.smartfile.fs.toObjectSync(
domainConfigPath,
"json"
);
if (Date.now() >= ((domainConfig.expires - 604800) * 1000)) {
return false;
} else {
return true;
}
} else {
return false;
}
}
let updateSslDirSync = (sslDirArg: string, domainNameArg: string) => {
plugins.smartfile.fs.ensureDirSync(sslDirArg);
let domainCertFolder = plugins.path.join(paths.certDir, domainNameArg)
if (plugins.smartfile.fs.listFoldersSync(paths.certDir).indexOf(domainNameArg) != -1) {
plugins.smartfile.fs.copySync(
plugins.path.join(domainCertFolder, "fullchain.pem"),
plugins.path.join(sslDirArg, domainNameArg, "fullchain.pem")
);
plugins.smartfile.fs.copySync(
plugins.path.join(domainCertFolder, "privkey.pem"),
plugins.path.join(sslDirArg, domainNameArg, "privkey.pem")
);
// create cert config
let certRegex = /.*\-([0-9]*)\.pem/;
let certFileNameWithTime: string = plugins.smartfile.fs.listFilesSync(domainCertFolder, certRegex)[0];
let certTime = parseInt(certRegex.exec(certFileNameWithTime)[1]);
let certConfig: certConfig = {
domainName: domainNameArg,
created: certTime,
expires: certTime + 7776000
};
plugins.smartfile.memory.toFsSync(
JSON.stringify(certConfig),
plugins.path.join(sslDirArg, domainNameArg, "config.json")
);
};
}
const enum gitSyncDirection {
toOrigin,
fromOrigin
}
let updateGitOrigin = (syncDirectionArg: gitSyncDirection) => {
};
updateGitOrigin(gitSyncDirection.toOrigin);

View File

@ -0,0 +1,23 @@
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();