2020-10-03 22:52:01 +02:00
const _ = require ( 'underscore' ) ;
2020-10-04 00:10:00 +02:00
const cookieParser = require ( 'cookie-parser' ) ;
2020-10-03 22:52:01 +02:00
const express = require ( 'express' ) ;
2020-10-04 00:10:00 +02:00
const expressSession = require ( 'express-session' ) ;
2020-10-03 22:52:01 +02:00
const fs = require ( 'fs' ) ;
const hooks = require ( '../../static/js/pluginfw/hooks' ) ;
2020-10-04 00:10:00 +02:00
const log4js = require ( 'log4js' ) ;
2020-10-03 22:52:01 +02:00
const npm = require ( 'npm/lib/npm' ) ;
const path = require ( 'path' ) ;
2020-10-04 00:10:00 +02:00
const sessionStore = require ( '../db/SessionStore' ) ;
2020-10-03 22:52:01 +02:00
const settings = require ( '../utils/Settings' ) ;
2020-10-04 00:10:00 +02:00
const stats = require ( '../stats' ) ;
2020-09-21 06:42:29 +02:00
const util = require ( 'util' ) ;
2012-07-03 23:30:40 +02:00
2020-10-04 00:10:00 +02:00
const logger = log4js . getLogger ( 'http' ) ;
2020-10-03 22:52:01 +02:00
let serverName ;
2012-07-03 23:30:40 +02:00
2020-09-21 06:42:29 +02:00
exports . server = null ;
exports . createServer = async ( ) => {
2020-11-23 19:24:19 +01:00
console . log ( 'Report bugs at https://github.com/ether/etherpad-lite/issues' ) ;
2012-07-03 23:30:40 +02:00
2019-04-16 00:54:54 +02:00
serverName = ` Etherpad ${ settings . getGitCommit ( ) } (https://etherpad.org) ` ;
2019-04-16 00:34:29 +02:00
2018-08-27 01:29:37 +02:00
console . log ( ` Your Etherpad version is ${ settings . getEpVersion ( ) } ( ${ settings . getGitCommit ( ) } ) ` ) ;
2012-07-03 23:30:40 +02:00
2020-09-21 06:42:29 +02:00
await exports . restartServer ( ) ;
2012-07-03 23:30:40 +02:00
2020-11-23 19:24:19 +01:00
if ( settings . ip === '' ) {
2020-03-30 00:27:22 +02:00
// 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 } / ` ) ;
}
2019-04-16 00:17:56 +02:00
if ( ! _ . isEmpty ( settings . users ) ) {
2018-08-27 01:29:37 +02:00
console . log ( ` The plugin admin page is at http:// ${ settings . ip } : ${ settings . port } /admin/plugins ` ) ;
2019-04-16 00:17:56 +02:00
} else {
2012-07-03 23:30:40 +02:00
console . warn ( "Admin username and password not set in settings.json. To access admin please uncomment and edit 'users' in settings.json" ) ;
}
2019-04-16 00:17:56 +02:00
2020-10-03 22:52:01 +02:00
const env = process . env . NODE _ENV || 'development' ;
2019-04-16 00:17:56 +02:00
if ( env !== 'production' ) {
2020-11-23 19:24:19 +01:00
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
} ;
2012-07-03 23:30:40 +02:00
2020-09-21 06:42:29 +02:00
exports . restartServer = async ( ) => {
if ( exports . server ) {
2020-11-23 19:24:19 +01:00
console . log ( 'Restarting express server' ) ;
2020-09-21 06:42:29 +02:00
await util . promisify ( exports . server . close ) . bind ( exports . server ) ( ) ;
2012-07-03 23:30:40 +02:00
}
2020-10-03 22:52:01 +02:00
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' ) ;
2018-08-27 01:29:37 +02:00
console . log ( ` SSL -- server key file: ${ settings . ssl . key } ` ) ;
console . log ( ` SSL -- Certificate Authority's certificate file: ${ settings . ssl . cert } ` ) ;
2019-04-16 00:34:29 +02:00
2020-10-03 22:52:01 +02:00
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
} ;
2019-04-16 00:17:56 +02:00
2015-04-22 20:29:19 +02:00
if ( settings . ssl . ca ) {
options . ca = [ ] ;
2020-10-03 22:52:01 +02:00
for ( let i = 0 ; i < settings . ssl . ca . length ; i ++ ) {
const caFileName = settings . ssl . ca [ i ] ;
2015-04-22 20:29:19 +02:00
options . ca . push ( fs . readFileSync ( caFileName ) ) ;
}
}
2019-04-16 00:34:29 +02:00
2020-10-03 22:52:01 +02:00
const https = require ( 'https' ) ;
2020-09-21 06:42:29 +02:00
exports . server = https . createServer ( options , app ) ;
2012-11-22 10:12:58 +01:00
} else {
2020-10-03 22:52:01 +02:00
const http = require ( 'http' ) ;
2020-09-21 06:42:29 +02:00
exports . server = http . createServer ( app ) ;
2012-11-22 10:12:58 +01:00
}
2012-07-03 23:30:40 +02:00
2020-11-23 19:24:19 +01:00
app . use ( ( req , res , next ) => {
2014-06-17 13:21:38 +02:00
// res.header("X-Frame-Options", "deny"); // breaks embedded pads
2019-04-16 00:17:56 +02:00
if ( settings . ssl ) {
// we use SSL
2020-11-23 19:24:19 +01:00
res . header ( 'Strict-Transport-Security' , 'max-age=31536000; includeSubDomains' ) ;
2013-03-14 23:03:20 +01:00
}
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' ) ;
2019-04-15 16:02:46 +02:00
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
2019-04-15 16:02:46 +02: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 ) ;
2019-04-15 16:02:46 +02:00
}
2012-07-03 23:30:40 +02:00
next ( ) ;
} ) ;
2019-04-16 00:17:56 +02:00
if ( settings . trustProxy ) {
2020-04-14 01:10:19 +02:00
/ *
* If 'trust proxy' === true , the client ’ s 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
* /
2013-04-24 12:19:41 +02:00
app . enable ( 'trust proxy' ) ;
}
2015-04-07 14:55:05 +02:00
2020-10-04 00:10:00 +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
} ,
2020-10-04 00:10:00 +02:00
} ) ;
app . use ( exports . sessionMiddleware ) ;
app . use ( cookieParser ( settings . sessionKey , { } ) ) ;
2020-11-23 19:24:19 +01:00
hooks . callAll ( 'expressConfigure' , { app } ) ;
2020-09-21 06:42:29 +02:00
hooks . callAll ( 'expressCreateServer' , { app , server : exports . server } ) ;
2012-07-03 23:30:40 +02:00
2020-09-21 06:42:29 +02:00
await util . promisify ( exports . server . listen ) . bind ( exports . server ) ( settings . port , settings . ip ) ;
} ;
exports . shutdown = async ( hookName , context ) => {
if ( ! exports . server ) return ;
await util . promisify ( exports . server . close ) . bind ( exports . server ) ( ) ;
} ;