Don't start in production if important security fields are misconfigured

This commit is contained in:
Alice Gaudon 2020-07-15 15:06:13 +02:00
parent 7a96790f97
commit 9ce345d99d
7 changed files with 59 additions and 3 deletions

View File

@ -8,7 +8,7 @@ export default {
public_url: "http://localhost:4899", public_url: "http://localhost:4899",
public_websocket_url: "ws://localhost:4899", public_websocket_url: "ws://localhost:4899",
port: 4899, port: 4899,
gitlab_webhook_token: 'secret', gitlab_webhook_token: 'default',
mysql: { mysql: {
connectionLimit: 10, connectionLimit: 10,
host: "localhost", host: "localhost",
@ -23,7 +23,7 @@ export default {
prefix: 'wms' prefix: 'wms'
}, },
session: { session: {
secret: "very_secret_not_known", secret: 'default',
cookie: { cookie: {
secure: false, secure: false,
maxAge: 30 * 24 * 3600 * 1000, // 30 days maxAge: 30 * 24 * 3600 * 1000, // 30 days

View File

@ -62,6 +62,11 @@ export default abstract class Application {
// Register all components and alike // Register all components and alike
await this.init(); await this.init();
// Security
if (process.env.NODE_ENV === 'production') {
await this.checkSecuritySettings();
}
// Init express // Init express
const app = express(); const app = express();
const initRouter = express.Router(); const initRouter = express.Router();
@ -171,6 +176,12 @@ export default abstract class Application {
return false; return false;
} }
private async checkSecuritySettings(): Promise<void> {
for (const component of this.components) {
await component.checkSecuritySettings();
}
}
async stop(): Promise<void> { async stop(): Promise<void> {
Logger.info('Stopping application...'); Logger.info('Stopping application...');

View File

@ -2,11 +2,16 @@ import {Express, Router} from "express";
import Logger from "./Logger"; import Logger from "./Logger";
import {sleep} from "./Utils"; import {sleep} from "./Utils";
import Application from "./Application"; import Application from "./Application";
import config from "config";
import SecurityError from "./SecurityError";
export default abstract class ApplicationComponent<T> { export default abstract class ApplicationComponent<T> {
private val?: T; private val?: T;
protected app?: Application; protected app?: Application;
public async checkSecuritySettings(): Promise<void> {
}
public async start(app: Express): Promise<void> { public async start(app: Express): Promise<void> {
} }
@ -60,4 +65,10 @@ export default abstract class ApplicationComponent<T> {
Logger.error(e, `An error occurred while closing the ${thingName}.`); Logger.error(e, `An error occurred while closing the ${thingName}.`);
} }
} }
}
protected checkSecurityConfigField(field: string) {
if (!config.has(field) || config.get<string>(field) === 'default') {
throw new SecurityError('field not configured.');
}
}
}

8
src/SecurityError.ts Normal file
View File

@ -0,0 +1,8 @@
export default class SecurityError implements Error {
public readonly name: string = 'SecurityError';
public readonly message: string;
public constructor(message: string) {
this.message = message;
}
}

View File

@ -6,6 +6,10 @@ import {ForbiddenHttpError} from "../HttpError";
import Logger from "../Logger"; import Logger from "../Logger";
export default class AutoUpdateComponent extends ApplicationComponent<void> { export default class AutoUpdateComponent extends ApplicationComponent<void> {
public async checkSecuritySettings(): Promise<void> {
this.checkSecurityConfigField('gitlab_webhook_token');
}
public async init(router: Router): Promise<void> { public async init(router: Router): Promise<void> {
router.post('/update/push.json', (req, res) => { router.post('/update/push.json', (req, res) => {
const token = req.header('X-Gitlab-Token'); const token = req.header('X-Gitlab-Token');

View File

@ -1,8 +1,21 @@
import ApplicationComponent from "../ApplicationComponent"; import ApplicationComponent from "../ApplicationComponent";
import {Express} from "express"; import {Express} from "express";
import Mail from "../Mail"; import Mail from "../Mail";
import config from "config";
import SecurityError from "../SecurityError";
export default class MailComponent extends ApplicationComponent<void> { export default class MailComponent extends ApplicationComponent<void> {
public async checkSecuritySettings(): Promise<void> {
if (!config.get<boolean>('mail.secure')) {
throw new SecurityError('Cannot set mail.secure to false');
}
if (config.get<boolean>('mail.allow_invalid_tls')) {
throw new SecurityError('Cannot set mail.allow_invalid_tls to true');
}
}
public async start(app: Express): Promise<void> { public async start(app: Express): Promise<void> {
await this.prepare('Mail connection', () => Mail.prepare()); await this.prepare('Mail connection', () => Mail.prepare());
} }

View File

@ -4,6 +4,7 @@ import config from "config";
import RedisComponent from "./RedisComponent"; import RedisComponent from "./RedisComponent";
import flash from "connect-flash"; import flash from "connect-flash";
import {Router} from "express"; import {Router} from "express";
import SecurityError from "../SecurityError";
export default class SessionComponent extends ApplicationComponent<void> { export default class SessionComponent extends ApplicationComponent<void> {
private readonly storeComponent: RedisComponent; private readonly storeComponent: RedisComponent;
@ -13,6 +14,14 @@ export default class SessionComponent extends ApplicationComponent<void> {
this.storeComponent = storeComponent; this.storeComponent = storeComponent;
} }
public async checkSecuritySettings(): Promise<void> {
this.checkSecurityConfigField('session.secret');
if (!config.get<boolean>('session.cookie.secure')) {
throw new SecurityError('Cannot set cookie secure field to false.');
}
}
public async init(router: Router): Promise<void> { public async init(router: Router): Promise<void> {
router.use(session({ router.use(session({
saveUninitialized: true, saveUninitialized: true,