Browse Source

Merge pull request #118 from gkrizek/zerossl

Add in SSL Certificate provisioning from ZeroSSL to automatically create certificates on boot
dependabot/npm_and_yarn/ini-1.3.7
Evan Feenstra 4 years ago
committed by GitHub
parent
commit
eeac976820
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      app.ts
  2. 14
      config/app.json
  3. 15
      dist/app.js
  4. 2
      dist/app.js.map
  5. 180
      dist/src/utils/cert.js
  6. 1
      dist/src/utils/cert.js.map
  7. 36
      package-lock.json
  8. 3
      package.json
  9. 167
      src/utils/cert.ts

12
app.ts

@ -12,6 +12,7 @@ import * as socket from './src/utils/socket'
import * as network from './src/network'
import {authModule, unlocker} from './src/auth'
import * as grpc from './src/grpc'
import * as cert from './src/utils/cert'
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname, 'config/app.json'))[env];
@ -50,10 +51,9 @@ async function finishSetup(){
}
function setupApp(){
return new Promise(resolve=>{
return new Promise(async resolve=>{
const app = express();
const server = require("http").Server(app);
app.use(helmet());
app.use(bodyParser.json());
@ -69,6 +69,14 @@ function setupApp(){
app.use('/static', express.static('public'));
app.get('/app', (req, res) => res.send('INDEX'))
if ('ssl' in config && config.ssl.enabled) {
var certData = await cert.getCertificate(config.public_url, config.ssl.port, config.ssl.save)
var credentials = { key: certData?.privateKey.toString(), ca: certData?.caBundle, cert: certData?.certificate };
var server = require("https").createServer(credentials, app);
} else {
var server = require("http").Server(app);
}
server.listen(port, (err) => {
if (err) throw err;
/* eslint-disable no-console */

14
config/app.json

@ -13,7 +13,12 @@
"hub_check_invite_url": "http://lvh.me/check_invite",
"media_host": "localhost:5000",
"tribes_host": "tribes.sphinx.chat",
"public_url": ""
"public_url": "",
"ssl": {
"enabled": false,
"save": true,
"port": "80"
}
},
"production": {
"senza_url": "https://staging.senza.us/api/v2/",
@ -30,6 +35,11 @@
"hub_check_invite_url": "https://hub.sphinx.chat/check_invite",
"media_host": "memes.sphinx.chat",
"tribes_host": "tribes.sphinx.chat",
"public_url": ""
"public_url": "",
"ssl": {
"enabled": false,
"save": true,
"port": "80"
}
}
}

15
dist/app.js

@ -23,6 +23,7 @@ const socket = require("./src/utils/socket");
const network = require("./src/network");
const auth_1 = require("./src/auth");
const grpc = require("./src/grpc");
const cert = require("./src/utils/cert");
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname, 'config/app.json'))[env];
const port = process.env.PORT || config.node_http_port || 3001;
@ -60,9 +61,9 @@ function finishSetup() {
});
}
function setupApp() {
return new Promise(resolve => {
return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c;
const app = express();
const server = require("http").Server(app);
app.use(helmet());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
@ -76,6 +77,14 @@ function setupApp() {
}
app.use('/static', express.static('public'));
app.get('/app', (req, res) => res.send('INDEX'));
if ('ssl' in config && config.ssl.enabled) {
var certData = yield cert.getCertificate(config.public_url, config.ssl.port, config.ssl.save);
var credentials = { key: (_a = certData) === null || _a === void 0 ? void 0 : _a.privateKey.toString(), ca: (_b = certData) === null || _b === void 0 ? void 0 : _b.caBundle, cert: (_c = certData) === null || _c === void 0 ? void 0 : _c.certificate };
var server = require("https").createServer(credentials, app);
}
else {
var server = require("http").Server(app);
}
server.listen(port, (err) => {
if (err)
throw err;
@ -101,6 +110,6 @@ function setupApp() {
});
});
}
});
}));
}
//# sourceMappingURL=app.js.map

2
dist/app.js.map

