summaryrefslogtreecommitdiffstats
path: root/machines/jormungand
diff options
context:
space:
mode:
authorDominique Martinet @ jormungand <asmadeus@codewreck.org>2020-09-01 20:37:01 +0200
committerDominique Martinet @ jormungand <asmadeus@codewreck.org>2020-09-01 20:37:03 +0200
commit5c999697f8f4ed0c90f31a69f3950c19f67eb0a5 (patch)
tree8281b27ded3562ca56505147e83a4b0e8e86dfa6 /machines/jormungand
parent1f40be29fe8cc60cf534e37404011f253dbb266e (diff)
cryptpad: use nginx more profusely
There is a problem in using a simple proxy when it comes to blob, the server does not handle $HOME and its node_modules/cryptpad dir being different correctly. Using nginx lets us: - bind data read only for the server home and serve it where it expects - disable /register kludgingly - probably a bit more efficient? meh.
Diffstat (limited to 'machines/jormungand')
-rw-r--r--machines/jormungand/cryptpad.config.js8
-rw-r--r--machines/jormungand/nginx.nix205
2 files changed, 202 insertions, 11 deletions
diff --git a/machines/jormungand/cryptpad.config.js b/machines/jormungand/cryptpad.config.js
index 6963d16..973be97 100644
--- a/machines/jormungand/cryptpad.config.js
+++ b/machines/jormungand/cryptpad.config.js
@@ -87,7 +87,7 @@ module.exports = {
* that of your httpPort + 1. You probably don't need to change this.
*
*/
- httpSafePort: 3001,
+ //httpSafePort: 3001,
/* CryptPad will launch a child process for every core available
* in order to perform CPU-intensive tasks in parallel.
@@ -137,6 +137,12 @@ module.exports = {
*/
removeDonateButton: true,
+ /* undocumented: disable anonymous file upload in drive */
+ disableAnonymousStore: true,
+
+ /* undocumented: disable crowdfunding messages */
+ disableCrowdfundingMessages: true,
+
/* CryptPad will display a point of contact for your instance on its contact page
* (/contact.html) if you provide it below.
*/
diff --git a/machines/jormungand/nginx.nix b/machines/jormungand/nginx.nix
index c10d7f0..6812a93 100644
--- a/machines/jormungand/nginx.nix
+++ b/machines/jormungand/nginx.nix
@@ -1,10 +1,201 @@
{ config, pkgs, ... }:
-{
+let
+ cryptpadConfig = ''
+ # CryptPad serves static assets over these two domains.
+ # `main_domain` is what users will enter in their address bar.
+ # Privileged computation such as key management is handled in this scope
+ # UI content is loaded via the `sandbox_domain`.
+ # "Content Security Policy" headers prevent content loaded via the sandbox
+ # from accessing privileged information.
+ # These variables must be different to take advantage of CryptPad's sandboxing techniques.
+ # In the event of an XSS vulnerability in CryptPad's front-end code
+ # this will limit the amount of information accessible to attackers.
+ set $main_domain "cryptpad.codewreck.org";
+ set $sandbox_domain "cryptpad-sandbox.codewreck.org";
+
+ # CryptPad's dynamic content (websocket traffic and encrypted blobs)
+ # can be served over separate domains. Using dedicated domains (or subdomains)
+ # for these purposes allows you to move them to a separate machine at a later date
+ # if you find that a single machine cannot handle all of your users.
+ # If you don't use dedicated domains, this can be the same as $main_domain
+ # If you do, they'll be added as exceptions to any rules which block connections to remote domains.
+ set $api_domain "cryptpad.codewreck.org";
+ set $files_domain "cryptpad.codewreck.org";
+
+ add_header X-XSS-Protection "1; mode=block";
+ add_header X-Content-Type-Options nosniff;
+ add_header Access-Control-Allow-Origin "*";
+ # add_header X-Frame-Options "SAMEORIGIN";
+
+ # Insert the path to your CryptPad repository root here
+ root ${pkgs.cryptpad}/lib/node_modules/cryptpad;
+ index index.html;
+ error_page 404 /customize.dist/404.html;
+
+ # any static assets loaded with "ver=" in their URL will be cached for a year
+ if ($args ~ ver=) {
+ set $cacheControl max-age=31536000;
+ }
+ # Will not set any header if it is emptystring
+ add_header Cache-Control $cacheControl;
+
+ # CSS can be dynamically set inline, loaded from the same domain, or from $main_domain
+ set $styleSrc "'unsafe-inline' 'self' ''${main_domain}";
+
+ # connect-src restricts URLs which can be loaded using script interfaces
+ set $connectSrc "'self' https://''${main_domain} ''${main_domain} https://''${api_domain} blob: wss://''${api_domain} ''${api_domain} ''${files_domain}";
+
+ # fonts can be loaded from data-URLs or the main domain
+ set $fontSrc "'self' data: ''${main_domain}";
+
+ # images can be loaded from anywhere, though we'd like to deprecate this as it allows the use of images for tracking
+ set $imgSrc "'self' data: * blob: ''${main_domain}";
+
+ # frame-src specifies valid sources for nested browsing contexts.
+ # this prevents loading any iframes from anywhere other than the sandbox domain
+ set $frameSrc "'self' ''${sandbox_domain} blob:";
+
+ # specifies valid sources for loading media using video or audio
+ set $mediaSrc "'self' data: * blob: ''${main_domain}";
+
+ # defines valid sources for webworkers and nested browser contexts
+ # deprecated in favour of worker-src and frame-src
+ set $childSrc "https://''${main_domain}";
+
+ # specifies valid sources for Worker, SharedWorker, or ServiceWorker scripts.
+ # supercedes child-src but is unfortunately not yet universally supported.
+ set $workerSrc "https://''${main_domain}";
+
+ # script-src specifies valid sources for javascript, including inline handlers
+ set $scriptSrc "'self' resource: ''${main_domain}";
+
+ set $unsafe 0;
+ # the following assets are loaded via the sandbox domain
+ # they unfortunately still require exceptions to the sandboxing to work correctly.
+ if ($uri = "/pad/inner.html") { set $unsafe 1; }
+ if ($uri = "/sheet/inner.html") { set $unsafe 1; }
+ if ($uri ~ ^\/common\/onlyoffice\/.*\/index\.html.*$) { set $unsafe 1; }
+
+ # everything except the sandbox domain is a privileged scope, as they might be used to handle keys
+ if ($host != $sandbox_domain) { set $unsafe 0; }
+
+ # privileged contexts allow a few more rights than unprivileged contexts, though limits are still applied
+ if ($unsafe) {
+ set $scriptSrc "'self' 'unsafe-eval' 'unsafe-inline' resource: ''${main_domain}";
+ }
+
+ # Finally, set all the rules you composed above.
+ add_header Content-Security-Policy "default-src 'none'; child-src $childSrc; worker-src $workerSrc; media-src $mediaSrc; style-src $styleSrc; script-src $scriptSrc; connect-src $connectSrc; font-src $fontSrc; img-src $imgSrc; frame-src $frameSrc;";
+
+ # The nodejs process can handle all traffic whether accessed over websocket or as static assets
+ # We prefer to serve static content from nginx directly and to leave the API server to handle
+ # the dynamic content that only it can manage. This is primarily an optimization
+ location ^~ /cryptpad_websocket {
+ proxy_pass http://[::1]:3000;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ # WebSocket support (nginx 1.4)
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection upgrade;
+ }
+
+ location ^~ /customize.dist/ {
+ # This is needed in order to prevent infinite recursion between /customize/ and the root
+ }
+ # try to load customizeable content via /customize/ and fall back to the default content
+ # located at /customize.dist/
+ # This is what allows you to override behaviour.
+ location ^~ /customize/ {
+ rewrite ^/customize/(.*)$ $1 break;
+ try_files /customize/$uri /customize.dist/$uri;
+ }
+
+ # /api/config is loaded once per page load and is used to retrieve
+ # the caching variable which is applied to every other resource
+ # which is loaded during that session.
+ location = /api/config {
+ proxy_pass http://[::1]:3000;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+
+ # encrypted blobs are immutable and are thus cached for a year
+ location ^~ /blob/ {
+ root /www/cryptpad;
+ add_header X-XSS-Protection "1; mode=block";
+ add_header X-Content-Type-Options nosniff;
+ add_header Content-Security-Policy "default-src 'none'; child-src $childSrc; worker-src $workerSrc; media-src $mediaSrc; style-src $styleSrc; script-src $scriptSrc; connect-src $connectSrc; font-src $fontSrc; img-src $imgSrc; frame-src $frameSrc;";
+ if ($request_method = 'OPTIONS') {
+ add_header Cache-Control $cacheControl;
+ add_header X-XSS-Protection "1; mode=block";
+ add_header X-Content-Type-Options nosniff;
+ add_header Content-Security-Policy "default-src 'none'; child-src $childSrc; worker-src $workerSrc; media-src $mediaSrc; style-src $styleSrc; script-src $scriptSrc; connect-src $connectSrc; font-src $fontSrc; img-src $imgSrc; frame-src $frameSrc;";
+ add_header 'Access-Control-Allow-Origin' '*';
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
+ add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
+ add_header 'Access-Control-Max-Age' 1728000;
+ add_header 'Content-Type' 'application/octet-stream; charset=utf-8';
+ add_header 'Content-Length' 0;
+ return 204;
+ }
+ add_header Cache-Control max-age=31536000;
+ add_header 'Access-Control-Allow-Origin' '*';
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
+ add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
+ add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
+ try_files $uri =404;
+ }
+
+ # the "block-store" serves encrypted payloads containing users' drive keys
+ # these payloads are unlocked via login credentials. They are mutable
+ # and are thus never cached. They're small enough that it doesn't matter, in any case.
+ location ^~ /block/ {
+ add_header X-XSS-Protection "1; mode=block";
+ add_header X-Content-Type-Options nosniff;
+ add_header Access-Control-Allow-Origin "*";
+ add_header Content-Security-Policy "default-src 'none'; child-src $childSrc; worker-src $workerSrc; media-src $mediaSrc; style-src $styleSrc; script-src $scriptSrc; connect-src $connectSrc; font-src $fontSrc; img-src $imgSrc; frame-src $frameSrc;";
+ add_header Cache-Control max-age=0;
+ try_files $uri =404;
+ }
+
+ # This block provides an alternative means of loading content
+ # otherwise only served via websocket. This is solely for debugging purposes,
+ # and is thus not allowed by default.
+ #location ^~ /datastore/ {
+ #add_header Cache-Control max-age=0;
+ #try_files $uri =404;
+ #}
+
+ # ugly hack: disable registration
+ location /register {
+ deny all;
+ }
+
+
+ # The nodejs server has some built-in forwarding rules to prevent
+ # URLs like /pad from resulting in a 404. This simply adds a trailing slash
+ # to a variety of applications.
+ location ~ ^/(register|login|settings|user|pad|drive|poll|slide|code|whiteboard|file|media|profile|contacts|todo|filepicker|debug|kanban|sheet|support|admin|notifications|teams)$ {
+ rewrite ^(.*)$ $1/ redirect;
+ }
+
+ # Finally, serve anything the above exceptions don't govern.
+ try_files /www/$uri /www/$uri/index.html /customize/$uri;
+ '';
+in {
+
imports = [ ../../profiles/nginx.nix ];
systemd.services.nginx = {
- serviceConfig.BindReadOnlyPaths = [ "/home/asmadeus/www:/www/local" ];
+ serviceConfig.BindReadOnlyPaths = [
+ "/home/asmadeus/www:/www/local"
+ "/var/lib/private/cryptpad:/www/cryptpad"
+ ];
};
services.nginx.virtualHosts = {
@@ -57,18 +248,12 @@
"cryptpad.codewreck.org" = {
forceSSL = true;
enableACME = true;
- locations."/" = {
- proxyPass = "http://[::1]:3000";
- proxyWebsockets = true;
- };
+ extraConfig = cryptpadConfig;
};
"cryptpad-sandbox.codewreck.org" = {
forceSSL = true;
enableACME = true;
- locations."/" = {
- proxyPass = "http://[::1]:3001";
- proxyWebsockets = true;
- };
+ extraConfig = cryptpadConfig;
};
};
}