SSL works!

This commit is contained in:
Simon Larsen 2022-12-11 08:02:56 +00:00
parent 82c53afbe7
commit dc89ba748d
12 changed files with 126 additions and 213 deletions

View File

@ -0,0 +1 @@
Certs for Status page with custom domains.

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFKTCCBBGgAwIBAgISBI+ZMOTLreLlsVt7PF9qX1u2MA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMjEyMTAxMjA2MTJaFw0yMzAzMTAxMjA2MTFaMB0xGzAZBgNVBAMT
EnN0YXR1cy5nZW5vc3luLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAMgb7+Qhe/f9YCIATTMF7kVGO4Zwd2nJ167vEczBOsHkNspiVD9UPdL0KpFV
L37sqiclcVbUds5QO0h81ckBMNwsRiqSeEj5V5FcRII1PdduROBB7mVfgtuwG2jQ
NtU2IEDAIuxQVTOep3ciZBK4iiwgYttOKbvw5EtjlYKMk2+ZJy2eUjvEstNGkm5b
iekNEvt5ZTInYfWoyCaD9gsjMEaB/ueL21M7jbgJ049n64CYv2robhU9JEQW5p8h
B849YvO+nzhGdCuWRU4FPXzI4aDPs6ONceodg0IIeEbEZMntc1AKXyL7TJp3+kLI
eBpmtwXqQvNuCeqV7QXMK7X7QEsCAwEAAaOCAkwwggJIMA4GA1UdDwEB/wQEAwIF
oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAd
BgNVHQ4EFgQUvnstsk3lPsOYn1nKiQGwdeXVGscwHwYDVR0jBBgwFoAUFC6zF7dY
VsuuUAlA5h+vnYsUwsYwVQYIKwYBBQUHAQEESTBHMCEGCCsGAQUFBzABhhVodHRw
Oi8vcjMuby5sZW5jci5vcmcwIgYIKwYBBQUHMAKGFmh0dHA6Ly9yMy5pLmxlbmNy
Lm9yZy8wHQYDVR0RBBYwFIISc3RhdHVzLmdlbm9zeW4uY29tMEwGA1UdIARFMEMw
CAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9j
cHMubGV0c2VuY3J5cHQub3JnMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHYAtz77
JN+cTbp18jnFulj0bF38Qs96nzXEnh0JgSXttJkAAAGE/CSqhwAABAMARzBFAiAm
PBXbjWWdkehEstdTQ5LoAS1omXJQNnuo1nt2YsoL3gIhAJSewh6nfHL+lXhckXtk
pSkI2qNdq2otCyq3Z9W/Z4YpAHUArfe++nz/EMiLnT2cHj4YarRnKV3PsQwkyoWG
NOvcgooAAAGE/CSqvQAABAMARjBEAiAwAB2istEPxzNrMuw04guD5NyOt/RKDX5l
0y71+H9ekQIgaBg24zPbRNw01G5Y7WUsS9/vBgAEpEtfqpMcedNVwXAwDQYJKoZI
hvcNAQELBQADggEBALU11dZ+HIWxGUz0Q4IqVLfN9dP4cTVdee5GOX8KNh2rmIBv
7YZtuGJVVbILW+D5w/r+3l3z3MFg1ocEHUtfDCtmZes4WyBHXntaJe9JuqY6/ArN
VUIR1r0S6iKmZ17S0/FjruyHOyOw5Zhxix1Q5dMCpuCIfOtOaG8cAf8Qi1re3br9
6dwDpgW5mUQBLA9DGnnXYKvkd/EcEUnf39o2UaDCfFSeQgSu46/98GcOQQ0SpN6c
OqEJ4Dnc6NP/Trmb1nDQbIMx4c5y/q7miDb8URdK3H+EGdmmBie7M46se3i11oGC
u7agSvvW32dShImvoC8pJFAS0cEaAPdODHgg5U0=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAyBvv5CF79/1gIgBNMwXuRUY7hnB3acnXru8RzME6weQ2ymJU
P1Q90vQqkVUvfuyqJyVxVtR2zlA7SHzVyQEw3CxGKpJ4SPlXkVxEgjU9125E4EHu
ZV+C27AbaNA21TYgQMAi7FBVM56ndyJkEriKLCBi204pu/DkS2OVgoyTb5knLZ5S
O8Sy00aSbluJ6Q0S+3llMidh9ajIJoP2CyMwRoH+54vbUzuNuAnTj2frgJi/auhu
FT0kRBbmnyEHzj1i876fOEZ0K5ZFTgU9fMjhoM+zo41x6h2DQgh4RsRkye1zUApf
IvtMmnf6Qsh4Gma3BepC824J6pXtBcwrtftASwIDAQABAoIBABmBohq4f+Y0uCet
VSm/REc1NAonVLk5vpGwLFsmeBhVv/wc+3MVCEpW0AQ1UPgDL48M0T0JmNkkVeIf
81oLGlC+HfV4NPfMPHKtSZg1NBw9FG9nR/1I5tOcx2mdPJgBravDMdBgTvPk8aCY
VBwkxIvqVt9wP5aSlm7bkyeQRoyvRYP+cWnLo1sR6sIEO4SyjtoFzKtp/CrP8MBs
67mVyLPurEqCB0wVDCAr/G2SkJhCUXGvqSzfDU0oGEVGWmb95PQGsOe1KjqegY7A
BTFKMNjbOyncYau4EkZcPAKpv/9Qgo+52bYDKekSxk/ZHitE0TmW4WlC73UMkIQK
yG+Si+kCgYEA8R7+roCHqaxT30bNlJe+FCkVQuXsBJp3TAPUxBSnZZCp0mifEhQc
WGvvG9AOSVmoCPDMGw1xUo+zsUNkxmi+FcLUeAM5hdiV68D1QfjhUlrPidEv74l9
0WlW3+z1tJVbN2WYdXD1JHg5nyq/hEMg4Q/FLEAMj6daIK1PFc+INuMCgYEA1HUS
6q89LzFMSGrGxksxQIoT4cgg1pUA3QLWijEnnT2YDk3Z5iB7F+a1jYQFH1SXBYMx
pIwiHM+qXsumcg0upLPj+ulqGKMt0qPkJTb4w1rGr73BKygDdmFbym3jQxbAqpIY
gW1ZvuOBgR8hstBc0gLUOsgl5TeSel1h/swzpXkCgYEA4ZsmoRAR32gmcdtFr6rr
ZuGpyxZmZ0hAJxfOlEje9+ELhJvvenLmsrUK3PMm6urAltz3nLhPN/jNISb1u891
S9coBcK+p8WnQRciY8AC05O0bDcWqwHyf2YYqxyEKZs15fdhV0GBncX/5DWTTKWi
tfKTgnvLRP5JDhoazUWJJhECgYEAwJaf3z2bKPx3Oe4Q4g+nRenku/a+TcYkUjQQ
ZpTIZDFBdTX9IC6xZqksSmwyeIQlokma5p5hDdzxg5z39MseTQ8Eyp5sHolNMHSA
i3uZZP0Uvpo0UPqkqNr4ajfSmy402Go27JxDjlaNPo8J7R4UBguqdt6X+4C0t1eP
TXmuF4ECgYEAzK8INorcqp4a+rHrtIrBqS6I6q/NtBydZ6QC03qOL/6/6ITdf894
6+S5cksZZqr8HC86f/FOxjGvtLFuuuWoHLTgEra2Blm9lAwrSXIRIgbTDI19rQGq
WaNnUaRke0jQMkNXndToTlU2j7OBI1vGMA5UwrXAD8LWAMrkgzPeKUc=
-----END RSA PRIVATE KEY-----

