# ================= # - Install Cryptad on Debian 9 (stretch) # ================= # - See: # - https://blog.cavebeat.org/2017/07/cryptpad-installation-on-debian-stretch/ # - # - See also: # - git repository: https://github.com/xwiki-labs/cryptpad # - installation guide: https://github.com/xwiki-labs/cryptpad/wiki/Installation-guide # - cryptpad_url=cpad-01.oopen.de cryptpad_url=o13-pad.oopen.de # bei vorgeschalteter authentifizierung funktioniert # eine *extra* sandbox url nicht. in diesem falle die gleiche # adresse nehmen wie für die hautp seite # #cryptpad_sandbox_url=cpadsb-01.oopen.de #cryptpad_sandbox_url=o13-padsb.oopen.de cryptpad_sandbox_url="$cryptpad_url" # ---------- # - Pre-requisites # ---------- # - Install curl, git # - apt-get install curl git-core # - Install Python # - apt-get install python-minimal python # - Install compiler stuff # - # - apt-get install -y g++ g++-multilib gcc gcc-multilib cpp \ # - make automake autoconf libtool flex bison \ # - gettext pkg-config gnu-standards \ # - libssl-dev libreadline-dev libncurses-dev # - apt-get install gcc g++ make # --- # - Install Nginx webservice # --- apt-get install nginx # - Generate Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits # - mkdir /etc/nginx/ssl openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048 # - Create nginx configuration for CryptPad # - cat < /etc/nginx/sites-available/${cryptpad_url}.conf # -- $cryptpad_url server { listen 80; listen [::]:80; server_name $cryptpad_url; return 301 https://\$server_name\$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; # 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_url"; set \$sandbox_domain "$cryptpad_url"; # 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_url"; set \$files_domain "$cryptpad_url"; server_name $cryptpad_url $cryptpad_sandbox_url; ssl_certificate /var/lib/dehydrated/certs/$cryptpad_url/fullchain.pem; ssl_certificate_key /var/lib/dehydrated/certs/$cryptpad_url/privkey.pem; ssl_trusted_certificate /var/lib/dehydrated/certs/$cryptpad_url/chain.pem; # - Needed for (automated) updating certificate # - include snippets/letsencrypt-acme-challenge.conf; # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits # # To generate a dhparam.pem file, run in a terminal # openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048 # ssl_dhparam /etc/nginx/ssl/dhparam.pem; # Speeds things up a little bit when resuming a session ssl_session_timeout 5m; ssl_session_cache shared:SSL_CP:5m; # You'll need nginx 1.13.0 or better to support TLSv1.3 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; 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"; # Nginx Bad Bot Blocker Includes #include /etc/nginx/bots.d/ddos.conf; #include /etc/nginx/bots.d/blockbots.conf; #if (\$allowed_country = no) { # set \$blockreason '[geo_blocked]'; # return 403; #} #if ( \$bad_querystring !~* "\[OK\]" ) { # set \$blockreason \$bad_querystring; # return 403; #} #if ( \$bad_request !~* "\[OK\]" ) { # set \$blockreason \$bad_request; # return 403; #} #if ( \$bad_request_method !~* "\[OK\]" ) { # set \$blockreason \$bad_request_method; # return 403; #} # End Bad Bot Blocker Includes set \$coop ''; if (\$uri ~ ^\/sheet\/.*\$) { set \$coop 'same-origin'; } add_header Cross-Origin-Resource-Policy cross-origin; add_header Cross-Origin-Opener-Policy \$coop; add_header Cross-Origin-Embedder-Policy require-corp; # Insert the path to your CryptPad repository root here root /var/www/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; } if (\$uri ~ ^/.*(\/|\.html)\$) { set \$cacheControl no-cache; } # 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 = "/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://localhost: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://localhost: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/ { if (\$request_method = 'OPTIONS') { 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,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'; 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 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; #} # 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; # Only authenticated users # auth_basic "Restricted Content"; auth_basic_user_file /etc/nginx/.htpasswd-${cryptpad_url%%.*}; } EOF # --- # - Install NodeJS v6.x on Debian 9 Stretch # --- # - Creates the apt sources list file '/etc/apt/sources.list.d/nodesource.list' for # - the NodeSource Node.js v6.x and also adds the NodeSource signing key to your keyring # - # - curl -sL https://deb.nodesource.com/setup_6.x | bash - # - # - Hope nodejs version 14.x will work # - # - nodejs 6.0 does not yet work for actuall etherpad, which ist also installed # - on this server. # - curl -sL https://deb.nodesource.com/setup_14.x | bash - apt-get update # - Install nodejs version 6.x from repository 'deb.nodesource.com' # - # - Tell the apt system to install nodejs from repository deb.nodesource.com # - # - You should pin the external source using the origin option to assign a high # - priority to "the external source" instead of using the release name. # - # - e,g: Add the following lines to your /etc/apt/preferences.d/preferences: # - # - Package: * # - Pin: origin deb.nodesource.com # - Pin-Priority: 1001 # - if ! $(grep -E -q "^\s*Pin:\s+origin\s+deb.nodesource.com" /etc/apt/preferences.d/preferences) ; then cat <> /etc/apt/preferences.d/preferences Package: * Pin: origin deb.nodesource.com Pin-Priority: 1001 EOF fi # - Verify with 'apt-cache policy nodejs' # - # - Output must conatin somethin liek that: # - Installationskandidat: 6.14.4-1nodesource1 # - apt-cache policy nodejs # - Install nodejs.. # - apt-get install nodejs # - An alternative possibility: # - # - 1. Show versions for nodejs # - # - # aptitude versions nodejs # - # - output may looks like: # - # - i 6.14.4-1nodesource1 500 # - p 10.15.2~dfsg-2 stable 500 # - # - 2. install nodejs 6.14.4-1nodesource1 # - # - apt-get install nodejs=6.14.4-1nodesource1 # - ^^^^^^^^^^^^^^^^^^^^^^^^^^ # - # - Test if installation was successfully # - node -v npm -v # --- # - Install bower # --- # - Install bower (global) # - npm install -g bower # --- # - Create the user for the service: # --- mkdir -p /var/www/{.cache,.config,.local,.node-gyp,.npm,.private} chown -R www-data:www-data /var/www/{.cache,.config,.local,.node-gyp,.npm,.private} # --- # - Install cryptpad # --- cd /var/www/ mkdir cryptpad chown www-data:www-data cryptpad # - Get cryptpad # - su www-data -s /bin/bash -c "git clone https://github.com/xwiki-labs/cryptpad /var/www/cryptpad" cd cryptpad # - Complete Installation of cryptpad # - su www-data -s /bin/bash -c "npm install" su www-data -s /bin/bash -c "bower install" # --- # - Configure CryptPad # --- # - Create configuration file 'config.js'. (Copy the example file) # - # - The defaults should be good enough for most cases, but you may want to edit # - them. We recommend you read over the example file and change the values to # - fit your needs. cp -a config/config.example.js config/config.js # - Some base configuration # - # - adminEmail: 'admin@oopen.de', # - ... # - httpUnsafeOrigin: http://localhost:3000 # - httpSafeOrigin: ${cryptpad_sandbox_url} # - httpAddress: '::', # - ... # - myDomain: oopen.de, # - perl -i -n -p -e"s#(\s*)(adminEmail:.*)#\1// \2\n\1adminEmail: 'admin\@oopen.de',#" /var/www/cryptpad/config/config.js perl -i -n -p -e"s#(\s*)(httpUnsafeOrigin:.*)#\1// \2\n\1httpUnsafeOrigin: 'http://localhost:3000',#" /var/www/cryptpad//config/config.js perl -i -n -p -e"s#(\s*)(/*\s*)(httpSafeOrigin:.*)#\1// \3\n\1httpSafeOrigin: 'https://${cryptpad_sandbox_url}',#" /var/www/cryptpad/config/config.js perl -i -n -p -e"s#(\s*)(/*\s*)(httpAddress:.*)#\1// \3\n\1httpAddress: '::',#" /var/www/cryptpad/config/config.js # - deaktivate donate Button # - perl -i -n -p -e"s#(\s*)(/*\s*)(removeDonateButton:.*)#\1//\3\n\1removeDonateButton: true,#" /var/www/cryptpad/config/config.js # - Set storage.. # - perl -i -n -p -e"s#(\s*)(/*\s*)(defaultStorageLimit:.*)#\1//\3\n\1defaultStorageLimit: 250 * 1024 * 1024,#" /var/www/cryptpad/config/config.js # - Customizing CryptPad # - # - In order allow a variety of features to be changed and to allow site-specific # - changes to CryptPad apps while still keeping the git repository pristine, this # - directory exists to allow a set of hooks to be run. # - # - The server is configured to load files from the '/customize/' path # - preferentially from 'cryptpad/customize/', and to fall back to # - 'cryptpad/customize.dist/' if they are not found. # - # - If you wish to customize cryptpad, please **copy** # - '/customize.dist/' to '/customize' and then edit it there, this way you will # - still be able to pull from (and make pull requests to (!) the git repository. # - cp -a /var/www/cryptpad/customize.dist /var/www/cryptpad/customize # - Copy 'favicon.ico' to '/var/www/cryptpad/customize/main-favicon.png' # - #cp ~chris/favicon.ico /var/www/cryptpad/customize/main-favicon.png cp ~chris/favicon.ico /var/www/cryptpad/customize.dist/ chown www-data:www-data /var/www/cryptpad/customize.dist/favicon.ico # --- # Systemd Unit # --- # - Run as daemon using systemd # - cat << EOF > /etc/systemd/system/cryptpad.service [Unit] Description=CryptPad Service After=syslog.target network.target Requires=nginx.service [Service] Type=simple User=www-data Group=www-data Environment='PWD="/var/www/cryptpad"' # modify to match the location of your cryptpad repository WorkingDirectory=/var/www/cryptpad ExecStart=/usr/bin/node /var/www/cryptpad/server.js TimeoutSec=30 # Restart service after 10 seconds if node service crashes RestartSec=2 Restart=always # Output to syslog StandardOutput=syslog StandardError=syslog SyslogIdentifier=cryptpad ## Modify these two values and uncomment them if you have lots of files and get an HTTP error 500 because of that LimitMEMLOCK=infinity LimitNOFILE=65535 ### If you want to bind CryptPad to a port below 1024 uncomment the two values below #CapabilityBoundingSet=CAP_NET_BIND_SERVICE #AmbientCapabilities=CAP_NET_BIND_SERVICE [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable cryptpad # - Start cryptpad # - systemctl start cryptpad