From 294a444603ada0804755a9ccb39a8989623be5d2 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 27 Feb 2020 13:09:12 -0500 Subject: [PATCH 01/10] WIP removing defaults from the example config file --- config/config.example.js | 56 ++++------------------------------ lib/defaults.js | 65 ++++++++++++++++++++++++++++++++++++++++ lib/load-config.js | 2 +- 3 files changed, 71 insertions(+), 52 deletions(-) create mode 100644 lib/defaults.js diff --git a/config/config.example.js b/config/config.example.js index 90e96a66a..e2bcea5bc 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -16,37 +16,7 @@ var _domain = 'http://localhost:3000/'; // requiring admins to preserve it is unnecessarily confusing var domain = ' ' + _domain; -// Content-Security-Policy -var baseCSP = [ - "default-src 'none'", - "style-src 'unsafe-inline' 'self' " + domain, - "font-src 'self' data:" + domain, - - /* child-src is used to restrict iframes to a set of allowed domains. - * connect-src is used to restrict what domains can connect to the websocket. - * - * it is recommended that you configure these fields to match the - * domain which will serve your CryptPad instance. - */ - "child-src blob: *", - // IE/Edge - "frame-src blob: *", - - /* this allows connections over secure or insecure websockets - if you are deploying to production, you'll probably want to remove - the ws://* directive, and change '*' to your domain - */ - "connect-src 'self' ws: wss: blob:" + domain, - - // data: is used by codemirror - "img-src 'self' data: blob:" + domain, - "media-src * blob:", - - // for accounts.cryptpad.fr authentication and cross-domain iframe sandbox - "frame-ancestors *", - "" -]; - +var Default = require("../lib/defaults"); module.exports = { /* ===================== @@ -113,34 +83,18 @@ module.exports = { * These settings may vary widely depending on your needs * Examples are provided below */ - httpHeaders: { - "X-XSS-Protection": "1; mode=block", - "X-Content-Type-Options": "nosniff", - "Access-Control-Allow-Origin": "*" - }, + httpHeaders: Default.httpHeaders(), - contentSecurity: baseCSP.join('; ') + - "script-src 'self'" + domain, + contentSecurity: Default.contentSecurity(domain), // CKEditor and OnlyOffice require significantly more lax content security policy in order to function. - padContentSecurity: baseCSP.join('; ') + - "script-src 'self' 'unsafe-eval' 'unsafe-inline'" + domain, + padContentSecurity: Default.padContentSecurity(domain), /* Main pages * add exceptions to the router so that we can access /privacy.html * and other odd pages */ - mainPages: [ - 'index', - 'privacy', - 'terms', - 'about', - 'contact', - 'what-is-cryptpad', - 'features', - 'faq', - 'maintenance' - ], + mainPages: Default.mainPages(), /* ===================== * Subscriptions diff --git a/lib/defaults.js b/lib/defaults.js new file mode 100644 index 000000000..f5d87b20b --- /dev/null +++ b/lib/defaults.js @@ -0,0 +1,65 @@ +var Default = module.exports; + +Default.commonCSP = function (domain) { + // Content-Security-Policy + return [ + "default-src 'none'", + "style-src 'unsafe-inline' 'self' " + domain, + "font-src 'self' data:" + domain, + + /* child-src is used to restrict iframes to a set of allowed domains. + * connect-src is used to restrict what domains can connect to the websocket. + * + * it is recommended that you configure these fields to match the + * domain which will serve your CryptPad instance. + */ + "child-src blob: *", + // IE/Edge + "frame-src blob: *", + + /* this allows connections over secure or insecure websockets + if you are deploying to production, you'll probably want to remove + the ws://* directive, and change '*' to your domain + */ + "connect-src 'self' ws: wss: blob:" + domain, + + // data: is used by codemirror + "img-src 'self' data: blob:" + domain, + "media-src * blob:", + + // for accounts.cryptpad.fr authentication and cross-domain iframe sandbox + "frame-ancestors *", + "" + ]; +}; + +Default.contentSecurity = function (domain) { + return Default.commonCSP(domain).join('; ') + "script-src 'self'" + domain; +}; + +Default.padContentSecurity = function (domain) { + return Default.commonCSP(domain).join('; ') + "script-src 'self' 'unsafe-eval' 'unsafe-inline'" + domain; +}; + +Default.httpHeaders = function () { + return { + "X-XSS-Protection": "1; mode=block", + "X-Content-Type-Options": "nosniff", + "Access-Control-Allow-Origin": "*" + }; +}; + +Default.mainPages = function () { + return [ + 'index', + 'privacy', + 'terms', + 'about', + 'contact', + 'what-is-cryptpad', + 'features', + 'faq', + 'maintenance' + ]; +}; + diff --git a/lib/load-config.js b/lib/load-config.js index 0756c2df4..1c502226a 100644 --- a/lib/load-config.js +++ b/lib/load-config.js @@ -1,7 +1,7 @@ /* jslint node: true */ "use strict"; var config; -var configPath = process.env.CRYPTPAD_CONFIG || "../config/config"; +var configPath = process.env.CRYPTPAD_CONFIG || "../config/config.js"; try { config = require(configPath); if (config.adminEmail === 'i.did.not.read.my.config@cryptpad.fr') { From 3df47a1415fed91e9a795199d1f49d0ff8f98e96 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 27 Feb 2020 13:24:19 -0500 Subject: [PATCH 02/10] continued WIP config reorganization --- config/config.example.js | 67 ++++++++++++++-------------------------- lib/defaults.js | 19 ++++++++++++ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/config/config.example.js b/config/config.example.js index e2bcea5bc..0754b325b 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -19,6 +19,30 @@ var domain = ' ' + _domain; var Default = require("../lib/defaults"); module.exports = { + /* Sales coming from your server will be identified by your domain + * + * If you are using CryptPad in a business context, please consider taking a support contract + * by contacting sales@cryptpad.fr + */ + myDomain: _domain, + + // the address you want to bind to, :: means all ipv4 and ipv6 addresses + // this may not work on all operating systems + httpAddress: '::', + + // the port on which your httpd will listen + httpPort: 3000, + + // This is for allowing the cross-domain iframe to function when developing + httpSafePort: 3001, + + // This is for deployment in production, CryptPad uses a separate origin (domain) to host the + // cross-domain iframe. It can simply host the same content as CryptPad. + // httpSafeOrigin: "https://some-other-domain.xyz", + + httpUnsafeOrigin: domain, + + /* ===================== * Admin * ===================== */ @@ -51,22 +75,6 @@ module.exports = { * Infra setup * ===================== */ - // the address you want to bind to, :: means all ipv4 and ipv6 addresses - // this may not work on all operating systems - httpAddress: '::', - - // the port on which your httpd will listen - httpPort: 3000, - - // This is for allowing the cross-domain iframe to function when developing - httpSafePort: 3001, - - // This is for deployment in production, CryptPad uses a separate origin (domain) to host the - // cross-domain iframe. It can simply host the same content as CryptPad. - // httpSafeOrigin: "https://some-other-domain.xyz", - - httpUnsafeOrigin: domain, - /* Your CryptPad server will share this value with clients * via its /api/config endpoint. * @@ -127,13 +135,6 @@ module.exports = { */ adminEmail: 'i.did.not.read.my.config@cryptpad.fr', - /* Sales coming from your server will be identified by your domain - * - * If you are using CryptPad in a business context, please consider taking a support contract - * by contacting sales@cryptpad.fr - */ - myDomain: _domain, - /* * If you are using CryptPad internally and you want to increase the per-user storage limit, * change the following value. @@ -171,12 +172,6 @@ module.exports = { * STORAGE * ===================== */ - /* By default the CryptPad server will run scheduled tasks every five minutes - * If you want to run scheduled tasks in a separate process (like a crontab) - * you can disable this behaviour by setting the following value to true - */ - disableIntegratedTasks: false, - /* Pads that are not 'pinned' by any registered user can be set to expire * after a configurable number of days of inactivity (default 90 days). * The value can be changed or set to false to remove expiration. @@ -205,20 +200,6 @@ module.exports = { */ maxUploadSize: 20 * 1024 * 1024, - /* ===================== - * HARDWARE RELATED - * ===================== */ - - /* CryptPad's file storage adaptor closes unused files after a configurable - * number of milliseconds (default 30000 (30 seconds)) - */ - channelExpirationMs: 30000, - - /* CryptPad's file storage adaptor is limited by the number of open files. - * When the adaptor reaches openFileLimit, it will clean up older files - */ - openFileLimit: 2048, - /* ===================== * DATABASE VOLUMES * ===================== */ diff --git a/lib/defaults.js b/lib/defaults.js index f5d87b20b..fc807a2c7 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -63,3 +63,22 @@ Default.mainPages = function () { ]; }; +/* By default the CryptPad server will run scheduled tasks every five minutes + * If you want to run scheduled tasks in a separate process (like a crontab) + * you can disable this behaviour by setting the following value to true + */ + //disableIntegratedTasks: false, + + /* CryptPad's file storage adaptor closes unused files after a configurable + * number of milliseconds (default 30000 (30 seconds)) + */ +// channelExpirationMs: 30000, + + /* CryptPad's file storage adaptor is limited by the number of open files. + * When the adaptor reaches openFileLimit, it will clean up older files + */ + //openFileLimit: 2048, + + + + From 3cf09924aec87b7bd98876c54c19cd84d25d3213 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 10:46:44 -0500 Subject: [PATCH 03/10] fix default CSP headers --- lib/defaults.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/defaults.js b/lib/defaults.js index fc807a2c7..6d8ec5e04 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -1,7 +1,9 @@ var Default = module.exports; Default.commonCSP = function (domain) { + domain = ' ' + domain; // Content-Security-Policy + return [ "default-src 'none'", "style-src 'unsafe-inline' 'self' " + domain, @@ -34,11 +36,11 @@ Default.commonCSP = function (domain) { }; Default.contentSecurity = function (domain) { - return Default.commonCSP(domain).join('; ') + "script-src 'self'" + domain; + return (Default.commonCSP(domain).join('; ') + "script-src 'self' " + domain).replace(/\s+/g, ' '); }; Default.padContentSecurity = function (domain) { - return Default.commonCSP(domain).join('; ') + "script-src 'self' 'unsafe-eval' 'unsafe-inline'" + domain; + return (Default.commonCSP(domain).join('; ') + "script-src 'self' 'unsafe-eval' 'unsafe-inline' " + domain).replace(/\s+/g, ' '); }; Default.httpHeaders = function () { From 94d8e7f2c2d8c3065e8cfa14db6c7dcaf15a7487 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 10:49:20 -0500 Subject: [PATCH 04/10] provide a consistent type for 'customLimits' --- lib/historyKeeper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index ed67602bd..95cb43e56 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -68,7 +68,7 @@ module.exports.create = function (config, cb) { allowSubscriptions: config.allowSubscriptions, myDomain: config.myDomain, mySubdomain: config.mySubdomain, - customLimits: config.customLimits, + customLimits: config.customLimits || {}, // FIXME this attribute isn't in the default conf // but it is referenced in Quota domain: config.domain From de6b9e2228c7537f77b3c80688d1468fa1c97d3e Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 10:50:30 -0500 Subject: [PATCH 05/10] apply some defaults when loading the config --- lib/load-config.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/load-config.js b/lib/load-config.js index 1c502226a..0b861b826 100644 --- a/lib/load-config.js +++ b/lib/load-config.js @@ -18,5 +18,23 @@ try { } config = require("../config/config.example"); } + +var isPositiveNumber = function (n) { + return (!isNaN(n) && n >= 0); +}; + +if (!isPositiveNumber(config.inactiveTime)) { + config.inactiveTime = 90; +} +if (!isPositiveNumber(config.archiveRetentionTime)) { + config.archiveRetentionTime = 90; +} +if (!isPositiveNumber(config.maxUploadSize)) { + config.maxUploadSize = 20 * 1024 * 1024; +} +if (!isPositiveNumber(config.defaultStorageLimit)) { + config.defaultStorageLimit = 50 * 1024 * 1024; +} + module.exports = config; From 03ff9bd0d77499be4bdb75efa414533065f85de2 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 11:45:22 -0500 Subject: [PATCH 06/10] start deprecating usage of myDomain --- lib/historyKeeper.js | 3 ++- scripts/check-accounts.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index 95cb43e56..1d938ccfd 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -66,7 +66,8 @@ module.exports.create = function (config, cb) { flushCache: config.flushCache, adminEmail: config.adminEmail, allowSubscriptions: config.allowSubscriptions, - myDomain: config.myDomain, + myDomain: config.httpUnsafeOrigin, + // XXX not included in the config... mySubdomain: config.mySubdomain, customLimits: config.customLimits || {}, // FIXME this attribute isn't in the default conf diff --git a/scripts/check-accounts.js b/scripts/check-accounts.js index 4d0067d43..9e75da0c6 100644 --- a/scripts/check-accounts.js +++ b/scripts/check-accounts.js @@ -4,7 +4,7 @@ var Config = require("../lib/load-config"); var Package = require("../package.json"); var body = JSON.stringify({ - domain: Config.myDomain, + domain: Config.myDomain || Config.httpUnsafeOrigin, subdomain: Config.mySubdomain || null, adminEmail: Config.adminEmail, version: Package.version, From e70c3ff0abced42a5c426edd65c4237067258473 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 12:01:52 -0500 Subject: [PATCH 07/10] add some default config values and warn if provided values seem incorrect --- server.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/server.js b/server.js index ddf9fc8b0..d7f663d4e 100644 --- a/server.js +++ b/server.js @@ -8,6 +8,7 @@ var Package = require('./package.json'); var Path = require("path"); var nThen = require("nthen"); var Util = require("./lib/common-util"); +var Default = require("./lib/defaults"); var config = require("./lib/load-config"); @@ -35,6 +36,47 @@ if (process.env.PACKAGE) { FRESH_KEY = +new Date(); } +(function () { + // you absolutely must provide an 'httpUnsafeOrigin' + if (typeof(config.httpUnsafeOrigin) !== 'string') { + throw new Error("No 'httpUnsafeOrigin' provided"); + } + + config.httpUnsafeOrigin = config.httpUnsafeOrigin.trim(); + + // fall back to listening on a local address + // if httpAddress is not a string + if (typeof(config.httpAddress) !== 'string') { + config.httpAddress = '127.0.0.1'; + } + + // listen on port 3000 if a valid port number was not provided + if (typeof(config.httpPort) !== 'number' || config.httpPort > 65535) { + config.httpPort = 3000; + } + + if (typeof(httpSafeOrigin) !== 'string') { + if (typeof(config.httpSafePort) !== 'number') { + config.httpSafePort = config.httpPort + 1; + } + + if (DEV_MODE) { return; } + console.log(` + m m mm mmmmm mm m mmmmm mm m mmm m + # # # ## # "# #"m # # #"m # m" " # + " #"# # # # #mmmm" # #m # # # #m # # mm # + ## ##" #mm# # "m # # # # # # # # # + # # # # # " # ## mm#mm # ## "mmm" # +`); + + console.log("\nNo 'httpSafeOrigin' provided."); + console.log("Your configuration probably isn't taking advantage of all of CryptPad's security features!"); + console.log("This is acceptable for development, otherwise your users may be at risk.\n"); + + console.log("Serving sandboxed content via port %s.\nThis is probably not what you want for a production instance!\n", config.httpSafePort); + } +}()); + var configCache = {}; config.flushCache = function () { configCache = {}; @@ -47,11 +89,21 @@ config.flushCache = function () { const clone = (x) => (JSON.parse(JSON.stringify(x))); var setHeaders = (function () { - if (typeof(config.httpHeaders) !== 'object') { return function () {}; } + // load the default http headers unless the admin has provided their own via the config file + var headers; + + var custom = config.httpHeaders; + // if the admin provided valid http headers then use them + if (custom && typeof(custom) === 'object' && !Array.isArray(custom)) { + headers = clone(custom); + } else { + // otherwise use the default + headers = Default.httpHeaders(); + } - const headers = clone(config.httpHeaders); - if (config.contentSecurity) { - headers['Content-Security-Policy'] = clone(config.contentSecurity); + // next define the base Content Security Policy (CSP) headers + if (typeof(config.contentSecurity) === 'string') { + headers['Content-Security-Policy'] = config.contentSecurity; if (!/;$/.test(headers['Content-Security-Policy'])) { headers['Content-Security-Policy'] += ';' } if (headers['Content-Security-Policy'].indexOf('frame-ancestors') === -1) { // backward compat for those who do not merge the new version of the config @@ -59,10 +111,16 @@ var setHeaders = (function () { // It also fixes the cross-domain iframe. headers['Content-Security-Policy'] += "frame-ancestors *;"; } + } else { + // use the default CSP headers constructed with your domain + headers['Content-Security-Policy'] = Default.contentSecurity(config.httpUnsafeOrigin); } + const padHeaders = clone(headers); - if (config.padContentSecurity) { - padHeaders['Content-Security-Policy'] = clone(config.padContentSecurity); + if (typeof(config.padContentSecurity) === 'string') { + padHeaders['Content-Security-Policy'] = config.padContentSecurity; + } else { + padHeaders['Content-Security-Policy'] = Default.padContentSecurity(config.httpUnsafeOrigin); } if (Object.keys(headers).length) { return function (req, res) { @@ -116,7 +174,7 @@ app.use(Express.static(__dirname + '/www')); // FIXME I think this is a regression caused by a recent PR // correct this hack without breaking the contributor's intended behaviour. -var mainPages = config.mainPages || ['index', 'privacy', 'terms', 'about', 'contact']; +var mainPages = config.mainPages || Default.mainPages(); var mainPagePattern = new RegExp('^\/(' + mainPages.join('|') + ').html$'); app.get(mainPagePattern, Express.static(__dirname + '/customize')); app.get(mainPagePattern, Express.static(__dirname + '/customize.dist')); @@ -163,7 +221,7 @@ var serveConfig = (function () { removeDonateButton: (config.removeDonateButton === true), allowSubscriptions: (config.allowSubscriptions === true), websocketPath: config.externalWebsocketURL, - httpUnsafeOrigin: config.httpUnsafeOrigin.replace(/^\s*/, ''), + httpUnsafeOrigin: config.httpUnsafeOrigin, adminEmail: config.adminEmail, adminKeys: admins, inactiveTime: config.inactiveTime, From c371a257bfd7ae116b044c53d22512bd6ab90f0e Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 13:17:02 -0500 Subject: [PATCH 08/10] WIP example config reorganization --- config/config.example.js | 79 ++++++++++++---------------------------- 1 file changed, 23 insertions(+), 56 deletions(-) diff --git a/config/config.example.js b/config/config.example.js index a15ea0992..ae5b11702 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -1,46 +1,23 @@ -/* - globals module -*/ -var _domain = 'http://localhost:3000/'; - -// You can `kill -USR2` the node process and it will write out a heap dump. -// If your system doesn't support dumping, comment this out and install with -// `npm install --production` -// See: https://strongloop.github.io/strongloop.com/strongblog/how-to-heap-snapshots/ - -// to enable this feature, uncomment the line below: -// require('heapdump'); +/* globals module */ +module.exports = { + httpUnsafeOrigin: 'http://localhost:3000/', // XXX -// we prepend a space because every usage expects it -// requiring admins to preserve it is unnecessarily confusing -var domain = ' ' + _domain; + // This is for deployment in production, CryptPad uses a separate origin (domain) to host the + // cross-domain iframe. It can simply host the same content as CryptPad. + // httpSafeOrigin: "https://some-other-domain.xyz", -var Default = require("../lib/defaults"); -module.exports = { - /* Sales coming from your server will be identified by your domain - * - * If you are using CryptPad in a business context, please consider taking a support contract - * by contacting sales@cryptpad.fr - */ - myDomain: _domain, // the address you want to bind to, :: means all ipv4 and ipv6 addresses // this may not work on all operating systems - httpAddress: '::', + //httpAddress: '::', // the port on which your httpd will listen - httpPort: 3000, + //httpPort: 3000, // This is for allowing the cross-domain iframe to function when developing httpSafePort: 3001, - // This is for deployment in production, CryptPad uses a separate origin (domain) to host the - // cross-domain iframe. It can simply host the same content as CryptPad. - // httpSafeOrigin: "https://some-other-domain.xyz", - - httpUnsafeOrigin: domain, - /* ===================== * Admin @@ -53,9 +30,11 @@ module.exports = { * which can be found on the settings page for registered users. * Entries should be strings separated by a comma. */ +/* adminKeys: [ //"https://my.awesome.website/user/#/1/cryptpad-user1/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=", ], +*/ /* CryptPad's administration panel includes a "support" tab * wherein administrators with a secret key can view messages @@ -69,6 +48,7 @@ module.exports = { * */ // supportMailboxPublicKey: "", + supportMailboxPublicKey: 'oxuMPm3xXHFALYaeFdAepVZyCpEPNTAPBO8MlpjdQw8=', /* ===================== * Infra setup @@ -86,23 +66,6 @@ module.exports = { */ //externalWebsocketURL: 'wss://api.yourdomain.com/cryptpad_websocket - /* CryptPad can be configured to send customized HTTP Headers - * These settings may vary widely depending on your needs - * Examples are provided below - */ - httpHeaders: Default.httpHeaders(), - - contentSecurity: Default.contentSecurity(domain), - - // CKEditor and OnlyOffice require significantly more lax content security policy in order to function. - padContentSecurity: Default.padContentSecurity(domain), - - /* Main pages - * add exceptions to the router so that we can access /privacy.html - * and other odd pages - */ - mainPages: Default.mainPages(), - /* ===================== * Subscriptions * ===================== */ @@ -122,7 +85,7 @@ module.exports = { * If you chose B, set 'allowSubscriptions' to false. * If you chose C, set 'removeDonateButton' to true */ - allowSubscriptions: true, + //allowSubscriptions: true, removeDonateButton: false, /* @@ -142,7 +105,7 @@ module.exports = { * development. Running a public instance that provides a "better deal" than cryptpad.fr * is effectively using the project against itself. */ - defaultStorageLimit: 50 * 1024 * 1024, + //defaultStorageLimit: 50 * 1024 * 1024, /* * CryptPad allows administrators to give custom limits to their friends. @@ -152,8 +115,8 @@ module.exports = { * * hint: 1GB is 1024 * 1024 * 1024 bytes */ +/* customLimits: { - /* "https://my.awesome.website/user/#/1/cryptpad-user1/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=": { limit: 20 * 1024 * 1024 * 1024, plan: 'insider', @@ -164,8 +127,8 @@ module.exports = { plan: 'insider', note: 'storage space donated by my.awesome.website' } - */ }, +*/ /* ===================== * STORAGE @@ -175,9 +138,11 @@ module.exports = { * after a configurable number of days of inactivity (default 90 days). * The value can be changed or set to false to remove expiration. * Expired pads can then be removed using a cron job calling the - * `delete-inactive.js` script with node + * `evict-inactive.js` script with node + * + * defaults to 90 days if nothing is provided */ - inactiveTime: 90, // days + //inactiveTime: 90, // days /* CryptPad archives some data instead of deleting it outright. * This archived data still takes up space and so you'll probably still want to @@ -190,14 +155,16 @@ module.exports = { * deletion. Set this value to the number of days you'd like to retain * archived data before it's removed permanently. * + * defaults to 15 days if nothing is provided */ - archiveRetentionTime: 15, + //archiveRetentionTime: 15, /* Max Upload Size (bytes) * this sets the maximum size of any one file uploaded to the server. * anything larger than this size will be rejected + * defaults to 20MB if no value is provided */ - maxUploadSize: 20 * 1024 * 1024, + //maxUploadSize: 20 * 1024 * 1024, // XXX premiumUploadSize: 100 * 1024 * 1024, From 92896fb9197c60d7c9aabec6a08360a6f4ceacf6 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 14:24:03 -0500 Subject: [PATCH 09/10] rearrange a big part of the example config file --- config/config.example.js | 212 ++++++++++++++++++++++++--------------- 1 file changed, 132 insertions(+), 80 deletions(-) diff --git a/config/config.example.js b/config/config.example.js index ae5b11702..273c196d2 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -1,30 +1,100 @@ /* globals module */ -module.exports = { - httpUnsafeOrigin: 'http://localhost:3000/', // XXX - // This is for deployment in production, CryptPad uses a separate origin (domain) to host the - // cross-domain iframe. It can simply host the same content as CryptPad. - // httpSafeOrigin: "https://some-other-domain.xyz", +/* DISCLAIMER: + + There are two recommended methods of running a CryptPad instance: + + 1. Using a standalone nodejs server without HTTPS (suitable for local development) + 2. Using NGINX to serve static assets and to handle HTTPS for API server's websocket traffic + + We do not officially recommend or support Apache, Docker, Kubernetes, Traefik, or any other configuration. + Support requests for such setups should be directed to their authors. + If you're having difficulty difficulty configuring your instance + we suggest that you join the project's IRC/Matrix channel. + If you don't have any difficulty configuring your instance and you'd like to + support us for the work that went into making it pain-free we are quite happy + to accept donations via our opencollective page: https://opencollective.com/cryptpad + +*/ +module.exports = { +/* CryptPad is designed to serve its content over two domains. + * Account passwords and cryptographic content is handled on the 'main' domain, + * while the user interface is loaded on a 'sandbox' domain + * which can only access information which the main domain willingly shares. + * + * In the event of an XSS vulnerability in the UI (that's bad) + * this system prevents attackers from gaining access to your account (that's good). + * + * Most problems with new instances are related to this system blocking access + * because of incorrectly configured sandboxes. If you only see a white screen + * when you try to load CryptPad, this is probably the cause. + * + * PLEASE READ THE FOLLOWING COMMENTS CAREFULLY. + * + */ + +/* httpUnsafeOrigin is the URL that clients will enter to load your instance. + * Any other URL that somehow points to your instance is supposed to be blocked. + * The default provided below assumes you are loading CryptPad from a server + * which is running on the same machine, using port 3000. + * + * In a production instance this should be available ONLY over HTTPS + * using the default port for HTTPS (443) ie. https://cryptpad.fr + * In such a case this should be handled by NGINX, as documented in + * cryptpad/docs/example.nginx.conf (see the $main_domain variable) + * + */ + httpUnsafeOrigin: 'http://localhost:3000/', + +/* httpSafeOrigin is the URL that is used for the 'sandbox' described above. + * If you're testing or developing with CryptPad on your local machine then + * it is appropriate to leave this blank. The default behaviour is to serve + * the main domain over port 3000 and to serve the content over port 3001. + * + * This is not appropriate in a production environment where invasive networks + * may filter traffic going over abnormal ports. + * To correctly configure your production instance you must provide a URL + * with a different domain (a subdomain is sufficient). + * It will be used to load the UI in our 'sandbox' system. + * + * This value corresponds to the $sandbox_domain variable + * in the example nginx file. + * + * CUSTOMIZE AND UNCOMMENT THIS FOR PRODUCTION INSTALLATIONS. + */ + // httpSafeOrigin: "https://some-other-domain.xyz", - // the address you want to bind to, :: means all ipv4 and ipv6 addresses - // this may not work on all operating systems +/* httpAddress specifies the address on which the nodejs server + * should be accessible. By default it will listen on 127.0.0.1 + * (IPv4 localhost on most systems). If you want it to listen on + * all addresses, including IPv6, set this to '::'. + * + */ //httpAddress: '::', - // the port on which your httpd will listen +/* httpPort specifies on which port the nodejs server should listen. + * By default it will serve content over port 3000, which is suitable + * for both local development and for use with the provided nginx example, + * which will proxy websocket traffic to your node server. + * + */ //httpPort: 3000, - // This is for allowing the cross-domain iframe to function when developing - httpSafePort: 3001, - +/* httpSafePort allows you to specify an alternative port from which + * the node process should serve sandboxed assets. The default value is + * that of your httpPort + 1. You probably don't need to change this. + * + */ + //httpSafePort: 3001, /* ===================== * Admin * ===================== */ /* - * CryptPad now contains an administration panel. Its access is restricted to specific + * CryptPad contains an administration panel. Its access is restricted to specific * users using the following list. * To give access to the admin panel to a user account, just add their user id, * which can be found on the settings page for registered users. @@ -48,87 +118,42 @@ module.exports = { * */ // supportMailboxPublicKey: "", - supportMailboxPublicKey: 'oxuMPm3xXHFALYaeFdAepVZyCpEPNTAPBO8MlpjdQw8=', - /* ===================== - * Infra setup - * ===================== */ - - /* Your CryptPad server will share this value with clients - * via its /api/config endpoint. + /* We're very proud that CryptPad is available to the public as free software! + * We do, however, still need to pay our bills as we develop the platform. * - * If you want to host your API and asset servers on different hosts - * specify a URL for your API server websocket endpoint, like so: - * wss://api.yourdomain.com/cryptpad_websocket + * By default CryptPad will prompt users to consider donating to + * our OpenCollective campaign. We publish the state of our finances periodically + * so you can decide for yourself whether our expenses are reasonable. * - * Otherwise, leave this commented and your clients will use the default - * websocket (wss://yourdomain.com/cryptpad_websocket) + * You can disable any solicitations for donations by setting 'removeDonateButton' to true, + * but we'd appreciate it if you didn't! */ - //externalWebsocketURL: 'wss://api.yourdomain.com/cryptpad_websocket + //removeDonateButton: false, - /* ===================== - * Subscriptions - * ===================== */ - - /* Limits, Donations, Subscriptions and Contact - * - * By default, CryptPad limits every registered user to 50MB of storage. It also shows a - * subscribe button which allows them to upgrade to a paid account. We handle payment, - * and keep 50% of the proceeds to fund ongoing development. - * - * You can: - * A: leave things as they are - * B: disable accounts but display a donate button - * C: hide any reference to paid accounts or donation - * - * If you chose A then there's nothing to do. - * If you chose B, set 'allowSubscriptions' to false. - * If you chose C, set 'removeDonateButton' to true - */ - //allowSubscriptions: true, - removeDonateButton: false, - - /* - * By default, CryptPad also contacts our accounts server once a day to check for changes in - * the people who have accounts. This check-in will also send the version of your CryptPad - * instance and your email so we can reach you if we are aware of a serious problem. We will - * never sell it or send you marketing mail. If you want to block this check-in and remain - * completely invisible, set this and allowSubscriptions both to false. + /* CryptPad will display a point of contact for your instance on its contact page + * (/contact.html) if you provide it below. */ adminEmail: 'i.did.not.read.my.config@cryptpad.fr', /* - * If you are using CryptPad internally and you want to increase the per-user storage limit, - * change the following value. + * By default, CryptPad contacts one of our servers once a day. + * This check-in will also send some very basic information about your instance including its + * version and the adminEmail so we can reach you if we are aware of a serious problem. + * We will never sell it or send you marketing mail. * - * Please note: This limit is what makes people subscribe and what pays for CryptPad - * development. Running a public instance that provides a "better deal" than cryptpad.fr - * is effectively using the project against itself. + * If you want to block this check-in and remain set 'blockDailyCheck' to true. */ - //defaultStorageLimit: 50 * 1024 * 1024, + //blockDailyCheck: false, /* - * CryptPad allows administrators to give custom limits to their friends. - * add an entry for each friend, identified by their user id, - * which can be found on the settings page. Include a 'limit' (number of bytes), - * a 'plan' (string), and a 'note' (string). + * By default users get 50MB of storage by registering on an instance. + * You can set this value to whatever you want. * - * hint: 1GB is 1024 * 1024 * 1024 bytes + * hint: 50MB is 50 * 1024 * 1024 */ -/* - customLimits: { - "https://my.awesome.website/user/#/1/cryptpad-user1/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=": { - limit: 20 * 1024 * 1024 * 1024, - plan: 'insider', - note: 'storage space donated by my.awesome.website' - }, - "https://my.awesome.website/user/#/1/cryptpad-user2/GdflkgdlkjeworijfkldfsdflkjeEAsdlEnkbx1vVOo=": { - limit: 10 * 1024 * 1024 * 1024, - plan: 'insider', - note: 'storage space donated by my.awesome.website' - } - }, -*/ + //defaultStorageLimit: 50 * 1024 * 1024, + /* ===================== * STORAGE @@ -166,8 +191,35 @@ module.exports = { */ //maxUploadSize: 20 * 1024 * 1024, - // XXX - premiumUploadSize: 100 * 1024 * 1024, + /* + * CryptPad allows administrators to give custom limits to their friends. + * add an entry for each friend, identified by their user id, + * which can be found on the settings page. Include a 'limit' (number of bytes), + * a 'plan' (string), and a 'note' (string). + * + * hint: 1GB is 1024 * 1024 * 1024 bytes + */ +/* + customLimits: { + "https://my.awesome.website/user/#/1/cryptpad-user1/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=": { + limit: 20 * 1024 * 1024 * 1024, + plan: 'insider', + note: 'storage space donated by my.awesome.website' + }, + "https://my.awesome.website/user/#/1/cryptpad-user2/GdflkgdlkjeworijfkldfsdflkjeEAsdlEnkbx1vVOo=": { + limit: 10 * 1024 * 1024 * 1024, + plan: 'insider', + note: 'storage space donated by my.awesome.website' + } + }, +*/ + + /* Users with premium accounts (those with a plan included in their customLimit) + * can benefit from an increased upload size limit. By default they are restricted to the same + * upload size as any other registered user. + * + */ + //premiumUploadSize: 100 * 1024 * 1024, /* ===================== * DATABASE VOLUMES From 2f00ff327861b2d26b0d426e4669d2d135831991 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 14:24:53 -0500 Subject: [PATCH 10/10] simplify check-in deactivation --- lib/commands/quota.js | 1 + lib/historyKeeper.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/commands/quota.js b/lib/commands/quota.js index 74c4eca44..72d04cc68 100644 --- a/lib/commands/quota.js +++ b/lib/commands/quota.js @@ -38,6 +38,7 @@ Quota.updateCachedLimits = function (Env, cb) { if (Env.adminEmail === false) { Quota.applyCustomLimits(Env); if (Env.allowSubscriptions === false) { return; } + if (Env.blockDailyCheck === true) { return; } throw new Error("allowSubscriptions must be false if adminEmail is false"); } diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index 1d938ccfd..3a27b228a 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -65,7 +65,9 @@ module.exports.create = function (config, cb) { WARN: WARN, flushCache: config.flushCache, adminEmail: config.adminEmail, - allowSubscriptions: config.allowSubscriptions, + allowSubscriptions: config.allowSubscriptions === true, + blockDailyCheck: config.blockDailyCheck === true, + myDomain: config.httpUnsafeOrigin, // XXX not included in the config... mySubdomain: config.mySubdomain,