diff --git a/templates/etc/nginx/sites-available/cryptpad.j2 b/templates/etc/nginx/sites-available/cryptpad.j2 index e55eeb7..589e44f 100644 --- a/templates/etc/nginx/sites-available/cryptpad.j2 +++ b/templates/etc/nginx/sites-available/cryptpad.j2 @@ -13,16 +13,27 @@ set $main_domain "{{ item.ssl_name }}"; set $sandbox_domain "sandbox.{{ item.ssl_name }}"; + # By default CryptPad allows remote domains to embed CryptPad documents in iframes. + # This behaviour can be blocked by changing $allowed_origins from "*" to the + # sandbox domain, which must be permitted to load content from the main domain + # in order for CryptPad to work as expected. + # + # An example is given below which can be uncommented if you want to block + # remote sites from including content from your server + set $allowed_origins "*"; + # set $allowed_origins "https://${sandbox_domain}"; + # 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. + # If you do, they can be added as exceptions to any rules which block connections to remote domains. + # You can find these variables referenced below in the relevant places. set $api_domain "{{ item.ssl_name }}"; set $files_domain "{{ item.ssl_name }}"; - add_header Access-Control-Allow-Origin "*"; + add_header Access-Control-Allow-Origin "${allowed_origins}"; #set $coop ''; #if ($uri ~ ^\/(sheet|presentation|doc|convert)\/.*$) { set $coop 'same-origin'; } @@ -33,7 +44,7 @@ # Enable SharedArrayBuffer in Firefox (for .xlsx export) add_header Cross-Origin-Resource-Policy cross-origin; add_header Cross-Origin-Embedder-Policy require-corp; - + # any static assets loaded with "ver=" in their URL will be cached for a year if ($args ~ ver=) { set $cacheControl max-age=31536000; @@ -48,6 +59,8 @@ set $styleSrc "'unsafe-inline' 'self' https://${main_domain}"; # connect-src restricts URLs which can be loaded using script interfaces + # if you have configured your instance to use a dedicated $files_domain or $api_domain + # you will need to add them below as: https://${files_domain} and https://${api_domain} set $connectSrc "'self' https://${main_domain} blob: wss://${api_domain} https://${sandbox_domain}"; # fonts can be loaded from data-URLs or the main domain @@ -75,6 +88,13 @@ # script-src specifies valid sources for javascript, including inline handlers set $scriptSrc "'self' resource: https://${main_domain}"; + # frame-ancestors specifies which origins can embed your CryptPad instance + # this must include 'self' and your main domain (over HTTPS) in order for CryptPad to work + # if you have enabled remote embedding via the admin panel then this must be more permissive. + # note: cryptpad.fr permits web pages served via https: and vector: (element desktop app) + set $frameAncestors "'self' https://${main_domain}"; + # set $frameAncestors "'self' https: vector:"; + set $unsafe 0; # the following assets are loaded via the sandbox domain # they unfortunately still require exceptions to the sandboxing to work correctly. @@ -95,7 +115,7 @@ } # 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;"; + 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; frame-ancestors $frameAncestors"; {% endblock %} {% block root %} @@ -152,7 +172,7 @@ # encrypted blobs are immutable and are thus cached for a year location ^~ /blob/ { if ($request_method = 'OPTIONS') { - add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Origin' "${allowed_origins}"; 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; @@ -160,8 +180,9 @@ add_header 'Content-Length' 0; return 204; } + add_header X-Content-Type-Options nosniff; add_header Cache-Control max-age=31536000; - add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Origin' "${allowed_origins}"; 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,Content-Length'; 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,Content-Length'; @@ -172,6 +193,7 @@ # 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-Content-Type-Options nosniff; add_header Cache-Control max-age=0; try_files $uri =404; }