Come è fatto mochela.it, con i nomi delle cose. Per chi vuole verificare, copiare, criticare o mettere su un server simile.
Quello che è scritto qui gira davvero; quello che non gira ancora è segnato come previsto.
Codice sorgente
- Tutto il codice (playbook e ruoli Ansible, script del portatile, guida d'uso) è pubblicato su mochela.it/codice, in pagine statiche, solo in lettura: file e storia. Il repository completo si chiede a forgy@mochela.it e arriva come
git bundlefirmato con OpenPGP. - Il repository di lavoro sta su un Forgejo nostro, su
mx2: ascolta solo su127.0.0.1, chi amministra ci arriva dal tunnel di amministrazione. Da Internet nessuno parla con Forgejo: le pagine pubbliche le genera il portatile a ogni pubblicazione. - Licenze AGPL-3.0-or-later per il codice e CC BY-SA 4.0 per i testi, dichiarate file per file secondo REUSE. Ogni pubblicazione è una copia ripulita (niente Vault, variabili personali, indirizzi dei nodi, nomi dei fornitori), controllata con gitleaks.
- Scritto in gran parte con un assistente di IA (Claude, di Anthropic), sotto la direzione di chi gestisce mochela e provato sul cluster in produzione: come è stato fatto.
Nodi e rete
| Nodo | Paese | Ruolo |
|---|---|---|
mx1 | Germania | posta, sito, SimpleX (smp1), videochiamate (Galène), dati |
mx2 | Danimarca | posta, SimpleX (smp2), dati |
arb1 | Spagna | arbitro del quorum, dati |
- VPS KVM con Debian 13 (trixie); pacchetti da un mirror europeo.
- Rete privata WireGuard a maglia completa tra i tre nodi. Database, archivio, coordinamento e controlli passano solo nel tunnel; nessuna di quelle porte è aperta su Internet. Ogni coppia di nodi ha una chiave condivisa in più (
PresharedKey, 256 bit): chi registra oggi il traffico non potrà aprirlo domani con un computer quantistico attivo - Firewall nftables con politica
dropin ingresso. Aperte: 22 (SSH), 80 e 443 (web); sui nodi di posta anche 25, 465, 587, 993 (posta) e 5223 (SimpleX); su mx1 anche 1194 TCP e UDP (TURN di Galène) e UDP 20000-20999 (audio e video delle videochiamate). - Posta in uscita solo in IPv4, con PTR coerente con il nome del nodo IPv6 previsto
Dati replicati
- etcd a tre membri: il quorum. Decide chi è il primario del database; sopravvive alla perdita di un nodo qualsiasi.
- PostgreSQL gestito da Patroni: un primario e due repliche, passaggio automatico se il primario cade. Su ogni nodo di posta HAProxy porta Stalwart sempre al primario del momento.
- Garage (archivio a oggetti compatibile S3, di Deuxfleurs): corpo dei messaggi e allegati, una copia per zona, tre zone, una per fornitore.
- Valkey con tre Sentinel: dati temporanei e coordinamento tra le due istanze di Stalwart.
Posta
- Stalwart 0.16 su
mx1emx2, tutti e due attivi, come un server solo: configurazione in PostgreSQL, messaggi in Garage, coordinamento in Valkey. SMTP, IMAP, JMAP. - Porte per i client: IMAP 993 e invio 465, tutte e due con TLS implicito; JMAP e
/accountsu HTTPS. Niente POP3. - Record SRV (RFC 6186, RFC 8314) per
_imaps,_submissionse_jmap; file di autoconfigurazione in/.well-known/autoconfig/. - SPF, DKIM con doppia firma (RSA e Ed25519), DMARC con
p=reject: le mail che si spacciano per mochela.it senza firma valida vengono rifiutate. Rapporti aggregati (rua) apostmaster@: chi ha spedito a nome di mochela.it, giorno per giorno. - Nella posta inviata da un client autenticato il server non aggiunge la riga
Receivedcon l'IP del client. - Registri di Stalwart: un file all'ora. Ogni ora un timer toglie dai file chiusi gli indirizzi IP e la parte locale degli indirizzi email (resta il dominio:
xxx@gmail.com), e cancella i file dopo sette giorni. Restano in chiaro solo gli indirizzi interni del cluster. Comandostalwart-logper le ricerche; il controllore avvisa se un file con indirizzi supera le due ore. - Cifratura all'arrivo con la chiave OpenPGP pubblica dell'utente (funzione di Stalwart, facoltativa per ogni account). Chiavi pubblicate con la Web Key Directory, metodo diretto su
mochela.it, solo dopo aver verificato che arrivino dalla casella interessata; nel file pubblicato resta solo l'uid@mochela.it(export-minimal). - Antispam: solo liste DNSBL di tipo IP, europee (Spamhaus, blocklist.de, VirusFree), che ricevono solo l'indirizzo del server mittente. Spente le liste su domini, URL e indirizzi; spento Pyzor (manderebbe impronte dei messaggi); spento il classificatore con LLM. Il classificatore locale impara da solo che una mail è spam solo se il server mittente è su almeno tre liste.
- DANE in uscita: verso i server che pubblicano record TLSA firmati con DNSSEC la posta parte solo con TLS e con il certificato che corrisponde, anche nei tentativi dopo un errore (nessuna strategia di ripiego spegne DANE o MTA-STS). DNSSEC verificato da Stalwart attraverso l'Unbound del nodo, in TCP. In ingresso: zona
mochela.itfirmata con DNSSEC e record TLSA3 1 1per_25._tcp.posta; un solo certificato sulla porta 25, stessa chiave ai rinnovi (reuseKey), controllo ogni cinque minuti che la chiave presentata corrisponda ai record pubblicati. - MX unico,
posta.mochela.it, che punta amx1emx2: lo stesso nome del certificato della porta 25 (Actalis gratuito dà un nome per certificato). I nomi dei nodi restano per il DNS inverso e il saluto della posta in uscita. - MTA-STS (RFC 8461) per chi non usa DANE, come Gmail e Microsoft: politica su
mta-sts.mochela.itin modalitàenforcedall'8 ottobre 2026 (in prova dal 1 ottobre): chi la rispetta non ci consegna posta in chiaro attivo - TLS-RPT (RFC 8460): i server che ci scrivono mandano ogni giorno un rapporto sulle connessioni cifrate verso di noi. Rapporti a
postmaster@, letti solo qui. /.well-known/security.txt(RFC 9116): a chi segnalare un problema di sicurezza.- Verifica esterna con MECSA (Centro comune di ricerca della Commissione europea), 1 ottobre 2026: 100% su STARTTLS, X.509, SPF, DKIM, DMARC, DNSSEC, DANE e MTA-STS. Report
a96b6500a4131f74d944a6a05f6e4720(come verificarlo).
Cifratura end to end e ad accesso zero
Due meccanismi, entrambi con OpenPGP, che si completano.
| End to end, tra utenti | All'arrivo, sul server | |
|---|---|---|
| Chi cifra | chi scrive, sul proprio dispositivo, con la chiave pubblica del destinatario | Stalwart, appena la mail arriva, con la chiave pubblica di chi la riceve |
| In viaggio e sui server di chi spedisce | cifrata | com'è arrivata |
| Sui dischi di mochela.it e nei backup | cifrata | cifrata |
| Cosa sa il server del contenuto | niente: non ha mai avuto la chiave | niente, dopo l'arrivo: la mail passa in chiaro solo nella sua memoria |
| Cosa serve | OpenPGP da tutte e due le parti | solo la chiave di chi riceve |
- Con OpenPGP tra utenti il contenuto è a conoscenza zero per il server: corpo e allegati, e l'oggetto se il programma usa le intestazioni protette (in Thunderbird: Cifra l'oggetto).
- Restano visibili i metadati: mittente, destinatari, data, dimensione, i server attraversati. Servono a consegnare la posta, e nessuna cifratura della posta li nasconde.
- Le chiavi pubbliche si trovano con la Web Key Directory (
/.well-known/openpgpkey/, metodo diretto): Thunderbird e GnuPG le cercano da soli. Le chiavi segrete non passano mai dai server. - Con chi non usa OpenPGP non si può cifrare end to end: Stalwart non ha il messaggio "con link e password" di alcuni servizi commerciali. Per quei messaggi resta la cifratura all'arrivo.
SimpleX
- Due server SMP indipendenti,
smp1.mochela.itsu mx1 esmp2.mochela.itsu mx2, porta 5223. SMP non si mette in cluster: la ridondanza la fa l'app, che usa più server insieme, e con l'instradamento privato il server di chi riceve non vede l'IP di chi scrive. - Nessun utente sul server: niente account, numeri o nomi. Il server vede solo code anonime.
- Creazione delle code protetta da password, data a chi ha una casella mochela; chi scrive a un utente mochela non ne ha bisogno.
- Chiave della CA di ogni server tenuta fuori dai nodi: sui server resta solo la chiave di lavoro.
- Code salvate su disco, messaggi non ritirati cancellati dopo 21 giorni, statistiche giornaliere spente, nessun web server integrato.
- Programma ufficiale (simplexmq), con impronta SHA-256 fissata nel playbook.
Videochiamate (Galène)
- Galène 1.2 (SFU scritto in Go, IRIF / Université Paris Cité) su
mx1, dietro nginx suriunioni.mochela.itcon certificato Actalis. Galène ascolta solo su127.0.0.1; da Internet passano solo HTTPS (pagine e WebSocket), l'audio e il video in UDP 20000-20999 e il suo TURN sulla 1194, TCP e UDP, per chi sta dietro reti che bloccano l'UDP. - Cifratura: audio e video in DTLS-SRTP tra ogni browser e il server, che li decifra e li ricifra per inoltrarli agli altri (è il funzionamento di un SFU). Non è cifratura end to end, ed è scritto anche sul sito di Galène. Lo è invece l'invio di file, da browser a browser. Per una chiamata end to end a due: SimpleX.
- Stanze: ogni riunione può avere un indirizzo casuale (sottostanze create al primo ingresso, sparite quando si svuotano). Chiuse finché non entra un operatore (
autolock). Gli ospiti entrano con un link d'invito a tempo, senza account. Password salvate solo come impronta PBKDF2-SHA256; l'interfaccia di amministrazione di Galène è chiusa, le stanze le scrive il playbook. - Registrazioni solo da un operatore (
/record), solo nelle stanze dove sono abilitate e dopo averlo detto a chi partecipa: il server le scrive in uno spazio fisso nel volume cifrato dimx1, le fa scaricare solo a chi è operatore della stanza, non le mette nei backup e le cancella dopo cinque giorni. Chat solo in memoria, cancellata dopo quattro ore. - Indirizzi IP: nginx non tiene registri delle visite e non passa a Galène l'indirizzo di chi si collega, così Galène vede solo
127.0.0.1; le righe del registro di Galène che contengono un indirizzo IP vengono scartate da journald (LogFilterPatterns). Audio e video, però, arrivano a mx1 direttamente dall'indirizzo di chi partecipa, finché la chiamata dura. - Programma compilato sul portatile di amministrazione dal sorgente ufficiale, versione fissata e impronta del sorgente nel repository; servizio systemd senza privilegi, con il file system in sola lettura tranne la sua cartella dei dati.
Web e certificati
- nginx. Il sito è statico: niente JavaScript, niente cookie, niente risorse esterne, imposto da una
Content-Security-Policyche vieta tutto tranne fogli di stile, font e immagini del sito stesso. HSTS,Referrer-Policy: no-referrer. - TLS 1.2 solo con cifrari ECDHE (forward secrecy), TLS 1.3; scambio di chiavi ibrido post-quantistico
X25519MLKEM768(OpenSSL 3.5), conX25519per i client che non lo conoscono. Vale per sito, pagine della posta e videochiamate attivo - Registro delle visite con
0.0.0.0al posto dell'IP, tenuto sette giorni. Registro degli errori di nginx: dopo un'ora via indirizzi IP e nomi, poi sette giorni. - Sui nodi di posta nginx apre HTTPS per
posta.mochela.ite passa a Stalwart solo le pagine per gli utenti:/adminda Internet risponde 404 (la gestione passa da un tunnel SSH). Stalwart riceve l'IP del client inX-Forwarded-For, scritto da nginx, per il suo blocco dei tentativi di accesso. - Certificati da Actalis (Italia) via ACME con verifica HTTP-01: quelli di nginx con certbot, uno per nodo; quelli di IMAP e SMTP chiesti e rinnovati da Stalwart. La verifica funziona qualunque nodo Actalis interroghi: nginx cerca la risposta in locale e poi sull'altro nodo, via WireGuard.
DNS e ora
- Unbound su ogni nodo, come resolver ricorsivo: le domande partono dal nodo verso i server autoritativi, senza resolver pubblici di terzi.
- chrony con NTS (Network Time Security, l'ora autenticata) da server europei.
- Zona di
mochela.itpresso INWX (Germania), firmata con DNSSEC, con CAA che ammette solo Actalis.
Sicurezza del sistema
- SSH solo con chiave; la chiave privata non è mai stata su un server.
- Amministrazione (SSH e pannello di Stalwart) solo dentro un tunnel WireGuard dedicato tra il portatile e i nodi, con chiave condivisa aggiuntiva (
PresharedKey); porta 22 chiusa su Internet dal 14 ottobre 2026, con la console del fornitore come rientro d'emergenza - Aggiornamenti di sicurezza automatici (
unattended-upgrades); i pacchetti del cluster sono esclusi e si aggiornano con il playbook, un nodo alla volta. Riavvii manuali, uno per volta. - Segreti (password dei servizi, chiavi API) cifrati con Ansible Vault, mai in chiaro nel repository.
- Dati in un volume LUKS2 su ogni nodo (AES-XTS a 512 bit, passphrase protetta con Argon2id): database, archivio dei messaggi, etcd, Valkey, registri di Stalwart, code di SimpleX. Passphrase solo nel Vault e su carta, mai sui nodi. Dopo un riavvio i servizi dei dati non partono (condizione di systemd) finché il volume non viene sbloccato dal portatile, o dalla console del fornitore; Sentinel resta fuori e continua a votare. Attivo su tutti e tre i nodi da ottobre 2026 attivo
- Tutta la configurazione è un playbook Ansible: un nodo nuovo si installa e si unisce al cluster con un lancio. Con una prova a vuoto (
--check) mensile si verifica che niente sia cambiato a mano. - Il playbook, la guida d'uso e gli script pubblicati come software libero (AGPL-3.0 per il codice, CC BY-SA 4.0 per i testi), senza segreti né dati: codice sorgente attivo. Un installatore interattivo, perché chiunque possa mettere in piedi lo stesso cluster sui propri server previsto
Controlli e avvisi
- Su ogni nodo un timer systemd lancia ogni cinque minuti uno script di controllo: servizi attivi, disco, ora, certificati, replica del database, stato di etcd, Garage e Sentinel.
- Integrità della WKD: il nodo senza sito scarica da
mochela.itle chiavi pubblicate e ne confronta lo SHA-256 con quello dei file nel repository. Una chiave cambiata sul server fa scattare l'avviso. - I nodi si controllano a vicenda attraverso WireGuard; i controlli del cluster li segnala un solo "portavoce", il primo nodo raggiungibile.
- Avvisi solo ai cambi di stato (guasto, rientro), via SMTP con TLS verso una casella esterna: non passano dalla posta che controllano.
Backup
- Ogni notte i nodi di posta esportano database (
pg_dump), messaggi (copia dell'archivio Garage) e configurazione di Stalwart. - Il portatile di amministrazione prende l'esportazione con rsync su SSH, con una chiave dedicata che il server limita alla sola lettura di quella cartella (
rrsync -ro). - Nello stesso archivio, ogni versione dei programmi che non vengono da Debian (Stalwart, stalwart-cli, SimpleX, Garage; Galène come sorgente, compilato sul portatile) con il suo codice sorgente e l'impronta SHA-256 sotto git: il portatile li scarica una volta e li copia sui nodi, che non si collegano a GitHub.
- Archivio Borg (cifratura
repokey-blake2, compressione zstd) su un disco LUKS2 che resta scollegato. Conservazione: 7 giornalieri, 4 settimanali, 6 mensili. secondo disco altrove
Software
Tutto software libero.
| A cosa serve | Programma |
|---|---|
| Sistema | Debian 13 |
| Posta (SMTP, IMAP, JMAP) | Stalwart |
| Web, HTTPS | nginx, certbot |
| Rete cifrata tra i nodi | WireGuard |
| Firewall | nftables |
| DNS | Unbound |
| Ora | chrony (NTS) |
| Quorum | etcd |
| Database | PostgreSQL, Patroni, HAProxy |
| Archivio dei messaggi | Garage |
| Dati temporanei | Valkey, Sentinel |
| Messaggistica | SimpleX (server SMP) |
| Videochiamate | Galène |
| Configurazione | Ansible |
| Backup | Borg, LUKS |
Hai trovato qualcosa che non torna? Scrivi a info@mochela.it: le correzioni sono benvenute.