1{ lib, pkgs, ... }:
2let
3 certs = pkgs.runCommand "cryptpadSelfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
4 mkdir -p $out
5 cd $out
6 openssl req -x509 -newkey rsa:4096 \
7 -keyout key.pem -out cert.pem -nodes -days 3650 \
8 -subj '/CN=cryptpad.localhost' \
9 -addext 'subjectAltName = DNS.1:cryptpad.localhost, DNS.2:cryptpad-sandbox.localhost'
10 '';
11 # data sniffed from cryptpad's /checkup network trace, seems to be re-usable
12 test_write_data = pkgs.writeText "cryptpadTestData" ''
13 {"command":"WRITE_BLOCK","content":{"publicKey":"O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik=","signature":"aXcM9SMO59lwA7q7HbYB+AnzymmxSyy/KhkG/cXIBVzl8v+kkPWXmFuWhcuKfRF8yt3Zc3ktIsHoFyuyDSAwAA==","ciphertext":"AFwCIfBHKdFzDKjMg4cu66qlJLpP+6Yxogbl3o9neiQou5P8h8yJB8qgnQ=="},"publicKey":"O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik=","nonce":"bitSbJMNSzOsg98nEzN80a231PCkBQeH"}
14 '';
15 seleniumScript =
16 pkgs.writers.writePython3Bin "selenium-script"
17 {
18 libraries = with pkgs.python3Packages; [ selenium ];
19 }
20 ''
21 from sys import stderr
22 from time import time
23 from selenium import webdriver
24 from selenium.webdriver.common.by import By
25 from selenium.webdriver.firefox.options import Options
26 from selenium.webdriver.support.ui import WebDriverWait
27 from selenium.webdriver.support import expected_conditions as EC
28
29 options = Options()
30 options.add_argument("--headless")
31 service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501
32
33 driver = webdriver.Firefox(options=options, service=service)
34 driver.implicitly_wait(10)
35 driver.get("https://cryptpad.localhost")
36
37 WebDriverWait(driver, 10).until(
38 EC.text_to_be_present_in_element(
39 (By.TAG_NAME, "body"), "CryptPad")
40 )
41
42 driver.find_element(By.PARTIAL_LINK_TEXT, "Sheet").click()
43
44 # Title changes once the sheet is rendered, which can take
45 # a lot of time on first run (browser generates keypair etc)
46 start = time()
47 WebDriverWait(driver, 60).until(
48 EC.title_contains('Sheet')
49 )
50 print(f"Sheets done loading in {time() - start}", file=stderr)
51
52 # check screen looks sane...
53 # driver.print_page() and dump pdf somewhere through pdftotext? OCR?
54
55 driver.close()
56 '';
57in
58{
59 name = "cryptpad";
60 meta = with pkgs.lib.maintainers; {
61 maintainers = [ martinetd ];
62 };
63
64 nodes.machine = {
65 environment.systemPackages = [
66 pkgs.firefox-unwrapped
67 ];
68 services.cryptpad = {
69 enable = true;
70 configureNginx = true;
71 settings = {
72 httpUnsafeOrigin = "https://cryptpad.localhost";
73 httpSafeOrigin = "https://cryptpad-sandbox.localhost";
74 };
75 };
76 services.nginx = {
77 virtualHosts."cryptpad.localhost" = {
78 enableACME = false;
79 sslCertificate = "${certs}/cert.pem";
80 sslCertificateKey = "${certs}/key.pem";
81 };
82 };
83 security = {
84 pki.certificateFiles = [ "${certs}/cert.pem" ];
85 };
86 };
87
88 testScript = ''
89 machine.wait_for_unit("cryptpad.service")
90 machine.wait_for_unit("nginx.service")
91 machine.wait_for_open_port(3000)
92
93 # test home page
94 machine.succeed("curl --fail https://cryptpad.localhost -o /tmp/cryptpad_home.html")
95 machine.succeed("grep -F 'CryptPad: Collaboration suite' /tmp/cryptpad_home.html")
96
97 # test scripts/build.js actually generated customize content from config
98 machine.succeed("grep -F 'meta property=\"og:url\" content=\"https://cryptpad.localhost/index.html' /tmp/cryptpad_home.html")
99
100 # make sure child pages are accessible (e.g. check nginx try_files paths)
101 machine.succeed(
102 "grep -oE '/(customize|components)[^\"]*' /tmp/cryptpad_home.html"
103 " | while read -r page; do"
104 " curl -O --fail https://cryptpad.localhost$page || exit;"
105 " done")
106
107 # test some API (e.g. check cryptpad main process)
108 machine.succeed("curl --fail -d @${test_write_data} -H 'Content-Type: application/json' https://cryptpad.localhost/api/auth")
109
110 # page loads
111 machine.succeed("${lib.getExe seleniumScript}")
112
113 # test telemetry has been disabled
114 machine.fail("journalctl -u cryptpad | grep TELEMETRY");
115
116 # for future improvements
117 machine.log(machine.execute("systemd-analyze security cryptpad.service")[1])
118 '';
119}