# SPDX-FileCopyrightText: 2024 XWiki CryptPad Team and contributors # # SPDX-License-Identifier: AGPL-3.0-or-later # This file is included strictly as an example of how Caddy can be configured # to work with CryptPad. This example WILL NOT WORK AS IS. For best results, # compare the sections of this configuration file against a working CryptPad # installation (http server by the Nodejs process). If you are using CryptPad # in production and require professional support please contact sales@cryptpad.org (trustedProxies) { # Force Caddy to accept `X-Forwarded-For` and other origin headers. # Modify the line below if you want to restrict the scope of direct downstream sending these headers. trusted_proxies 0.0.0.0/0 ::/0 } # Caddy does not have variables for server names, so domains need to be hardcoded. # You can bulk replace "your-main-domain.com" and "your-sandbox-domain.com" safely. your-main-domain.com:443, your-sandbox-domain.com:443 { # Define your certificates below. # No need to adjust TLS configurations, as the defaults in Caddy are already secure. tls /path/to/fullchain/publicKey.pem /path/to/certificate/privateKey.pem # Enable HSTS. # Do not enable this line when configuring over mixnet, e.g. Tor. header Strict-Transport-Security "max-age=63072000; includeSubDomains" # Security headers header X-XSS-Protection "1; mode=block" header X-Content-Type-Options "nosniff" header Access-Control-Allow-Credentials "true" #header X-Frame-Options "SAMEORIGIN" # OnlyOffice fonts may be loaded from both domains. @onlyOfficeFonts { path_regexp "^\\/common\\/onlyoffice\\/.*\\/fonts\\/.*$" } header Access-Control-Allow-Origin "*" # By default CryptPad forbids remote domains from embedding CryptPad documents in iframes. # The sandbox domain must always be permitted in order for the platform to function. # If you wish to enable remote embedding you may change the value below to "*" # as per the commented value. header ?Access-Control-Allow-Origin "https://your-sandbox-domain.com" #header ?Access-Control-Allow-Origin "*" # Opt out of Google's FLoC Network header Permissions-Policy "interest-cohort=()" # Enable SharedArrayBuffer in Firefox (for .xlsx export) header ?Cross-Origin-Resource-Policy "cross-origin" header ?Cross-Origin-Embedder-Policy "require-corp" # Specify the relative path to root of your custom error page. # This error page won't only be served for 404 errors. handle_errors { rewrite * /error.htm header Cache-Control "no-cache, no-store" file_server templates } # Insert the path to your CryptPad repository root here root /home/cryptpad/cryptpad # Any static assets loaded with "vers=" in their URL will be cached for a year @staticAssets { query "ver=*" } header @staticAssets Cache-Control "max-age=31536000" vars { # CSS can be dynamically set inline, loaded from the same domain, or from your main domain. styleSrc "'unsafe-inline' 'self' https://your-main-domain.com" # connect-src restricts URLs which can be loaded using script interfaces. # If you have configured your instance to use a dedicated file delivery domain or API domain, # you will need to add them below. connectSrc "'self' https://your-main-domain.com blob: wss://api.your-main-domain.com https://your-sandbox-domain.com" # Fonts can be loaded from data-URLs or the main domain. fontSrc "'self' data: https://your-main-domain.com" # Images can be loaded from anywhere, though we'd like to deprecate this as it allows # the use of images for tracking. imgSrc "'self' data: blob: https://your-main-domain.com" # frame-src specifies valid sources for nested browsing contexts. # This prevents loading any iframes from anywhere other than the sandbox domain. frameSrc "'self' https://your-sandbox-domain.com blob:" # media-src specifies valid sources for loading media using video or audio. mediaSrc "blob:" # child-src defines valid sources for webworkers and nested browser contexts. # It is deprecated in favour of worker-src and frame-src. childSrc "https://your-main-domain.com" # worker-src valid sources for Worker, Shared Worker, or Service Worker scripts. # Supercedes child-src, but is unfortunately not yet universally supported. workerSrc "'self'" # script-src specifies valid sources for JavaScript, including inline handlers. scriptSrc "'self' resource: https://your-main-domain.com" # 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) frameAncestors "'self' https://your-main-domain.com" #frameAncestors "'self' https: vector:" # A few assets are loaded via the sandbox domain. # They unfortunately still require exceptions to the sandboxing to work correctly. # Everything except the sandbox domain is a privileged scope, as they might be used to handle keys. # Unsafe iframes are exceptions. Office file formats are converted outside of the sandboxed scope, # because of bugs in Chromium-based browsers that incorrectly ignore headers supposed to enable # the use of some modern APIs, that are required when JavaScript is run in a cross-origin context. # We've applied other sandboxing techniques to mitigate the risk of running WebAssembly # in this privileged scope. # Privileged contexts allow a few more rights than unprivileged contexts, though limits are still applied. scriptSrcUnsafe "'self' 'unsafe-eval' 'unsafe-inline' resource: https://your-main-domain.com" } # Finally, set all the security rules you have composed above. @privilegedScope1 { host "your-sandbox-domain.com" path_regexp "^\\/(sheet|doc|presentation)\\/inner.html.*$" } @privilegedScope2 { host "your-sandbox-domain.com" path_regexp "^\\/common\\/onlyoffice\\/.*\\/.*\\.html.*$" } @privilegedScope3 { host "your-sandbox-domain.com" path_regexp "^\\/unsafeiframe\\/inner\\.html.*$" } header @privilegedScope1 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}" header @privilegedScope2 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}" header @privilegedScope3 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}" header ?Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrc}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}" # Add support for .mjs files used by pdfjs @fileModuleJS { path "*.mjs" } header @fileModuleJS Content-Type "application/javascript" # The Node.js process can handle all traffic, whether accessed over websocket or as static assets. # We prefer to serve static content from Caddy directly, and to leave the API server to handle the # the dynamic content that only it can manage. This is primarily for optimization. handle /cryptpad_websocket/* { reverse_proxy * { to 127.0.0.1:3003 header_up Host "{host}" header_up X-Real-IP "{remote_host}" # Caddy supports WebSockets directly. No additional headers are needed. import trustedProxies } } handle_path /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. handle_path /customize/* { try_files /customize/{path} /customize.dist/{path} file_server { index index.html index.htm default.html default.htm } } # /api/config is loaded once per page load, and is used to retrieve the caching variable, # which is applied to every other resource loaded during that session. @sharedReverseProxy { path /api/* path /extensions.js } handle @sharedReverseProxy { reverse_proxy * { to 127.0.0.1:3000 header_up Host "{host}" header_up X-Real-IP "{remote_host}" # These settings prevent both Caddy and the API server from setting duplicate headers. header_down Cross-Origin-Resource-Policy cross-origin header_down Cross-Origin-Embedder-Policy require-corp import trustedProxies } } # Requests for blobs and blocks are now proxied to the API server. # This simplifies Caddy path configuration, in the event they are being hosted in a non-standard location # or with odd unexpected permissions. Serving blobs in this manner also means that it will be possible to # enforce access control for them, though this is not yet implemented. # Access control (via TOTP 2FA) has been added to blocks, so they can be handled with the same directives. @blobsAndBlocks { path /blob/* path /block/* } handle @blobsAndBlocks { @corsPreflight { method OPTIONS } handle @corsPreflight { header Access-Control-Allow-Origin "https://your-sandbox-domain.com" header Access-Control-Allow-Credentials "true" header Access-Control-Allow-Methods "GET, POST, OPTIONS" 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" header Access-Control-Max-Age "1728000" header Content-Type "application/octet-stream; charset=utf-8" header Content-Length "0" respond 204 } reverse_proxy * { to 127.0.0.1:3000 # Preventing these headers from getting duplicated, since we are proxying to the API server. header_down -X-Content-Type-Options header_down -Access-Control-Allow-Origin header_down -Permissions-Policy header_down -X-XSS-Protection header_down -Cross-Origin-Resource-Policy header_down -Cross-Origin-Embedder-Policy } } # The Node.JS server has some built-in forwarding rulesets to prevent URLs not suffixed with a slash # from resulting in a 404 error. This simply adds a trailing slash to a variety of applications. @preventNotFound { path_regexp "^/(register|login|recovery|settings|user|pad|drive|poll|slide|code|whiteboard|file|media|profile|contacts|todo|filepicker|debug|kanban|sheet|support|admin|notifications|teams|calendar|presentation|doc|form|report|convert|checkup|diagram)$" } redir @preventNotFound "{path}/" # Enable file serving file_server { index index.html index.htm default.html default.htm } }