at master 5.0 kB view raw
1let 2 certs = import ./common/acme/server/snakeoil-certs.nix; 3 domain = certs.domain; 4in 5{ 6 name = "schleuder"; 7 nodes.machine = 8 { pkgs, ... }: 9 { 10 imports = [ ./common/user-account.nix ]; 11 services.postfix = { 12 enable = true; 13 enableSubmission = true; 14 settings.main = { 15 mydomain = domain; 16 destination = domain; 17 smtp_tls_CAfile = "${certs.ca.cert}"; 18 smtpd_tls_chain_files = [ 19 "${certs.${domain}.key}" 20 "${certs.${domain}.cert}" 21 ]; 22 }; 23 localRecipients = [ 24 "root" 25 "alice" 26 "bob" 27 ]; 28 }; 29 services.schleuder = { 30 enable = true; 31 # Don't do it like this in production! The point of this setting 32 # is to allow loading secrets from _outside_ the world-readable 33 # Nix store. 34 extraSettingsFile = pkgs.writeText "schleuder-api-keys.yml" '' 35 api: 36 valid_api_keys: 37 - fnord 38 ''; 39 lists = [ "security@${domain}" ]; 40 settings.api = { 41 tls_cert_file = "${certs.${domain}.cert}"; 42 tls_key_file = "${certs.${domain}.key}"; 43 }; 44 }; 45 46 environment.systemPackages = [ 47 pkgs.gnupg 48 pkgs.msmtp 49 (pkgs.writeScriptBin "do-test" '' 50 #!${pkgs.runtimeShell} 51 set -exuo pipefail 52 53 # Generate a GPG key with no passphrase and export it 54 sudo -u alice gpg --passphrase-fd 0 --batch --yes --quick-generate-key 'alice@${domain}' rsa4096 sign,encr < <(echo) 55 sudo -u alice gpg --armor --export alice@${domain} > alice.asc 56 # Create a new mailing list with alice as the owner, and alice's key 57 schleuder-cli list new security@${domain} alice@${domain} alice.asc 58 59 # Send an email from a non-member of the list. Use --auto-from so we don't have to specify who it's from twice. 60 msmtp --auto-from security@${domain} --host=${domain} --port=25 --tls --tls-starttls <<EOF 61 Subject: really big security issue!! 62 From: root@${domain} 63 64 I found a big security problem! 65 EOF 66 67 # Wait for delivery 68 (set +o pipefail; journalctl -f -n 1000 -u postfix | grep -m 1 'delivered to maildir') 69 70 # There should be exactly one email 71 mail=(/var/spool/mail/alice/new/*) 72 [[ "''${#mail[@]}" = 1 ]] 73 74 # Find the fingerprint of the mailing list key 75 read list_key_fp address < <(schleuder-cli keys list security@${domain} | grep security@) 76 schleuder-cli keys export security@${domain} $list_key_fp > list.asc 77 78 # Import the key into alice's keyring, so we can verify it as well as decrypting 79 sudo -u alice gpg --import <list.asc 80 # And perform the decryption. 81 sudo -u alice gpg -d $mail >decrypted 82 # And check that the text matches. 83 grep "big security problem" decrypted 84 '') 85 86 # For debugging: 87 # pkgs.vim pkgs.openssl pkgs.sqliteinteractive 88 ]; 89 90 security.pki.certificateFiles = [ certs.ca.cert ]; 91 92 # Since we don't have internet here, use dnsmasq to provide MX records from /etc/hosts 93 services.dnsmasq = { 94 enable = true; 95 settings.selfmx = true; 96 }; 97 98 networking.extraHosts = '' 99 127.0.0.1 ${domain} 100 ''; 101 102 # schleuder-cli's config is not quite optimal in several ways: 103 # - A fingerprint _must_ be pinned, it doesn't even have an option 104 # to trust the PKI 105 # - It compares certificate fingerprints rather than key 106 # fingerprints, so renewals break the pin (though that's not 107 # relevant for this test) 108 # - It compares them as strings, which means we need to match the 109 # expected format exactly. This means removing the :s and 110 # lowercasing it. 111 # Refs: 112 # https://0xacab.org/schleuder/schleuder-cli/-/issues/16 113 # https://0xacab.org/schleuder/schleuder-cli/-/blob/f8895b9f47083d8c7b99a2797c93f170f3c6a3c0/lib/schleuder-cli/helper.rb#L230-238 114 systemd.tmpfiles.rules = 115 let 116 cliconfig = 117 pkgs.runCommand "schleuder-cli.yml" 118 { 119 nativeBuildInputs = [ 120 pkgs.jq 121 pkgs.openssl 122 ]; 123 } 124 '' 125 fp=$(openssl x509 -in ${certs.${domain}.cert} -noout -fingerprint -sha256 | cut -d = -f 2 | tr -d : | tr 'A-Z' 'a-z') 126 cat > $out <<EOF 127 host: localhost 128 port: 4443 129 tls_fingerprint: "$fp" 130 api_key: fnord 131 EOF 132 ''; 133 in 134 [ 135 "L+ /root/.schleuder-cli/schleuder-cli.yml - - - - ${cliconfig}" 136 ]; 137 }; 138 139 testScript = '' 140 machine.wait_for_unit("multi-user.target") 141 machine.wait_until_succeeds("nc -z localhost 4443") 142 machine.succeed("do-test") 143 ''; 144}