@ -1 +1 @@
{"version":3,"file":"app.js","sourceRoot":"","sources":["../app.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"app.js","sourceRoot":"","sources":["../app.ts"],"names":[],"mappings":"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"}

180
dist/src/utils/cert.js

@ -0,0 +1,180 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
const express = require("express");
const qs = require('qs');
const axios = require('axios');
var forge = require('node-forge');
const apiUrl = "https://api.zerossl.com";
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function generateCsr(keys, endpoint) {
var csr = forge.pki.createCertificationRequest();
csr.publicKey = keys.publicKey;
csr.setSubject([{
name: 'commonName',
value: endpoint
}]);
csr.sign(keys.privateKey);
if (!csr.verify()) {
throw new Error('=> [ssl] Verification of CSR failed.');
}
var csr = forge.pki.certificationRequestToPem(csr);
return csr.trim();
}
function requestCert(endpoint, csr, apiKey) {
return __awaiter(this, void 0, void 0, function* () {
let res = yield axios({
method: 'post',
url: `${apiUrl}/certificates?access_key=${apiKey}`,
data: qs.stringify({
certificate_domains: endpoint,
certificate_validity_days: '90',
certificate_csr: csr
}),
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
});
return res.data;
});
}
function validateCert(port, data, endpoint, apiKey) {
return __awaiter(this, void 0, void 0, function* () {
const app = express();
var validationObject = data.validation.other_methods[endpoint];
var replacement = new RegExp(`http://${endpoint}`, "g");
var path = validationObject.file_validation_url_http.replace(replacement, "");
yield app.get(path, (req, res) => {
res.set('Content-Type', 'text/plain');
res.send(validationObject.file_validation_content.join('\n'));
});
let server = yield app.listen(port, () => {
console.log(`=> [ssl] validation server started at http://0.0.0.0:${port}`);
});
yield requestValidation(data.id, apiKey);
console.log("=> [ssl] waiting for certificate to be issued");
while (true) {
let certData = yield getCert(data.id, apiKey);
if (certData.status === "issued") {
console.log("=> [ssl] certificate was issued");
break;
}
console.log("=> [ssl] checking certificate again...");
yield sleep(2000);
}
yield server.close(() => {
console.log('=> [ssl] validation server stopped.');
});
return;
});
}
function requestValidation(id, apiKey) {
return __awaiter(this, void 0, void 0, function* () {
let res = yield axios({
method: 'post',
url: `${apiUrl}/certificates/${id}/challenges?access_key=${apiKey}`,
data: qs.stringify({
validation_method: "HTTP_CSR_HASH"
}),
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
});
if (res.data.success === false) {
console.log("=> [ssl] Failed to request certificate validation");
console.log(res.data);
throw new Error("=> [ssl] Failing to provision ssl certificate");
}
return res.data;
});
}
function getCert(id, apiKey) {
return __awaiter(this, void 0, void 0, function* () {
let res = yield axios({
method: 'get',
url: `${apiUrl}/certificates/${id}?access_key=${apiKey}`,
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
});
return res.data;
});
}
function downloadCert(id, apiKey) {
return __awaiter(this, void 0, void 0, function* () {
let res = yield axios({
method: 'get',
url: `${apiUrl}/certificates/${id}/download/return?access_key=${apiKey}`,
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
});
return res.data;
});
}
function getCertificate(endpoint, port, save_ssl) {
return __awaiter(this, void 0, void 0, function* () {
if (fs_1.existsSync(__dirname + "/zerossl/tls.cert") && fs_1.existsSync(__dirname + "/zerossl/tls.key")) {
var certificate = fs_1.readFileSync(__dirname + '/zerossl/tls.cert', 'utf-8').toString();
var caBundle = fs_1.readFileSync(__dirname + '/zerossl/ca.cert', 'utf-8').toString();
var privateKey = fs_1.readFileSync(__dirname + '/zerossl/tls.key', 'utf-8').toString();
return {
privateKey: privateKey,
certificate: certificate,
caBundle: caBundle
};
}
var apiKey = process.env.ZEROSSL_API_KEY;
if (!apiKey) {
throw new Error("=> [ssl] ZEROSSL_API_KEY is not set");
}
var keys = forge.pki.rsa.generateKeyPair(2048);
var csr = generateCsr(keys, endpoint);
console.log("=> [ssl] Generated CSR");
var res = yield requestCert(endpoint, csr, apiKey);
console.log("=> [ssl] Requested certificate");
yield validateCert(port, res, endpoint, apiKey);
var certData = yield downloadCert(res.id, apiKey);
if (save_ssl === true) {
if (!fs_1.existsSync(__dirname + "/zerossl")) {
yield fs_1.mkdirSync(__dirname + "/zerossl");
}
yield fs_1.writeFile(__dirname + "/zerossl/tls.cert", certData["certificate.crt"], function (err) {
if (err) {
return console.log(err);
}
console.log("=> [ssl] wrote tls certificate");
});
yield fs_1.writeFile(__dirname + "/zerossl/ca.cert", certData["ca_bundle.crt"], function (err) {
if (err) {
return console.log(err);
}
console.log("=> [ssl] wrote tls ca bundle");
});
yield fs_1.writeFile(__dirname + "/zerossl/tls.key", forge.pki.privateKeyToPem(keys.privateKey), function (err) {
if (err) {
return console.log(err);
}
console.log("=> [ssl] wrote tls key");
});
}
return {
privateKey: forge.pki.privateKeyToPem(keys.privateKey),
certificate: certData["certificate.crt"],
caBundle: certData["ca_bundle.crt"]
};
});
}
exports.getCertificate = getCertificate;
//# sourceMappingURL=cert.js.map

1
dist/src/utils/cert.js.map

File diff suppressed because one or more lines are too long

36
package-lock.json

@ -669,6 +669,14 @@
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
},
"axios": {
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz",
"integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==",
"requires": {
"follow-redirects": "^1.10.0"
}
},
"babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
@ -789,6 +797,13 @@
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
},
"dependencies": {
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
}
}
},
"bowser": {
@ -2542,6 +2557,11 @@
"vary": "~1.1.2"
},
"dependencies": {
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@ -2680,6 +2700,11 @@
"resolved": "https://registry.npmjs.org/fn-name/-/fn-name-2.0.1.tgz",
"integrity": "sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc="
},
"follow-redirects": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@ -4137,6 +4162,11 @@
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"node-forge": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
},
"node-gyp-build": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.2.tgz",
@ -5039,9 +5069,9 @@
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
"version": "6.9.4",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz",
"integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ=="
},
"randombytes": {
"version": "2.1.0",

3
package.json

@ -26,6 +26,7 @@
"@types/node": "^13.5.1",
"async": "^2.6.2",
"async-lock": "^1.2.2",
"axios": "^0.21.0",
"bitcoin-address-validation": "^1.0.2",
"bytebuffer": "^5.0.1",
"change-case": "^4.1.1",
@ -52,10 +53,12 @@
"mqtt": "^4.0.0",
"multer": "^1.4.2",
"node-fetch": "^2.6.0",
"node-forge": "^0.10.0",
"pg": "^7.9.0",
"pg-hstore": "^2.3.2",
"public-ip": "^4.0.0",
"qrcode": "^1.4.4",
"qs": "^6.9.4",
"read-last-lines": "^1.7.2",
"reflect-metadata": "^0.1.13",
"request": "^2.88.0",

167
src/utils/cert.ts

@ -0,0 +1,167 @@
import { existsSync, readFileSync, writeFile, mkdirSync } from 'fs';
import * as express from 'express'
const qs = require('qs')
const axios = require('axios')
var forge = require('node-forge');
const apiUrl = "https://api.zerossl.com"
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function generateCsr(keys, endpoint) {
var csr = forge.pki.createCertificationRequest();
csr.publicKey = keys.publicKey;
csr.setSubject([{
name: 'commonName',
value: endpoint
}]);
csr.sign(keys.privateKey);
if (!csr.verify()) {
throw new Error('=> [ssl] Verification of CSR failed.');
}
var csr = forge.pki.certificationRequestToPem(csr)
return csr.trim()
}
async function requestCert(endpoint, csr, apiKey) {
let res = await axios({
method: 'post',
url: `${apiUrl}/certificates?access_key=${apiKey}`,
data: qs.stringify({
certificate_domains: endpoint,
certificate_validity_days: '90',
certificate_csr: csr
}),
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
})
return res.data
}
async function validateCert(port, data, endpoint, apiKey) {
const app = express()
var validationObject = data.validation.other_methods[endpoint]
var replacement = new RegExp(`http://${endpoint}`, "g");
var path = validationObject.file_validation_url_http.replace(replacement, "")
await app.get(path, (req, res) => {
res.set('Content-Type', 'text/plain');
res.send(validationObject.file_validation_content.join('\n'))
});
let server = await app.listen(port, () => {
console.log(`=> [ssl] validation server started at http://0.0.0.0:${port}`);
});
await requestValidation(data.id, apiKey)
console.log("=> [ssl] waiting for certificate to be issued")
while (true) {
let certData = await getCert(data.id, apiKey)
if (certData.status === "issued") {
console.log("=> [ssl] certificate was issued")
break
}
console.log("=> [ssl] checking certificate again...")
await sleep(2000);
}
await server.close(() => {
console.log('=> [ssl] validation server stopped.')
})
return
}
async function requestValidation(id, apiKey) {
let res = await axios({
method: 'post',
url: `${apiUrl}/certificates/${id}/challenges?access_key=${apiKey}`,
data: qs.stringify({
validation_method: "HTTP_CSR_HASH"
}),
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
})
if (res.data.success === false) {
console.log("=> [ssl] Failed to request certificate validation")
console.log(res.data)
throw new Error("=> [ssl] Failing to provision ssl certificate")
}
return res.data
}
async function getCert(id, apiKey) {
let res = await axios({
method: 'get',
url: `${apiUrl}/certificates/${id}?access_key=${apiKey}`,
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
})
return res.data
}
async function downloadCert(id, apiKey) {
let res = await axios({
method: 'get',
url: `${apiUrl}/certificates/${id}/download/return?access_key=${apiKey}`,
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
})
return res.data
}
async function getCertificate(endpoint, port, save_ssl) {
if (existsSync(__dirname + "/zerossl/tls.cert") && existsSync(__dirname + "/zerossl/tls.key")) {
var certificate = readFileSync(__dirname + '/zerossl/tls.cert', 'utf-8').toString();
var caBundle = readFileSync(__dirname + '/zerossl/ca.cert', 'utf-8').toString();
var privateKey = readFileSync(__dirname + '/zerossl/tls.key', 'utf-8').toString();
return {
privateKey: privateKey,
certificate: certificate,
caBundle: caBundle
}
}
var apiKey = process.env.ZEROSSL_API_KEY
if (!apiKey) {
throw new Error("=> [ssl] ZEROSSL_API_KEY is not set")
}
var keys = forge.pki.rsa.generateKeyPair(2048)
var csr = generateCsr(keys, endpoint)
console.log("=> [ssl] Generated CSR")
var res = await requestCert(endpoint, csr, apiKey)
console.log("=> [ssl] Requested certificate")
await validateCert(port, res, endpoint, apiKey)
var certData = await downloadCert(res.id, apiKey)
if (save_ssl === true) {
if (!existsSync(__dirname + "/zerossl")) {
await mkdirSync(__dirname + "/zerossl");
}
await writeFile(__dirname + "/zerossl/tls.cert", certData["certificate.crt"], function (err) {
if (err) {
return console.log(err);
}
console.log("=> [ssl] wrote tls certificate");
})
await writeFile(__dirname + "/zerossl/ca.cert", certData["ca_bundle.crt"], function (err) {
if (err) {
return console.log(err);
}
console.log("=> [ssl] wrote tls ca bundle");
})
await writeFile(__dirname + "/zerossl/tls.key", forge.pki.privateKeyToPem(keys.privateKey), function (err) {
if (err) {
return console.log(err);
}
console.log("=> [ssl] wrote tls key");
})
}
return {
privateKey: forge.pki.privateKeyToPem(keys.privateKey),
certificate: certData["certificate.crt"],
caBundle: certData["ca_bundle.crt"]
}
}
export {
getCertificate
}
Loading…
Cancel
Save