diff --git a/app.ts b/app.ts index 4297d05..461f8b6 100644 --- a/app.ts +++ b/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 */ diff --git a/config/app.json b/config/app.json index 7acdb5b..c4b9017 100644 --- a/config/app.json +++ b/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" + } } } diff --git a/dist/app.js b/dist/app.js index d421570..06b2b76 100644 --- a/dist/app.js +++ b/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 \ No newline at end of file diff --git a/dist/app.js.map b/dist/app.js.map index 034b93f..bdc1fc0 100644 --- a/dist/app.js.map +++ b/dist/app.js.map @@ -1 +1 @@ -{"version":3,"file":"app.js","sourceRoot":"","sources":["../app.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"app.js","sourceRoot":"","sources":["../app.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/src/utils/cert.js b/dist/src/utils/cert.js new file mode 100644 index 0000000..6c52943 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/dist/src/utils/cert.js.map b/dist/src/utils/cert.js.map new file mode 100644 index 0000000..68a4762 --- /dev/null +++ b/dist/src/utils/cert.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cert.js","sourceRoot":"","sources":["../../../src/utils/cert.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 819f25b..002e09b 100644 --- a/package-lock.json +++ b/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", diff --git a/package.json b/package.json index 6c1355a..2e4eb8f 100644 --- a/package.json +++ b/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", diff --git a/src/utils/cert.ts b/src/utils/cert.ts new file mode 100644 index 0000000..a4814fb --- /dev/null +++ b/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 +}