View File

@ -11,8 +11,6 @@ import {
import UserType from 'Common/Types/UserType';
import Dictionary from 'Common/Types/Dictionary';
import Port from 'Common/Types/Port';
import https from 'https';
import fs from 'fs';
export type RequestHandler = express.RequestHandler;
export type NextFunction = express.NextFunction;
@ -68,33 +66,13 @@ class Express {
public static async launchApplication(
appName: string,
port?: Port,
httpsOptions?: {
port?: Port,
sniCallback?: any
}
): Promise<express.Application> {
if (!this.app) {
this.setupExpress();
}
if (httpsOptions && httpsOptions.port) {
const serverOptions = {
SNICallback: httpsOptions?.sniCallback,
cert: fs.readFileSync(
"/usr/src/app/Certs/Cert.crt"
),
key: fs.readFileSync(
"/usr/src/app/Certs/Key.key"
),
}
https.createServer(serverOptions, this.app).listen(httpsOptions?.port.toNumber(), () => {
logger.info(`${appName} HTTPS server started on port: ${httpsOptions?.port?.toNumber()}`);
});
}
return new Promise<express.Application>((resolve: Function) => {

View File

@ -94,12 +94,8 @@ app.use(logRequest);
const init: Function = async (
appName: string,
port?: Port,
httpsOptions?: {
port?: Port,
sniCallBack?: Function
}
): Promise<ExpressApplication> => {
await Express.launchApplication(appName, port, httpsOptions);
await Express.launchApplication(appName, port);
LocalCache.setString('app', 'name', appName);
CommonAPI(appName);

View File

@ -22,19 +22,10 @@ upstream status-page {
server status-page:3105 weight=10 max_fails=3 fail_timeout=30s;
}
upstream status-page-secure {
server status-page:3107 weight=10 max_fails=3 fail_timeout=30s;
}
upstream status-page-api {
server status-page:3106 weight=10 max_fails=3 fail_timeout=30s;
}
upstream status-page-api-secure {
server status-page:3108 weight=10 max_fails=3 fail_timeout=30s;
}
upstream home {
server home:1444 weight=10 max_fails=3 fail_timeout=30s;
}
@ -43,78 +34,14 @@ upstream workers {
server workers:3452 weight=10 max_fails=3 fail_timeout=30s;
}
# Acme Verification on port 80
server {
listen 80 default_server;
server_name _; # All domains.
proxy_busy_buffers_size 512k;
proxy_buffers 4 512k;
proxy_buffer_size 256k;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# enable WebSockets (for ws://sockjs not connected error in the accounts source: https://stackoverflow.com/questions/41381444/websocket-connection-failed-error-during-websocket-handshake-unexpected-respon)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://status-page;
}
location /status-page {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# enable WebSockets (for ws://sockjs not connected error in the accounts source: https://stackoverflow.com/questions/41381444/websocket-connection-failed-error-during-websocket-handshake-unexpected-respon)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://status-page;
}
location /status-page-api {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# enable WebSockets (for ws://sockjs not connected error in the accounts source: https://stackoverflow.com/questions/41381444/websocket-connection-failed-error-during-websocket-handshake-unexpected-respon)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://status-page-api;
}
# Acme Verification.
location /.well-known {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# enable WebSockets (for ws://sockjs not connected error in the accounts source: https://stackoverflow.com/questions/41381444/websocket-connection-failed-error-during-websocket-handshake-unexpected-respon)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://status-page-api;
}
}
server {
listen 443 default_server ssl; # Port HTTPS
ssl_certificate /etc/nginx/certs/Cert.crt;
ssl_certificate_key /etc/nginx/certs/Key.key;
ssl_certificate /etc/nginx/certs/StatusPageCerts/$ssl_server_name.crt;
ssl_certificate_key /etc/nginx/certs/StatusPageCerts/$ssl_server_name.key;
server_name _; # All domains.
@ -139,12 +66,7 @@ server {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_ssl_server_name on;
proxy_ssl_verify off;
proxy_pass_request_headers on;
proxy_ssl_name $host;
proxy_pass https://status-page-secure;
proxy_pass http://status-page;
}
@ -160,12 +82,7 @@ server {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_ssl_server_name on;
proxy_ssl_name $host;
proxy_ssl_verify off;
proxy_pass_request_headers on;
proxy_pass https://status-page-secure;
proxy_pass http://status-page;
}
location /status-page-api {
@ -178,7 +95,8 @@ server {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass https://status-page-api-secure;
proxy_pass http://status-page-api;
}
# Acme Verification.
@ -192,7 +110,8 @@ server {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass https://status-page-api-secure;
proxy_pass http://status-page-api;
}
}

View File

@ -41,8 +41,6 @@ RUN npm install
COPY ./CommonServer /usr/src/CommonServer
RUN npm run compile
# Install CommonUI
RUN mkdir /usr/src/CommonUI
WORKDIR /usr/src/CommonUI
@ -51,8 +49,6 @@ RUN npm install --force
COPY ./CommonUI /usr/src/CommonUI
RUN npm run compile
#SET ENV Variables
ENV PRODUCTION=true
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
@ -69,11 +65,7 @@ RUN npm install
# - 3105: StatusPage
EXPOSE 3105
# API
EXPOSE 3106
# HTTPS UI
EXPOSE 3107
# HTTPS API
EXPOSE 3108
EXPOSE 3106
{{ if eq .Env.ENVIRONMENT "development" }}
#Run the app

View File

@ -14,9 +14,6 @@ import BadDataException from 'Common/Types/Exception/BadDataException';
import StatusPageDomain from 'Model/Models/StatusPageDomain';
import StatusPageDomainService from 'CommonServer/Services/StatusPageDomainService';
import Port from 'Common/Types/Port';
import tls from 'tls';
import GreenlockCertificateService from 'CommonServer/Services/GreenlockCertificateService';
import GreenlockCertificate from 'Model/Models/GreenlockCertificate';
export const APP_NAME: string = 'status-page-api';
@ -94,42 +91,7 @@ app.get(
const init: Function = async (): Promise<void> => {
try {
// init the app
await App(APP_NAME, new Port(3106), {
port: new Port(3108),
sniCallback: (serverName: string, callback: Function) => {
logger.info("SNI CALLBACK " + serverName);
GreenlockCertificateService.findBy({
query: {
key: serverName,
},
select: {
blob: true,
isKeyPair: true
},
skip: 0,
limit: 10,
props: {
isRoot: true,
},
}).then((result: Array<GreenlockCertificate>) => {
if (result.length === 0) {
return callback(null, null);
}
const certBlob = JSON.parse(result.find((i) => !i.isKeyPair)?.blob || '{}');
const keyBlob = JSON.parse(result.find((i) => i.isKeyPair)?.blob || '{}');
callback(null, tls.createSecureContext({
cert: certBlob.cert,
key: keyBlob.privateKeyPem,
}));
}).catch((err: Error) => {
logger.error(err);
return callback("Server Error. Please try again later.");
});
}
});
await App(APP_NAME, new Port(3106));
// connect to the database.
await PostgresAppInstance.connect(

View File

@ -8,10 +8,7 @@ import Express, {
} from 'CommonServer/Utils/Express';
import logger from 'CommonServer/Utils/Logger';
import Port from 'Common/Types/Port';
import tls from 'tls';
import { PostgresAppInstance } from 'CommonServer/Infrastructure/PostgresDatabase';
import GreenlockCertificateService from 'CommonServer/Services/GreenlockCertificateService';
import GreenlockCertificate from 'Model/Models/GreenlockCertificate';
export const APP_NAME: string = 'status-page';
const app: ExpressApplication = Express.getExpressApp();
@ -27,42 +24,7 @@ app.get('/*', (_req: ExpressRequest, res: ExpressResponse) => {
const init: Function = async (): Promise<void> => {
try {
// init the app
await App(APP_NAME, new Port(3105), {
port: new Port(3107),
sniCallback: (serverName: string, callback: Function) => {
logger.info("SNI CALLBACK " + serverName);
GreenlockCertificateService.findBy({
query: {
key: serverName,
},
select: {
blob: true,
isKeyPair: true
},
skip: 0,
limit: 10,
props: {
isRoot: true,
},
}).then((result: Array<GreenlockCertificate>) => {
if (result.length === 0) {
return callback(null, null);
}
const certBlob = JSON.parse(result.find((i) => !i.isKeyPair)?.blob || '{}');
const keyBlob = JSON.parse(result.find((i) => i.isKeyPair)?.blob || '{}');
callback(null, tls.createSecureContext({
cert: certBlob.cert,
key: keyBlob.privateKeyPem,
}));
}).catch((err: Error) => {
logger.error(err);
return callback("Server Error. Please try again later.");
});
}
});
await App(APP_NAME, new Port(3105));
// connect to the database.
await PostgresAppInstance.connect(

View File

@ -20,7 +20,7 @@
"use-async-effect": "^2.2.6"
},
"scripts": {
"dev": "node --require ts-node/register Serve.ts & nodemon",
"dev": "webpack-dev-server --port=3105 --mode=development & nodemon",
"build": "webpack build --mode=production",
"test": "react-app-rewired test",
"eject": "webpack eject",

View File

@ -19,6 +19,9 @@ import { JSONObject } from 'Common/Types/JSON';
import Response from 'CommonServer/Utils/Response';
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
import axios from 'axios';
import GreenlockCertificate from 'Model/Models/GreenlockCertificate';
import GreenlockCertificateService from 'CommonServer/Services/GreenlockCertificateService';
import fs from 'fs';
const router: ExpressRouter = Express.getRouter();
@ -323,6 +326,51 @@ RunCron(
}
);
RunCron(
'StatusPageCerts:WriteCertsToDisk',
IsDevelopment ? EVERY_MINUTE : EVERY_HOUR,
async () => {
// Fetch all domains where certs are added to greenlock.
const certs: Array<GreenlockCertificate> =
await GreenlockCertificateService.findBy({
query: {
},
select: {
isKeyPair: true,
key: true,
blob: true,
},
limit: LIMIT_MAX,
skip: 0,
props: {
isRoot: true,
},
});
for (const cert of certs) {
if (!cert.isKeyPair) {
continue;
}
const certBlob = certs.find((i) => i.key === cert.key && !i.isKeyPair);
if (!certBlob) {
continue;
}
const key = JSON.parse(cert.blob || '{}').privateKeyPem;
const crt = JSON.parse(certBlob.blob || '{}').cert;
// Write to disk.
fs.writeFileSync(`/usr/src/Certs/StatusPageCerts/${cert.key}.crt`, crt, { flag: 'wx' });
fs.writeFileSync(`/usr/src/Certs/StatusPageCerts/${cert.key}.key`, key, { flag: 'wx' });
}
}
);
RunCron(
'StatusPageCerts:CheckSslProvisioningStatus',
IsDevelopment ? EVERY_MINUTE : EVERY_HOUR,

View File

@ -141,8 +141,6 @@ services:
ports:
- '3105:3105' # HTTP UI Port
- '3106:3106' # HTTP API Port
- '3107:3107' # HTTPS UI Port
- '3108:3108' # HTTPS API Port
{{ if eq .Env.ENVIRONMENT "development" }}
- 9764:9229 # Debugging port.
{{ end }}
@ -163,10 +161,9 @@ services:
depends_on:
- accounts
- dashboard-api
volumes:
- ./Certs:/usr/src/app/Certs
{{ if eq .Env.ENVIRONMENT "development" }}
volumes:
- ./StatusPage:/usr/src/app
- /usr/src/app/node_modules/
- ./Common:/usr/src/Common
@ -249,8 +246,9 @@ services:
links:
- postgres
- mail
{{ if eq .Env.ENVIRONMENT "development" }}
volumes:
- ./Certs:/usr/src/Certs
{{ if eq .Env.ENVIRONMENT "development" }}
- ./Workers:/usr/src/app
# Use node modules of the container and not host system.
# https://stackoverflow.com/questions/29181032/add-a-volume-to-docker-but-exclude-a-sub-folder