at master 8.3 kB view raw
1{ pkgs, lib, ... }: 2let 3 orga = "example"; 4 domain = "${orga}.localdomain"; 5 6 tls-cert = pkgs.runCommand "selfSignedCert" { buildInputs = [ pkgs.openssl ]; } '' 7 openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -days 36500 \ 8 -subj '/CN=machine.${domain}' 9 install -D -t $out key.pem cert.pem 10 ''; 11 12 gitRepositories = [ 13 "repo1" 14 "repo2" 15 ]; 16in 17{ 18 name = "public-inbox"; 19 20 meta.maintainers = with pkgs.lib.maintainers; [ julm ]; 21 22 nodes.machine = 23 { 24 config, 25 pkgs, 26 nodes, 27 ... 28 }: 29 let 30 inherit (config.services) public-inbox; 31 in 32 { 33 virtualisation.diskSize = 1 * 1024; 34 virtualisation.memorySize = 1 * 1024; 35 networking.domain = domain; 36 37 security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ]; 38 # If using security.acme: 39 #security.acme.certs."${domain}".postRun = '' 40 # systemctl try-restart public-inbox-nntpd public-inbox-imapd 41 #''; 42 43 services.public-inbox = { 44 enable = true; 45 postfix.enable = true; 46 openFirewall = true; 47 settings.publicinbox = { 48 css = [ "href=https://machine.${domain}/style/light.css" ]; 49 nntpserver = [ "nntps://machine.${domain}" ]; 50 wwwlisting = "match=domain"; 51 }; 52 mda = { 53 enable = true; 54 args = [ "--no-precheck" ]; # Allow Bcc: 55 }; 56 http = { 57 enable = true; 58 port = "/run/public-inbox-http.sock"; 59 #port = 8080; 60 args = [ "-W0" ]; 61 mounts = [ 62 "https://machine.${domain}/inbox" 63 ]; 64 }; 65 nntp = { 66 enable = true; 67 #port = 563; 68 args = [ "-W0" ]; 69 cert = "${tls-cert}/cert.pem"; 70 key = "${tls-cert}/key.pem"; 71 }; 72 imap = { 73 enable = true; 74 #port = 993; 75 args = [ "-W0" ]; 76 cert = "${tls-cert}/cert.pem"; 77 key = "${tls-cert}/key.pem"; 78 }; 79 inboxes = 80 lib.recursiveUpdate 81 (lib.genAttrs gitRepositories (repo: { 82 address = [ 83 # Routed to the "public-inbox:" transport in services.postfix.transport 84 "${repo}@${domain}" 85 ]; 86 description = '' 87 ${repo}@${domain} : 88 discussions about ${repo}. 89 ''; 90 url = "https://machine.${domain}/inbox/${repo}"; 91 newsgroup = "inbox.comp.${orga}.${repo}"; 92 coderepo = [ repo ]; 93 })) 94 { 95 repo2 = { 96 hide = [ 97 "imap" # FIXME: doesn't work for IMAP as of public-inbox 1.6.1 98 "manifest" 99 "www" 100 ]; 101 }; 102 }; 103 settings.coderepo = lib.listToAttrs ( 104 map ( 105 repositoryName: 106 lib.nameValuePair repositoryName { 107 dir = "/var/lib/public-inbox/repositories/${repositoryName}.git"; 108 cgitUrl = "https://git.${domain}/${repositoryName}.git"; 109 } 110 ) gitRepositories 111 ); 112 }; 113 114 # Use nginx as a reverse proxy for public-inbox-httpd 115 services.nginx = { 116 enable = true; 117 recommendedGzipSettings = true; 118 recommendedOptimisation = true; 119 recommendedTlsSettings = true; 120 recommendedProxySettings = true; 121 virtualHosts."machine.${domain}" = { 122 forceSSL = true; 123 sslCertificate = "${tls-cert}/cert.pem"; 124 sslCertificateKey = "${tls-cert}/key.pem"; 125 locations."/".return = "302 /inbox"; 126 locations."= /inbox".return = "302 /inbox/"; 127 locations."/inbox".proxyPass = "http://unix:${public-inbox.http.port}:/inbox"; 128 # If using TCP instead of a Unix socket: 129 #locations."/inbox".proxyPass = "http://127.0.0.1:${toString public-inbox.http.port}/inbox"; 130 # Referred to by settings.publicinbox.css 131 # See http://public-inbox.org/meta/_/text/color/ 132 locations."= /style/light.css".alias = pkgs.writeText "light.css" '' 133 * { background:#fff; color:#000 } 134 135 a { color:#00f; text-decoration:none } 136 a:visited { color:#808 } 137 138 *.q { color:#008 } 139 140 *.add { color:#060 } 141 *.del {color:#900 } 142 *.head { color:#000 } 143 *.hunk { color:#960 } 144 145 .hl.num { color:#f30 } /* number */ 146 .hl.esc { color:#f0f } /* escape character */ 147 .hl.str { color:#f30 } /* string */ 148 .hl.ppc { color:#c3c } /* preprocessor */ 149 .hl.pps { color:#f30 } /* preprocessor string */ 150 .hl.slc { color:#099 } /* single-line comment */ 151 .hl.com { color:#099 } /* multi-line comment */ 152 /* .hl.opt { color:#ccc } */ /* operator */ 153 /* .hl.ipl { color:#ccc } */ /* interpolation */ 154 155 /* keyword groups kw[a-z] */ 156 .hl.kwa { color:#f90 } 157 .hl.kwb { color:#060 } 158 .hl.kwc { color:#f90 } 159 /* .hl.kwd { color:#ccc } */ 160 ''; 161 }; 162 }; 163 164 services.postfix = { 165 enable = true; 166 setSendmail = true; 167 #sslCert = "${tls-cert}/cert.pem"; 168 #sslKey = "${tls-cert}/key.pem"; 169 settings.main.recipient_delimiter = "+"; 170 }; 171 172 environment.systemPackages = [ 173 pkgs.gitMinimal 174 pkgs.mailutils 175 pkgs.openssl 176 ]; 177 178 }; 179 180 testScript = '' 181 start_all() 182 183 # The threshold and/or hardening may have to be changed with new features/checks 184 with subtest("systemd hardening thresholds"): 185 print(machine.succeed("systemd-analyze security public-inbox-httpd.service --threshold=5 --no-pager")) 186 print(machine.succeed("systemd-analyze security public-inbox-imapd.service --threshold=5 --no-pager")) 187 print(machine.succeed("systemd-analyze security public-inbox-nntpd.service --threshold=4 --no-pager")) 188 189 machine.wait_for_unit("multi-user.target") 190 machine.wait_for_unit("public-inbox-init.service") 191 192 machine.succeed( 193 ${lib.concatMapStrings (repositoryName: '' 194 "sudo -u public-inbox git init --bare -b main /var/lib/public-inbox/repositories/${repositoryName}.git", 195 '') gitRepositories} 196 ) 197 198 # List inboxes through public-inbox-httpd 199 machine.wait_for_unit("public-inbox-httpd.socket") 200 machine.wait_for_unit("nginx.service") 201 machine.succeed("curl -L https://machine.${domain} | grep repo1@${domain}") 202 # The repo2 inbox is hidden 203 machine.fail("curl -L https://machine.${domain} | grep repo2@${domain}") 204 205 # Send a mail and read it through public-inbox-httpd 206 # Must work too when using a recipientDelimiter. 207 machine.wait_for_unit("postfix.service") 208 machine.succeed("mail -t <${pkgs.writeText "mail" '' 209 Subject: Testing mail 210 From: root@localhost 211 To: repo1+extension@${domain} 212 Message-ID: <repo1@root-1> 213 Content-Type: text/plain; charset=utf-8 214 Content-Disposition: inline 215 216 This is a testing mail. 217 ''}") 218 machine.sleep(10) 219 machine.succeed("curl -L 'https://machine.${domain}/inbox/repo1/repo1@root-1/T/#u' | grep 'This is a testing mail.'") 220 221 # Read a mail through public-inbox-imapd 222 machine.wait_for_unit("public-inbox-imapd.socket") 223 machine.succeed("openssl s_client -ign_eof -crlf -connect machine.${domain}:993 <${pkgs.writeText "imap-commands" '' 224 tag login anonymous@${domain} anonymous 225 tag SELECT INBOX.comp.${orga}.repo1.0 226 tag FETCH 1 (BODY[HEADER]) 227 tag LOGOUT 228 ''} | grep '^Message-ID: <repo1@root-1>'") 229 230 # TODO: Read a mail through public-inbox-nntpd 231 #machine.wait_for_unit("public-inbox-nntpd.socket") 232 233 # Delete a mail. 234 # Note that the use of an extension not listed in the addresses 235 # require to use --all 236 machine.succeed("curl -L https://machine.${domain}/inbox/repo1/repo1@root-1/raw | sudo -u public-inbox public-inbox-learn rm --all") 237 machine.fail("curl -L https://machine.${domain}/inbox/repo1/repo1@root-1/T/#u | grep 'This is a testing mail.'") 238 239 # Compact the database 240 machine.succeed("sudo -u public-inbox public-inbox-compact --all") 241 ''; 242}