pad.libre-service.eu-etherpad/src/node/hooks/express.js

194 lines
6.8 KiB
JavaScript
Raw Normal View History

'use strict';
const _ = require('underscore');
const cookieParser = require('cookie-parser');
const express = require('express');
const expressSession = require('express-session');
const fs = require('fs');
const hooks = require('../../static/js/pluginfw/hooks');
const log4js = require('log4js');
const SessionStore = require('../db/SessionStore');
const settings = require('../utils/Settings');
const stats = require('../stats');
const util = require('util');
const logger = log4js.getLogger('http');
let serverName;
exports.server = null;
const closeServer = async () => {
if (exports.server == null) return;
logger.info('Closing HTTP server...');
await Promise.all([
util.promisify(exports.server.close.bind(exports.server))(),
hooks.aCallAll('expressCloseServer'),
]);
exports.server = null;
logger.info('HTTP server closed');
};
exports.createServer = async () => {
2020-11-23 19:24:19 +01:00
console.log('Report bugs at https://github.com/ether/etherpad-lite/issues');
serverName = `Etherpad ${settings.getGitCommit()} (https://etherpad.org)`;
console.log(`Your Etherpad version is ${settings.getEpVersion()} (${settings.getGitCommit()})`);
await exports.restartServer();
2020-11-23 19:24:19 +01:00
if (settings.ip === '') {
// using Unix socket for connectivity
console.log(`You can access your Etherpad instance using the Unix socket at ${settings.port}`);
} else {
console.log(`You can access your Etherpad instance at http://${settings.ip}:${settings.port}/`);
}
if (!_.isEmpty(settings.users)) {
console.log(`The plugin admin page is at http://${settings.ip}:${settings.port}/admin/plugins`);
} else {
console.warn('Admin username and password not set in settings.json. ' +
'To access admin please uncomment and edit "users" in settings.json');
}
const env = process.env.NODE_ENV || 'development';
if (env !== 'production') {
console.warn('Etherpad is running in Development mode. This mode is slower for users and ' +
'less secure than production mode. You should set the NODE_ENV environment ' +
'variable to production by using: export NODE_ENV=production');
2018-04-03 11:59:10 +02:00
}
2020-11-23 19:24:19 +01:00
};
exports.restartServer = async () => {
await closeServer();
const app = express(); // New syntax for express v3
2012-11-22 10:12:58 +01:00
if (settings.ssl) {
2020-11-23 19:24:19 +01:00
console.log('SSL -- enabled');
console.log(`SSL -- server key file: ${settings.ssl.key}`);
console.log(`SSL -- Certificate Authority's certificate file: ${settings.ssl.cert}`);
const options = {
2020-11-23 19:24:19 +01:00
key: fs.readFileSync(settings.ssl.key),
cert: fs.readFileSync(settings.ssl.cert),
2012-11-22 10:12:58 +01:00
};
if (settings.ssl.ca) {
options.ca = [];
for (let i = 0; i < settings.ssl.ca.length; i++) {
const caFileName = settings.ssl.ca[i];
options.ca.push(fs.readFileSync(caFileName));
}
}
const https = require('https');
exports.server = https.createServer(options, app);
2012-11-22 10:12:58 +01:00
} else {
const http = require('http');
exports.server = http.createServer(app);
2012-11-22 10:12:58 +01:00
}
2020-11-23 19:24:19 +01:00
app.use((req, res, next) => {
// res.header("X-Frame-Options", "deny"); // breaks embedded pads
if (settings.ssl) {
// we use SSL
2020-11-23 19:24:19 +01:00
res.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
}
2015-04-24 15:17:49 +02:00
// Stop IE going into compatability mode
// https://github.com/ether/etherpad-lite/issues/2547
2020-11-23 19:24:19 +01:00
res.header('X-UA-Compatible', 'IE=Edge,chrome=1');
referer: change referrer policy. Stop sending referers as much as possible Pull request with discussion: https://github.com/ether/etherpad-lite/pull/3636 What's already there: * `meta name=referrer`: already done in 1.6.1: https://github.com/ether/etherpad-lite/pull/3044 https://caniuse.com/#feat=referrer-policy https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-delivery-meta (Chrome>=78, Firefox>=70, Safari>=13, Opera>=64, ~IE[1], ~Edge[1]) The previous two commits (by @joelpurra) I backported in this batch: * `<a rel=noreferrer>`: a pull request denied before: https://github.com/ether/etherpad-lite/pull/2498 https://html.spec.whatwg.org/multipage/links.html#link-type-noreferrer https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types (Firefox>=37, I can't find more info about support) This commit adds the following: * `<a rel="noopener">`: fixing a not-so-well-known way to extract referer https://html.spec.whatwg.org/multipage/links.html#link-type-noopener (Chrome>=49, Firefox>=52, Safari>=10.1, Opera>=36, !IE, !Edge) * `Referrer-Policy: same-origin`: the last bastion of referrer security https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy (Chrome>=61, Firefox>=52, Safari>=11.1, Opera>=48, !IE, !Edge) meta name=referrer wasn't enough. I happened to leak a few referrers with my Firefox browser, though for some browsers it could have been enough. [1] IE>=11, Edge>=18 use a different syntax for meta name=referrer, making it most probably incompatible (but I may be wrong on that, they may support both, but I have no way to test it currently). The next Edge release will be based on Chromium, so for that the Chrome version applies.
2019-11-23 08:18:07 +01:00
// Enable a strong referrer policy. Same-origin won't drop Referers when
// loading local resources, but it will drop them when loading foreign resources.
// It's still a last bastion of referrer security. External URLs should be
// already marked with rel="noreferer" and user-generated content pages are already
// marked with <meta name="referrer" content="no-referrer">
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
// https://github.com/ether/etherpad-lite/pull/3636
2020-11-23 19:24:19 +01:00
res.header('Referrer-Policy', 'same-origin');
referer: change referrer policy. Stop sending referers as much as possible Pull request with discussion: https://github.com/ether/etherpad-lite/pull/3636 What's already there: * `meta name=referrer`: already done in 1.6.1: https://github.com/ether/etherpad-lite/pull/3044 https://caniuse.com/#feat=referrer-policy https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-delivery-meta (Chrome>=78, Firefox>=70, Safari>=13, Opera>=64, ~IE[1], ~Edge[1]) The previous two commits (by @joelpurra) I backported in this batch: * `<a rel=noreferrer>`: a pull request denied before: https://github.com/ether/etherpad-lite/pull/2498 https://html.spec.whatwg.org/multipage/links.html#link-type-noreferrer https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types (Firefox>=37, I can't find more info about support) This commit adds the following: * `<a rel="noopener">`: fixing a not-so-well-known way to extract referer https://html.spec.whatwg.org/multipage/links.html#link-type-noopener (Chrome>=49, Firefox>=52, Safari>=10.1, Opera>=36, !IE, !Edge) * `Referrer-Policy: same-origin`: the last bastion of referrer security https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy (Chrome>=61, Firefox>=52, Safari>=11.1, Opera>=48, !IE, !Edge) meta name=referrer wasn't enough. I happened to leak a few referrers with my Firefox browser, though for some browsers it could have been enough. [1] IE>=11, Edge>=18 use a different syntax for meta name=referrer, making it most probably incompatible (but I may be wrong on that, they may support both, but I have no way to test it currently). The next Edge release will be based on Chromium, so for that the Chrome version applies.
2019-11-23 08:18:07 +01:00
// send git version in the Server response header if exposeVersion is true.
if (settings.exposeVersion) {
2020-11-23 19:24:19 +01:00
res.header('Server', serverName);
}
next();
});
if (settings.trustProxy) {
/*
* If 'trust proxy' === true, the clients IP address in req.ip will be the
* left-most entry in the X-Forwarded-* header.
*
* Source: https://expressjs.com/en/guide/behind-proxies.html
*/
app.enable('trust proxy');
}
2015-04-07 14:55:05 +02:00
// Measure response time
app.use((req, res, next) => {
const stopWatch = stats.timer('httpRequests').start();
const sendFn = res.send.bind(res);
res.send = (...args) => { stopWatch.end(); sendFn(...args); };
next();
});
// If the log level specified in the config file is WARN or ERROR the application server never
// starts listening to requests as reported in issue #158. Not installing the log4js connect
// logger when the log level has a higher severity than INFO since it would not log at that level
// anyway.
if (!(settings.loglevel === 'WARN' && settings.loglevel === 'ERROR')) {
app.use(log4js.connectLogger(logger, {
level: log4js.levels.DEBUG,
format: ':status, :method :url',
}));
}
exports.sessionMiddleware = expressSession({
secret: settings.sessionKey,
store: new SessionStore(),
resave: false,
saveUninitialized: true,
// Set the cookie name to a javascript identifier compatible string. Makes code handling it
// cleaner :)
name: 'express_sid',
proxy: true,
cookie: {
sameSite: settings.cookie.sameSite,
// The automatic express-session mechanism for determining if the application is being served
// over ssl is similar to the one used for setting the language cookie, which check if one of
// these conditions is true:
//
// 1. we are directly serving the nodejs application over SSL, using the "ssl" options in
// settings.json
//
// 2. we are serving the nodejs application in plaintext, but we are using a reverse proxy
// that terminates SSL for us. In this case, the user has to set trustProxy = true in
// settings.json, and the information wheter the application is over SSL or not will be
// extracted from the X-Forwarded-Proto HTTP header
//
// Please note that this will not be compatible with applications being served over http and
// https at the same time.
//
// reference: https://github.com/expressjs/session/blob/v1.17.0/README.md#cookiesecure
secure: 'auto',
2020-11-23 19:24:19 +01:00
},
});
app.use(exports.sessionMiddleware);
app.use(cookieParser(settings.sessionKey, {}));
2020-11-23 19:24:19 +01:00
hooks.callAll('expressConfigure', {app});
hooks.callAll('expressCreateServer', {app, server: exports.server});
await util.promisify(exports.server.listen).bind(exports.server)(settings.port, settings.ip);
};
exports.shutdown = async (hookName, context) => {
await closeServer();
};