Home

Tutanum Classico

Guida pratica

Tutanum Classico

Introduzione

Per le basi (didattica): Il percorso della didattica.
Per i motivi (discussione): Indice della discussione, Che cosa è Tutanum?, Modello di minaccia.

Questa guida costruisce un nodo Tutanum su hardware scelto apposta per lo scopo. È la parte pratica della Discussione: lì si trova il perché di ogni scelta, qui la sequenza delle operazioni. Ogni passo rimanda alla sezione che lo motiva, e va letta almeno una volta prima di eseguirlo.

Chi vuole invece riutilizzare un computer che possiede già segue Tutanum Lieve, che parte da questa stessa guida e vi aggiunge le mitigazioni necessarie.

Internet, attraverso un router proprio(zona separata, niente Wi-Fi)Host FreeBSD: disco cifrato, securelevel, nessun servizio proprioipfw(livello 2)pf(livelli 3 e 4)dnscrypt-proxy(DNS cifrato)auditd, sentinella, collettore(monitoraggio e difesa)Jail: un servizio per jail, un indirizzo per jailwebnginx, nessuna uscitatorclient e onion servicei2pi2pd e bridge SAMiopsolo verso i2pescahoneypot isolatoSolo le jail tor e i2p (in verde) parlano con Internet. web risponde soltanto a tor, iop parla soltanto con i2p.
L'infrastruttura di Tutanum Classico: l'host non offre servizi, filtra e osserva. Ogni servizio vive in una jail con un indirizzo proprio, e verso Internet escono soltanto le jail delle reti anonime.

La costruzione procede per livelli, dal ferro ai servizi. Ogni livello dà per fatto il precedente.

LivelloChe cosa si ottieneDiscussione di riferimento
1. Hardware e firmwareUna macchina adatta, collocata, alimentata e collegata nel modo giusto Hardware, Firmware
2. Sistema operativoFreeBSD installato offline, cifrato e messo in sicurezza (hardening) Sistema operativo
3. Firewall e DNSFiltro a due livelli e risoluzione dei nomi cifrata Firewall, Proxy
4. JailUn compartimento per ogni servizio Namespace, container e jail
5. Reti anonimeTor, I2P e iop Tor, I2P
6. ServiziSito come onion service, SSH, trasferimento file Servizi esposti
7. Monitoraggio e manutenzioneAudit, difesa, backup e aggiornamenti Monitoraggio e difesa, Backup

Prima di iniziare

Per le basi (didattica): La sicurezza di rete.
Per i motivi (discussione): OPSEC: il livello umano, Password, Crittografia.

Materiale

  • Il computer che diventerà il nodo, scelto come indicato al livello 1.
  • Una SSD SATA nuova. Niente chiavette USB o microSD come disco di sistema: con ZFS si corrompono in fretta (vedi Estensioni).
  • Un DVD-R e un lettore CD/DVD USB. Un supporto che non si può riscrivere non può essere infettato dopo la masterizzazione.
  • Un secondo computer, fidato, per scaricare e verificare l'immagine e per consultare la documentazione mentre il nodo resta scollegato.
  • Un cavo di rete e un router proprio, non quello fornito dal provider.
  • Le passphrase si tengono a mente oppure in un password manager sul computer fidato. Non si scrivono mai su carta.

Regole che valgono per tutta la guida

  • Scollegato dalla rete finché il firewall non è attivo: il cavo di rete resta scollegato dall'installazione fino a quando il firewall del livello 3 è in funzione (vedi Rete).
  • Niente wireless: Wi-Fi e Bluetooth vanno disattivati e, dove possibile, rimossi fisicamente.
  • Tre passphrase distinte: cifratura del disco, root e utente di amministrazione. Si costruiscono come indicato in Password.
  • Nomi che dicono a cosa serve ogni cosa: nome host, utenti e jail devono far capire a colpo d'occhio la loro funzione. Si difende bene solo ciò che si riconosce: un sistema che il suo amministratore non padroneggia è debole e resta in balia degli automatismi. Fuori dai nomi resta soltanto l'identità di chi amministra (nome, cognome, soprannomi). In questa guida il nodo si chiama nodo, l'amministratore operatore e ogni jail prende il nome del servizio che ospita (tor, i2p, web).
  • Un servizio, una jail: sull'host non gira nulla che ascolti la rete.
  • Mai root direttamente: si lavora dall'utente di amministrazione, un comando per volta con doas (vedi Privilegi amministrativi).

Negli esempi la rete di casa è 192.168.178.0/24, l'interfaccia di rete è em0 e le jail stanno in 10.0.0.0/24. Vanno sostituiti con i propri valori.

Livello 1: hardware e firmware

Per le basi (didattica): Architettura di Neumann, Memoria, bus e ingresso/uscita, Circuiti e componenti, Campi e onde.
Per i motivi (discussione): CPU, Posizionamento, Alimentazione, BIOS e UEFI, Intel Management Engine e AMD PSP, Attacchi hardware delle intelligence.

Scegliere la macchina

I requisiti sono quelli elencati in Quali sono le caratteristiche di Tutanum?: CPU supportata da FreeBSD o OpenBSD, almeno 4 core (meglio 8), almeno 4 GB di RAM, bassi consumi, hardware stabile e facile da reperire. A questi il Classico aggiunge il requisito che lo distingue dal Lieve: una catena di avvio che si possa verificare.

StradaEsempioPunti di forzaDa verificare prima dell'acquisto
Single board computer ARMBanana Pi BPI-M7 (RK3588, da 8 a 32 GB di RAM), serie BPI-M* Niente Intel ME né AMD PSP, consumi minimi, clusterizzabile Lo stato del supporto della scheda in FreeBSD o OpenBSD, e la presenza di binari nel boot loader
x86 con firmware liberoUna scheda tra quelle supportate da Libreboot o Coreboot Supporto pieno del sistema operativo, firmware sostituito e Intel ME neutralizzato Che il modello esatto sia nell'elenco di compatibilità
RISC-VStarFive VisionFiveArchitettura aperta È ancora presto per la produzione: solo per sperimentare

Sono da escludere gli smartphone e, per il Classico, il Raspberry Pi, che si avvia con firmware proprietario (vedi Single Board Computer). Un comune PC con UEFI proprietario e Intel ME attivo non è un Classico: è un Lieve.

La macchina si acquista in contanti, in negozio, senza farsela spedire: una spedizione può essere intercettata (vedi Catena di fornitura).

Preparare la macchina

  1. Fotografare: scheda, saldature, etichette e sigilli, appena aperta la confezione. Le foto sono il riferimento per accorgersi di una manomissione futura.
  2. Togliere ciò che non serve: moduli Wi-Fi e Bluetooth, microfoni, webcam, altoparlanti. Ciò che non c'è non può essere acceso da remoto.
  3. Chiudere le interfacce pericolose: le porte con accesso diretto alla memoria (Thunderbolt, FireWire, ExpressCard) e quelle di debug (JTAG, UART) vanno disattivate o sigillate (vedi Attacchi con accesso fisico).
  4. Firmware: installare Libreboot o Coreboot dove previsto, annotarne l'impronta, impostare una password e disattivare l'avvio da rete e da USB. Sulle macchine x86 l'installazione avverrà in modalità BIOS con tabella GPT, non UEFI (vedi BIOS e UEFI).
  5. Orologio: rimuovere la batteria tampone e usare un modulo RTC esterno (DS3231), come indicato in Tempo. Prima si controlla che il firmware conservi password e impostazioni anche senza batteria: su molte schede si perdono, e in quel caso la batteria resta e il modulo esterno si aggiunge.
  6. Sigillare il case per accorgersi se qualcuno lo apre: applicare sulle viti e sulle giunture dei sigilli antimanomissione numerati, oppure una goccia di smalto con brillantini. I brillantini si dispongono a caso e nessuno riesce a rifare lo stesso disegno, quindi chi svita e richiude lascia una traccia. Fotografare ogni sigillo da vicino e confrontare la foto a ogni controllo (vedi Attacchi con accesso fisico).

Collocare, alimentare, collegare

  • Posizione: ancorata se fissa, in valigetta ventilata se deve muoversi, comunque fuori dalla vista e lontana dalle finestre (vedi Posizionamento e Compromissione elettromagnetica).
  • Ambiente: temperatura e umidità stabili. Oltre che per l'hardware, una temperatura costante toglie un canale laterale (vedi Calore e umidità).
  • Alimentazione: un gruppo di continuità, così che un'interruzione di corrente nel quartiere non faccia sparire il servizio nello stesso istante (vedi Alimentazione).
  • Rete: solo cavo, dietro un router proprio riportato alle impostazioni di fabbrica, con la password di amministrazione cambiata, il Wi-Fi spento e il nodo in una zona separata dagli altri dispositivi di casa.

Livello 2: sistema operativo

Per le basi (didattica): Sistemi operativi, File system, Processi e thread, Kernel e MINIX.
Per i motivi (discussione): Distribuzioni, Filesystem, Crittografia, Boot loader, Gestione degli utenti e dei gruppi, Verifica e integrità del software.

La guida usa FreeBSD, per le ragioni discusse in Distribuzioni. Lo stesso percorso vale, con comandi diversi, per OpenBSD.

Scaricare e verificare l'immagine

Sul computer fidato si scarica l'immagine dvd1 dell'ultima RELEASE dal sito di FreeBSD, insieme al file delle somme di controllo firmato. Un'immagine non verificata non si masterizza (vedi Verifica e integrità del software).

# verificare la firma del file delle somme, poi la somma dell'immagine gpg --verify CHECKSUM.SHA256-FreeBSD-*-RELEASE-amd64.asc sha256sum -c CHECKSUM.SHA256-FreeBSD-*-RELEASE-amd64 --ignore-missing # masterizzare su DVD-R e chiudere il disco, così che non sia più scrivibile

Sulle schede ARM l'installazione parte da un'immagine scritta sul supporto di avvio anziché da un DVD: la verifica è la stessa e le schermate del programma di installazione, dove è disponibile, non cambiano.

Installazione

Importante: procedere offline, con il cavo di rete scollegato. Conviene tenere aperto sul secondo computer il manuale di FreeBSD.

  1. Dal computer fidato masterizzare sul DVD-R l'immagine verificata al passo precedente, di FreeBSD oppure di OpenBSD, e collegare il lettore USB al computer che ospiterà Tutanum.
  2. Immettere il disco nel lettore e procedere ad installare il sistema operativo come segue (per questa guida abbiamo scelto FreeBSD):
    1. Selezionare "Boot Multi User" premendo il tasto invio.

      Schermata dell\'installazione
    2. Scegliere "Install" premendo il tasto invio.

      Schermata dell\'installazione
    3. Muoversi con le frecce su e giù della tastiera e posizionarsi su "Italian" e premere spazio per selezionare la mappatura italiana della tastiera.

      Al fine di assicurarsi che sia la mappatura corretta selezionare con il tasto invio la voce "Test it.kbd keymap".

      Per accettare l'impostazione inserita, muoversi su "Continue with it.kbd keymap" e premere invio.

      Schermata dell\'installazione
    4. Impostare come hostname un nome generico, che non dica nulla del nodo né di chi lo gestisce (per esempio "nodo"), e proseguire premendo il tasto invio.

      Schermata dell\'installazione
    5. Muovendosi con le frecce su e giù della tastiera selezionare con la barra spaziatrice le voci "ports" per il supporto al software a cui hanno fatto il porting (solitamente da GNU Linux) e "src" per la compilazione del software. La voce "lib32" (librerie a 32 bit) resta deselezionata: nessun servizio del nodo ne ha bisogno, e sarebbe solo codice in più da attaccare.

      Schermata dell\'installazione
    6. Selezionare la voce "Auto (ZFS)" per procedere con il partizionamento guidato del disco con filesystem di tipo OpenZFS.

      FreeBSD usa come filesystem predefinito OpenZFS, erede dello ZFS di Sun Microsystems. La cifratura del disco proposta dall'installatore è GELI, che cifra l'intera partizione sotto ZFS; la cifratura nativa di OpenZFS è un'altra cosa, che si applica ai singoli dataset. Il sistema si carica con il boot loader di FreeBSD (gptzfsboot e loader), più piccolo di GRUB e senza i suoi problemi. Il codice di avvio resta però fuori dalla parte cifrata: per questo l'host lavora con il securelevel a 2 (vedi Hardening).

      Schermata dell\'installazione
    7. Spostarsi con le frecce su e giù della tastiera per selezionare le voci da cambiare. Per potere scegliere un altro valore è possibile premere il tasto invio della tastiera una volta posizionati sulla voce interessata.

      È necessario impostare "Encrypt Disks" su "YES", "Partition Scheme" su "GPT (BIOS)" e "Encrypt Swap" su "YES". Una volta terminato posizionarsi sulla voce "Install" e premere invio.

      Schermata dell\'installazione
    8. Selezionare la voce "stripe" e premere invio, così da non impostare nessun RAID: ogni nodo ha un disco fisico proprio, e la ridondanza si ottiene con più nodi e con i backup, non con più dischi nella stessa macchina.

      Schermata dell\'installazione
    9. Selezionare con la barra spaziatrice il block device interessato e premere il tasto invio. Le voci potrebbero variare: le schermate di questa guida vengono da un'installazione di prova in macchina virtuale.

      Schermata dell\'installazione
    10. Spostarsi con le frecce sinistra e destra su "YES" e premere invio per procedere con la formattazione dei dischi (assicurarsi di non averci salvato sopra nulla di importante in quanto la memoria andrà ad essere formattata).

      Schermata dell\'installazione
    11. Impostare una passphrase per la cifratura del disco.

      Digitare la password scelta e premere invio.

      Schermata dell\'installazione
    12. Reinserire la password e premere invio.

      Schermata dell\'installazione
    13. Attendere la fine della formattazione del disco e l'installazione del sistema operativo.

      Schermata dell\'installazioneSchermata dell\'installazione
    14. Seguendo i consigli precedentemente riportati, impostare una password per root. Verrà richiesto di inserirla due volte. Per i non conoscenti dell'inserimento delle password sui sistemi Unix like, non verrà mostrata al momento della digitazione, questo per motivi di sicurezza.

      Schermata dell\'installazione
    15. Selezionare l'interfaccia di rete appropriata e premere invio.

    16. Selezionare la voce "Yes" e premere invio.

      Schermata dell\'installazione
    17. Alla domanda sul DHCP selezionare "No" e, nella schermata che segue, inserire l'indirizzo statico del nodo (in questa guida 192.168.178.3, maschera 255.255.255.0, router 192.168.178.1). sshd, sshguard e pf lo danno per fisso, e con il MAC casuale a ogni avvio il DHCP lo cambierebbe a ogni riavvio. La schermata mostra il "Yes" dell'installazione di prova.

      Schermata dell\'installazione
    18. Selezionare la voce "No" e premere invio, in quanto dall'IPv6 è possibile ricostruire il Mac Address.

      Schermata dell\'installazione
    19. Nella schermata dei DNS lasciare il router, selezionare "OK" spostandosi con il tasto Tab e premere invio. Verrà sostituito da dnscrypt-proxy al livello 3.

      Schermata dell\'installazione
    20. Digitare il tasto 7 per selezionare "Europe" come zona temporale e poi premere invio.

      Schermata dell\'installazione
    21. Selezionare "Italy" (è il 22) usando le frecce della tastiera e premere invio.

      Schermata dell\'installazione
    22. Premere invio per confermare le scelte configurate.

      Schermata dell\'installazione
    23. Selezionare "Set Date" usando le frecce sinistra e destra della tastiera e poi premere invio.

      Schermata dell\'installazione
    24. Procedere nello stesso modo con l'ora attuale.

      Schermata dell\'installazione
    25. Deselezionare tutte le voci con la barra spaziatrice e premere "OK". Se non si usa un servizio è necessario non installarlo o non renderlo attivo.

    26. Selezionare con la barra spaziatrice tutte le voci e premere "OK". In questo modo il sistema operativo verrà messo in hardening. Tra le voci c'è "Secure console": da qui in poi la modalità utente singolo chiede la password di root, che quindi non va mai bloccata (vedi Primo aggiornamento e doas).

      Schermata dell\'installazione
    27. Selezionare "Yes" e premere invio per creare un nuovo utente non root.

    28. Inserire a preferenza il nome dell'utente non root da utilizzare e premere invio. Evitare ovviamente di utilizzare nomi personali o che possano ricondurre a sé, ma che siano facilmente ricordabili. Per Uid, Login group, Login group, Login class, Shell, Home directory, Home directory permissions e per le ulteriori voci che seguono, premere invio, usando pertanto il valore di default. Quando verrà richiesto l'inserimento di una password, crearne una apposita per l'utente creato, che sia diversa da quella di root e dalla cifratura del disco.

    29. Confermare la scelta scrivendo "yes" e poi premendo invio.

    30. Scrivere "no" e premere invio.

      Schermata dell\'installazione
    31. Selezionare la voce "Exit" e premere invio.

      Schermata dell\'installazione
    32. Spostarsi sulla voce "Yes" con le frecce sinistra e destra della tastiera e premere invio.

      Schermata dell\'installazione
    33. Alla richiesta di aprire una shell per le ultime modifiche manuali, rispondere "Yes": le modifiche sono quelle del passo seguente di questa guida.

Prima configurazione

Nella shell aperta dal programma di installazione, o al primo accesso come root, si sistema /etc/rc.conf. Il file completo e commentato è config/rc.conf, descritto in Configurazioni di esempio. Le righe essenziali a questo punto sono queste.

# /etc/rc.conf hostname="nodo" # generico: il nome compare in registri e messaggi clear_tmp_enable="YES" syslogd_flags="-ss" # nessun socket di rete per syslog sendmail_enable="NONE" dumpdev="NO" # niente crash dump su disco zfs_enable="YES" ntpd_enable="NO" # niente NTP: vedi "Il tempo come vettore d'attacco" ipv6_network_interfaces="none" ipv6_activate_all_interfaces="NO" ifconfig_em0="ether random" # indirizzo MAC casuale a ogni avvio ifconfig_em0_alias0="inet 192.168.178.3/24" # indirizzo statico: con il MAC casuale il DHCP lo cambierebbe defaultrouter="192.168.178.1" cloned_interfaces="lo1" # l'interfaccia delle jail (livello 4) ifconfig_lo1="inet 10.0.0.1/24" # l'host visto dalle jail: vi ascolterà dnscrypt-proxy

Le ragioni sono in IPv6 e MAC Address e in Il tempo come vettore d'attacco. Si riavvia con reboot e si accede come root dalla console: fino al primo aggiornamento, quando verrà installato doas, i comandi di questa guida preceduti da doas si eseguono direttamente da root.

Messaggio di accesso

Il messaggio predefinito annuncia sistema e versione del kernel a chiunque apra una sessione. Lo si sostituisce con un testo che non descrive nulla (vedi Il messaggio di accesso e le jail).

doas sysrc update_motd=NO printf 'host. Qui non gira alcun servizio: ogni servizio ha la propria jail.\nOgni comando privilegiato passa da doas e viene registrato.\n' | doas tee /etc/motd.nodo # /etc/login.conf, classe "default": aggiungere :welcome=/etc/motd.nodo:\ e togliere la riga :copyright=... doas cap_mkdb /etc/login.conf

Hardening

Le impostazioni seguono la pagina security(7) del manuale, spiegata voce per voce in FreeBSD: nucleo del kernel, dispositivi grezzi e filesystem, le indicazioni di Kicksecure tradotte per FreeBSD e le contromisure contro la fuga dell'ora.

# /etc/sysctl.conf security.bsd.see_other_uids=0 # ognuno vede solo i propri processi security.bsd.see_other_gids=0 security.bsd.see_jail_proc=0 security.bsd.unprivileged_read_msgbuf=0 # il log del kernel solo a root security.bsd.unprivileged_proc_debug=0 # niente ptrace tra processi non privilegiati security.bsd.conservative_signals=1 security.bsd.hardlink_check_uid=1 security.bsd.hardlink_check_gid=1 kern.randompid=1 kern.coredump=0 kern.elf64.aslr.enable=1 # randomizzazione degli indirizzi kern.elf64.aslr.pie_enable=1 kern.elf64.aslr.stack=1 kern.elf64.aslr.honor_sbrk=0 kern.elf64.nxstack=1 # stack non eseguibile kern.elf64.allow_wx=0 # W^X: mai pagine scrivibili ed eseguibili insieme machdep.syscall_ret_flush_l1d=1 machdep.mitigations.rngds.enable=1 net.inet.ip.random_id=1 net.inet.tcp.rfc1323=0 # niente timestamp TCP (né window scaling: meno banda, nessun orologio esposto) net.inet.icmp.tstamprepl=0 # niente timestamp ICMP net.inet.tcp.blackhole=2 # nessuna risposta sulle porte chiuse net.inet.udp.blackhole=1 net.inet.ip.redirect=0 net.inet.icmp.drop_redirect=1 net.link.ether.ipfw=1 # ipfw esamina anche i frame ethernet (livello 2) net.inet.tcp.drop_synfin=1 # scarta i SYN+FIN, tipici delle scansioni security.bsd.unprivileged_mlock=0 # solo root blocca pagine in memoria security.bsd.unprivileged_idprio=0 # solo root usa le priorità di inattività

Sulle macchine x86 si aggiungono le mitigazioni dei difetti del processore, le stesse di Tutanum Lieve: anche una scheda con Coreboot monta una CPU con Meltdown, Spectre e varianti (vedi CPU). Il microcodice (cpu-microcode) si installa con il primo aggiornamento.

# /boot/loader.conf (solo x86) vm.pmap.pti=1 # Meltdown machdep.hyperthreading_allowed=0 # niente hyper-threading: toglie una famiglia di canali laterali # /etc/sysctl.conf (solo x86) hw.ibrs_disable=0 # Spectre v2 hw.mds_disable=3 # MDS hw.spec_store_bypass_disable=2 # Spectre v4 # /etc/rc.conf auditd_enable="YES" # il securelevel si alza solo all'ultimo passo, "Sigillare i file di avvio" # /etc/fstab: le cartelle scrivibili da tutti non eseguono tmpfs /tmp tmpfs rw,noexec,nosuid,mode=1777 0 0 doas zfs set exec=off zroot/var/tmp doas zfs set setuid=off zroot/var/tmp # i file nuovi non leggibili dagli altri utenti: in /etc/login.conf, classe default, :umask=027: # root però resta a 022: doas e pkg ereditano la umask, e con 027 le cartelle create installando # i pacchetti (provato: /usr/local/sbin) diventano illeggibili per gli utenti dei servizi, e # dnscrypt-proxy non parte ("No such file or directory"). In /etc/login.conf, classe root, :umask=022: doas cap_mkdb /etc/login.conf doas pw usermod root -L root # root nella propria classe (anche in ogni jail) doas sh -c umask; umask # -> 0022 con doas, 0027 per operatore # toor è un secondo account con UID 0, senza password ma inutile: si toglie doas pw userdel toor # cron e at soltanto per root: un utente compromesso non si lascia dietro compiti ripetuti echo root | doas tee /var/cron/allow /var/at/at.allow > /dev/null doas chmod 600 /var/cron/allow /var/at/at.allow

allow_wx=0 blocca i programmi che generano codice mentre girano: va provato con ogni servizio prima di renderlo definitivo.

A nodo finito l'host lavora al securelevel 2. Il livello 1 non basta: lascia scrivere sui dischi non montati, e quindi sulla partizione con il codice di avvio, che sta fuori dalla parte cifrata. Un intruso diventato root potrebbe sostituirlo e catturare la passphrase del disco al riavvio successivo. Il livello 2 ha però un effetto che va conosciuto prima: su FreeBSD 15 congela pf. Non si caricano regole, non si aggiungono indirizzi alle tabelle, non si chiudono connessioni e non si spegne il firewall; si possono soltanto togliere indirizzi. Per questo il livello si alza all'ultimo passo, quando pacchetti e jail sono a posto, e tutto ciò che deve cambiare a sistema avviato (sshguard, contenimento, esche) vive nelle tabelle di ipfw, che al livello 2 restano modificabili. Il livello 3, che congela anche ipfw, si assegna alle jail come indicato al livello 4 (vedi Il livello di sicurezza del kernel). Dal livello 2 il kernel sposta l'orologio al massimo di un secondo per volta e non lo riporta indietro: va regolato prima, come indicato in Orologio.

Con il securelevel attivo i moduli del kernel non si caricano più a sistema avviato: pf, ipfw e ogni altro modulo necessario vanno dichiarati in /boot/loader.conf o in rc.conf prima del riavvio. L'audit si configura con le classi indicate in In pratica su FreeBSD: registrare tutto (all) produce un volume di eventi in cui non si trova più nulla.

Livello 3: firewall e DNS

Per le basi (didattica): La rete internet, Il trasporto, Applicazioni e web, La sicurezza di rete.
Per i motivi (discussione): Firewall, Difesa dalla forza bruta, Proxy, Privilegi amministrativi.

L'impianto è quello descritto in Configurazioni di esempio: ipfw filtra al livello 2 (ethernet e MAC), pf ai livelli 3 e 4 (IP, porte, NAT per le jail). Il principio è il default-deny, in ingresso e in uscita.

ipfw e pf

  1. Copiare config/ipfw.rules in /etc/ipfw.rules e config/pf.conf in /etc/pf.conf, sostituendo interfaccia e indirizzi con i propri: le macro in cima al file li raccolgono tutti.
  2. Abilitare i due firewall e i relativi log.
  3. Provare le regole prima di caricarle, e caricarle dalla console, mai da remoto.
doas sysrc firewall_enable=YES firewall_script=/etc/ipfw.rules firewall_logging=YES doas sysrc pf_enable=YES pf_rules=/etc/pf.conf pflog_enable=YES pflog_logfile=/var/log/pflog echo 'pf_load="YES"' | doas tee -a /boot/loader.conf doas pfctl -nf /etc/pf.conf # solo controllo della sintassi: nessun output = nessun errore doas service ipfw start doas service pf start doas service pflog start doas pfctl -sr # le regole in vigore doas pfctl -si # i contatori

Il file di pf fa quattro cose che vale la pena sapere prima di caricarlo. L'host esce in rete soltanto con dnscrypt-proxy: tutto il resto passerà da Tor, e se Tor è fermo fallisce invece di uscire in chiaro. Verso Internet escono soltanto le jail tor e i2p, riconosciute da un'etichetta che mette il NAT. Né lo0 né lo1 si saltano, perché il traffico tra le jail e verso l'host passa da lì: saltarle vorrebbe dire non filtrare più nulla tra un servizio e l'altro. E le regole quick sono in un ordine preciso, con i blocchi delle tabelle prima di ogni pass: ciò che si aggiunge va messo nella sezione giusta.

I compiti sono divisi così: pf è la politica fissa, caricata all'avvio; ipfw, oltre al livello 2, tiene le tabelle che cambiano a sistema avviato (quarantena, 22 di sshguard, esche, mai_bloccare). Le tabelle di ipfw hanno anche un altro vantaggio: ipfw esamina ogni pacchetto, quindi un indirizzo inserito perde subito anche le connessioni già aperte, che pf invece lascerebbe proseguire. Dopo la sigillatura ogni modifica a pf.conf vale dal riavvio successivo.

Contro la forza bruta il primo strato è già nel file: la tabella di overload di pf, riempita dal kernel, da cui il computer di amministrazione è escluso (bastano quattro connessioni in quindici secondi per finirci dentro). Le sue voci non scadono da sole: le toglie cron.

# /etc/crontab: le voci di overload scadono dopo un giorno (togliere indirizzi resta permesso) 7 * * * * root /sbin/pfctl -q -t bruteforce -T expire 86400

Sopra si mette un solo demone, sshguard, che si installerà con il primo aggiornamento. blacklistd e fail2ban fanno la stessa cosa e non vanno sommati: il confronto e le configurazioni complete sono in Difesa dalla forza bruta. Il blocco di un intero paese è in Bloccare gli IP di una nazione.

Solo ora, con i due firewall in funzione, si collega il cavo di rete.

Primo aggiornamento e doas

Il sistema appena installato è già vecchio. Il primo aggiornamento avviene dietro il firewall, prima di esporre qualsiasi servizio. Tor però non c'è ancora, e pf lascia uscire soltanto dnscrypt-proxy: per questa volta si riempie la tabella manutenzione, prevista in pf.conf, che apre all'host DNS, HTTP e HTTPS. Resta aperta finché la jail tor non funziona (livello 5), e poi si svuota. Funziona perché il securelevel non è ancora salito.

# apertura temporanea, dalla console pfctl -t manutenzione -T add 0.0.0.0/0 pfctl -t manutenzione -T show # -> 0.0.0.0/0: l'host esce in chiaro fino al livello 5 zfs snapshot -r zroot@prima-del-primo-aggiornamento freebsd-update fetch install # solo con i set di distribuzione: con pkgbase lo fa il pkg upgrade qui sotto pkg update && pkg upgrade pkg audit -F # vulnerabilità note nei pacchetti installati pkg install cpu-microcode # solo x86: poi cpu_microcode_load="YES" in /boot/loader.conf # doas al posto di sudo pkg install doas echo "permit operatore as root" > /usr/local/etc/doas.conf chown root:wheel /usr/local/etc/doas.conf && chmod 600 /usr/local/etc/doas.conf doas -C /usr/local/etc/doas.conf # nessun output = sintassi corretta

È la regola minima. I ruoli più stretti (sola osservazione, backup, gestione dei casi dell'EDR) e il significato di ogni parola chiave sono in Configurare doas.conf.

Si esce, si accede come operatore e si verifica che doas id risponda uid=0(root). Da qui in poi root non si usa più, ma non si blocca con pw lock: con la console marcata insecure la modalità utente singolo chiede la sua password, ed è proprio da lì che passano gli aggiornamenti del sistema di base dopo la sigillatura. Bloccarlo vorrebbe dire chiudersi fuori. L'accesso diretto si chiude altrove: SSH non accetta root, e l'utente operatore non va aggiunto al gruppo wheel, così che nemmeno su funzioni. I suoi privilegi passano solo da doas (vedi Gestione degli utenti e dei gruppi).

sshguard

doas pkg install sshguard # /usr/local/etc/sshguard.conf BACKEND="/usr/local/libexec/sshg-fw-ipfw" # tabella 22 di ipfw: pf al livello 2 non accetta indirizzi FILES="/var/log/auth.log" THRESHOLD=30 # bando al terzo tentativo BLOCK_TIME=600 DETECTION_TIME=1800 BLACKLIST_FILE=90:/var/db/sshguard/blacklist.db WHITELIST_FILE=/usr/local/etc/sshguard.whitelist # /usr/local/etc/sshguard.whitelist: il computer di amministrazione e la jail tor, nient'altro 192.168.178.10 10.0.0.2 doas install -d -m 700 /var/db/sshguard # per la lista dei bandi permanenti doas sysrc sshguard_enable=YES doas service sshguard start doas ipfw table 22 list # vuota, finché nessuno insiste

Nell'elenco di chi non va bandito stanno soltanto il computer da cui si amministra, per non chiudersi fuori, e la jail tor: ciò che arriva dall'onion service di amministrazione ha sempre il suo indirizzo, e bandirlo significherebbe chiudere quell'accesso a tutti (vedi Dietro Tor e I2P il bando per indirizzo non funziona). Il resto della rete locale non è nell'elenco: SSH ascolta solo lì, ed è proprio da lì che arriverebbe un dispositivo di casa compromesso.

DNS cifrato

Il nodo non deve chiedere i nomi in chiaro al router. Si installa dnscrypt-proxy con la configurazione config/dnscrypt-proxy.toml, che richiede DNSSEC e server senza registri, usa i relay anonimi e blocca IPv6. Ogni opzione è spiegata in Proxy, insieme alla variante che instrada le richieste dentro Tor.

doas pkg install dnscrypt-proxy2 doas cp dnscrypt-proxy.toml /usr/local/etc/dnscrypt-proxy/dnscrypt-proxy.toml # il file ascolta su 127.0.0.1:53 e su 10.0.0.1:53 (l'host visto dalle jail) e non tiene registri delle richieste doas sysrc dnscrypt_proxy_enable=YES doas sysrc dnscrypt_proxy_mac_portacl_enable=YES # permette al demone non privilegiato di usare la porta 53 doas service dnscrypt-proxy start echo "nameserver 127.0.0.1" | doas tee /etc/resolv.conf doas chflags schg /etc/resolv.conf # nessun programma può più riscriverlo drill freebsd.org @127.0.0.1 # deve rispondere

Allo stesso proxy si affida il filtro dei domini di tracciamento e degli indicatori malevoli: una lista di blocco è un file di testo, senza alcun servizio in più. È l'alternativa minima a Pi-hole, discussa in Le alternative minime.

# /usr/local/etc/dnscrypt-proxy/dnscrypt-proxy.toml [blocked_names] blocked_names_file = '/usr/local/etc/dnscrypt-proxy/blacklist.txt' #log_file = '/var/log/dnscrypt-proxy/blocked-names.log' # solo per una diagnosi: registra i nomi bloccati # blacklist.txt: un nome o un modello per riga *.doubleclick.net ads.* =dominio-esatto.example # i domini malevoli dei feed MISP si aggiungono in coda sort -u /usr/local/etc/dnscrypt-proxy/blacklist.txt /var/db/ioc/domini.txt \ | doas tee /usr/local/etc/dnscrypt-proxy/blacklist.new > /dev/null doas mv /usr/local/etc/dnscrypt-proxy/blacklist.new /usr/local/etc/dnscrypt-proxy/blacklist.txt doas service dnscrypt-proxy restart drill doubleclick.net @127.0.0.1 # -> rifiutato

Livello 4: jail

Per le basi (didattica): Processi e thread, Memoria virtuale.
Per i motivi (discussione): Namespace, container e jail, IPC (comunicazione tra i processi).

Ogni servizio vive in una jail, per le ragioni spiegate in Namespace, container e jail. La jail è anche l'unità di contenimento dell'EDR: se un servizio cade, si ferma quella jail e il resto continua.

JailIndirizzoChe cosa contieneCon chi può parlare
tor10.0.0.2Tor e nient'altroInternet (solo bridge o guard), jail web
i2p10.0.0.3i2pd con il bridge SAMInternet
web10.0.0.10nginxNessuno: risponde soltanto alla jail tor
iop10.0.0.11Il demone di iopSolo la jail i2p, porta SAM
esca10.0.0.66HoneypotNessuno

Installare CBSD

doas pkg install cbsd doas zfs create -o mountpoint=/jails zroot/jails doas env workdir="/jails" cbsd initenv

initenv pone una serie di domande. Le risposte per un nodo Tutanum (provate con CBSD 15.1 su FreeBSD 15.1; le stesse si possono scrivere in un file e passare a /usr/local/cbsd/sudoexec/initenv, partendo da /usr/local/cbsd/share/initenv.conf):

DomandaRispostaPerché
Preparare l'ambiente (prepare or upgrade hier environment)1
Aggiungere l'utente cbsd a sudoers0Ogni modifica alle jail deve passare da doas
Scrivere cbsd_workdir in rc.conf1
nodenamenodoLo stesso nome generico dell'host
nodeipL'indirizzo del nodo nella rete locale
jnameserverL'indirizzo dell'host visto dalle jailLe jail risolvono attraverso dnscrypt-proxy, non attraverso il router
Rete delle jail10.0.0.0/24Gli indirizzi stanno su lo1
nat_enable0Il NAT è già in pf.conf, scritto a mano: escono soltanto tor e i2p. Quello di CBSD farebbe uscire tutte le jail
ipfw_enable0Le regole di ipfw sono quelle di ipfw.rules: CBSD non le deve toccare
Impostare pf_load=YES1
fbsdrepo1Base e kernel dai repository ufficiali di FreeBSD, non da quelli di CBSD
zfsfeat1Un dataset per jail: istantanee e ripristino
parallel5
stable0Solo RELEASE
sqlreplica, i tre statsd_*, RSYNC0Funzioni non necessarie: meno codice in esecuzione
RACCT1Serve per limitare le risorse delle jail con rctl
Scrivere cbsdd_enable=YES1
rcshutdown_timeout, preseedinit0

Il valore predefinito di jnameserver sono i DNS di Google: va cambiato, come in tabella. Poi si scarica la base delle jail, che arriva senza le correzioni di sicurezza (provato: 15.1-RELEASE contro la 15.1-RELEASE-p5 dell'host) e va subito aggiornata. baseupdate usa freebsd-update e non passa da Tor: si esegue ora, con la tabella manutenzione aperta, e in seguito insieme agli aggiornamenti del sistema di base.

Con nat_enable a 0 CBSD esegue natoff, che per quel valore svuota il NAT di pf (pfctl -F nat): succede alla fine di initenv e a ogni arresto o riavvio di cbsdd. Le regole di filtro restano, le due righe nat di pf.conf no, e Tor e I2P restano senza uscita (provato: pfctl -sn vuoto dopo initenv; all'avvio del sistema invece il NAT resta). Dopo initenv, e dopo ogni service cbsdd restart, si ricarica il file; dopo la sigillatura pf è congelato e il problema non si pone.

doas pfctl -f /etc/pf.conf && doas pfctl -sn # le due regole nat e i rdr doas cbsd repo action=get sources=base ver=15.1 doas cbsd baseupdate ver=15.1 doas jexec <jail> freebsd-version -u # -> 15.1-RELEASE-pN, come l'host

Creare e preparare una jail

doas cbsd jconstruct-tui # nome, indirizzo e host_hostname secondo la tabella sopra # interface: lo1, sempre. Con "auto" CBSD mette l'indirizzo su em0 # (provato): le regole di pf per le jail non varrebbero più doas cbsd jstart tor doas cbsd jls # stato delle jail doas cbsd jlogin tor # entrare nella jail

Ogni jail riceve un securelevel proprio, più alto di quello dell'host, e non vede i dispositivi con cui si intercetta il traffico o si legge la memoria.

# CBSD non ha un parametro per il securelevel: lo alza la jail stessa, dal proprio rc.conf doas jexec web sysrc kern_securelevel_enable=YES kern_securelevel=3 doas cbsd jstop web && doas cbsd jstart web doas jexec web sysctl kern.securelevel # -> 3 doas cbsd jget jname=web devfs_ruleset # -> 4, il predefinito di CBSD doas jexec web ls /dev # -> nessun bpf, mem, kmem, io # due valori predefiniti di CBSD da cambiare (provato con CBSD 15.1): il debug dei processi tra utenti # non privilegiati è permesso dentro la jail, e l'albero dei port dell'host vi è montato. I mount sono # già negati (enforce_statfs=2), ma si scrive tutto esplicitamente doas cbsd jstop web doas cbsd jset jname=web allow_unprivileged_proc_debug=0 mount_ports=0 enforce_statfs=2 \ allow_mount=0 allow_nullfs=0 allow_tmpfs=0 allow_fdescfs=0 allow_procfs=0 allow_devfs=0 doas cbsd jstart web doas jexec web sysctl -n security.bsd.unprivileged_proc_debug # -> 0 mount | grep -c "jails/web/usr/ports" # -> 0 jls -j web enforce_statfs allow.mount children.max # -> 2 false 0

Una jail compromessa non deve poter affamare le altre. I limiti di risorse richiedono kern.racct.enable=1 nel loader e un riavvio; si scrivono in /etc/rctl.conf, che li applica a ogni avvio. Provato: con maxproc a 40 una jail che tenta di avviare cento processi si ferma a 39, e un ciclo infinito resta sotto il tetto di CPU.

# /boot/loader.conf kern.racct.enable=1 # /etc/rctl.conf: per ogni jail, valori da adattare al carico jail:web:maxproc:deny=100 jail:web:memoryuse:deny=512m jail:web:pcpu:deny=50 jail:tor:memoryuse:deny=1g jail:i2p:memoryuse:deny=1g jail:iop:maxproc:deny=50 jail:esca:memoryuse:deny=512m jail:esca:maxproc:deny=100 jail:esca:pcpu:deny=20 doas sysrc rctl_enable=YES # senza, /etc/rctl.conf non si carica all'avvio doas rctl -u jail:web # uso corrente rispetto ai limiti

Le jail non escono su Internet, quindi i pacchetti non si installano da dentro: li scarica l'host e li installa nella radice della jail avviata con pkg -r. Con -r pkg leggerebbe la configurazione dentro la jail: con -C gli si passa quella dell'host, che contiene il proxy. Finché Tor non funziona passano dalla tabella manutenzione, poi da Tor. pkg però non aggiorna la cache delle librerie della jail: dopo ogni installazione si esegue service ldconfig start nella jail, altrimenti i programmi non trovano le librerie appena installate (provato con nginx).

Dentro ogni jail si ripete, in piccolo, ciò che si è fatto sull'host: un utente non privilegiato per il servizio e nessun accesso diretto come root.

# dall'host, senza entrare nella jail doas pkg -C /usr/local/etc/pkg.conf -r /jails/jails/tor install doas doas jexec tor service ldconfig start doas jexec tor pw useradd gestore -m -s /bin/sh echo "permit gestore as root" | doas tee /jails/jails-data/tor-data/usr/local/etc/doas.conf

Ogni jail ha anche il proprio messaggio di accesso, che dice all'operatore dove si trova e nient'altro: con più jail aperte, il comando giusto nella jail sbagliata è l'errore più facile.

printf 'jail: tor. Solo Tor, nessun altro programma.\nOgni comando privilegiato passa da doas e viene registrato.\n' \ | doas tee /jails/jails-data/tor-data/etc/motd.nodo doas jexec tor sysrc update_motd=NO # nella jail: stessa modifica a /etc/login.conf (umask 027 per default, 022 per root), poi doas jexec tor cap_mkdb /etc/login.conf doas jexec tor pw usermod root -L root # i file scritti dall'host nella jail prendono la umask di chi li scrive: i permessi si danno # sempre espliciti (install -m, chmod), come per le pagine del sito nella jail web

Con CBSD la radice di una jail è in /jails/jails/nome e i suoi dati in /jails/jails-data/nome-data. Gli script della sezione Antivirus, EDR e XDR presumono le jail direttamente sotto /jails: i percorsi e i nomi dei dataset vanno adattati.

Le jail non si parlano tra loro, salvo le eccezioni della tabella. Le regole sono già in config/pf.conf; queste sono le righe che le realizzano.

# traduzione: soltanto tor e i2p hanno il NAT, e il NAT le marca nat on $ext_if inet from $j_tor to any tag TOR -> ($ext_if) nat on $ext_if inet from $j_i2p to any tag I2P -> ($ext_if) # filtro: tra le jail passa solo ciò che è elencato. Gli indirizzi stanno su lo1, e il kernel # instrada il traffico verso di loro attraverso lo1 stessa (jail_if) pass quick on $jail_if proto tcp from $j_tor to $j_web port 8080 keep state # tor -> web pass quick on $jail_if proto tcp from $j_iop to $j_i2p port 7656 keep state # iop -> SAM di i2pd # verso Internet: dopo il NAT l'origine è em0, quindi si guarda l'etichetta pass out quick on $ext_if proto tcp tagged TOR modulate state pass out quick on $ext_if proto { tcp, udp } tagged I2P keep state # web, iop ed esca non hanno né NAT né etichetta: cadono nel "block log all"

Livello 5: reti anonime

Per le basi (didattica): La sicurezza di rete, La rete internet.
Per i motivi (discussione): Tor, Architettura di Tor, I2P, Architettura della rete, I2P rispetto a Tor.

Tor

Nella jail tor gira Tor e nient'altro (vedi Buone pratiche da Whonix). La configurazione che segue è quella commentata riga per riga in Configurare torrc.

doas pkg -C /usr/local/etc/pkg.conf -r /jails/jails/tor install tor obfs4proxy-tor doas jexec tor service ldconfig start doas jexec tor sysrc tor_enable=YES doas jexec tor install -d -o _tor -g _tor -m 700 /var/db/tor /var/run/tor # /var/run/tor: senza, il # ControlSocket non si crea e Tor non parte (provato) # /usr/local/etc/tor/torrc (nella jail tor) RunAsDaemon 0 AvoidDiskWrites 1 ClientOnly 1 ExitPolicy reject *:* ExitPolicy reject6 *:* SOCKSPort 10.0.0.2:59353 IsolateSOCKSAuth IsolateClientAddr IsolateClientProtocol IsolateDestAddr IsolateDestPort # Chi può usare il SOCKS, oltre a pf: l'host (che arriva con l'indirizzo della jail, 10.0.0.2) # e gli ospiti della rete interna di Tutanum Lieve; tutti gli altri vengono rifiutati da Tor stesso SocksPolicy accept 10.0.0.2, accept 10.0.1.0/24, reject * ClientUseIPv4 1 ClientUseIPv6 0 ClientRejectInternalAddresses 1 AllowNonRFC953Hostnames 0 WarnPlaintextPorts 23,109,110,143,80 TestSocks 1 AutomapHostsOnResolve 1 AutomapHostsSuffixes .exit, .onion VirtualAddrNetwork 10.192.0.0/10 ConnectionPadding 1 ReducedConnectionPadding 0 UseEntryGuards 1 NumEntryGuards 1 EnforceDistinctSubnets 1 NewCircuitPeriod 30 MaxCircuitDirtiness 600 CircuitsAvailableTimeout 86400 DormantCanceledByStartup 1 HardwareAccel 1 Schedulers Vanilla # Porta di controllo: solo un socket locale, con autenticazione a cookie (per vanguards) ControlSocket /var/run/tor/control CookieAuthentication 1 # Bridge: attivarli se l'uso di Tor va nascosto al fornitore di rete. Le righe "Bridge" sono le proprie. UseBridges 0 ClientTransportPlugin obfs4 exec /usr/local/bin/obfs4proxy managed # Bridge obfs4 <indirizzo:porta> <impronta> cert=<certificato> iat-mode=0

Nel file non ci sono ExcludeNodes né StrictNodes. Escludere i paesi che ospitano la maggior parte dei relay (Stati Uniti, Germania, Paesi Bassi, Francia) toglie al client gran parte della rete: i guard possibili diventano pochi, il client si distingue da tutti gli altri, e un onion service rallenta. Il Tor Project lo sconsiglia, e la protezione che promette è minore di quella che toglie. Chi vuole comunque escludere qualcosa si limita al proprio paese per il guard, sapendo che anche questa è una scelta che rende riconoscibili (vedi Configurare torrc).

I bridge si ottengono e si scelgono come spiegato in Il problema dei bridge: non si copiano da una guida, perché un bridge pubblicato è un bridge noto. Il file state, che contiene i guard, entra nei backup e non si cancella mai. Dalla versione 0.4.8 Tor include già vanguards-lite; per un onion service importante si aggiunge l'addon vanguards completo, che si collega al socket di controllo (vanguards --control_socket /var/run/tor/control), descritto negli attacchi alla rete Tor.

doas jexec tor service tor start # dal nodo: la jail esce davvero da Tor? curl --socks5-hostname 10.0.0.2:59353 https://check.torproject.org/api/ip # -> "IsTor":true # da ora i pacchetti dell'host e delle jail (pkg -C ... -r) passano da Tor # /usr/local/etc/pkg.conf PKG_ENV { SOCKS5_PROXY: "10.0.0.2:59353" } # e la tabella del primo aggiornamento si svuota: l'host non esce più in chiaro doas pfctl -t manutenzione -T flush doas pfctl -t manutenzione -T show # nessun indirizzo

Il sistema di base si aggiorna in due modi, secondo come è stato installato. Con i set di distribuzione si usa freebsd-update; con il sistema di base a pacchetti (pkgbase, quello delle immagini ufficiali di FreeBSD 15.1) freebsd-update si rifiuta di partire e il base si aggiorna con pkg dal repository FreeBSD-base. In entrambi i casi, a nodo sigillato, l'aggiornamento del base avviene in modalità utente singolo: i suoi file portano il flag schg, che al securelevel 2 nessuno può togliere. Lì Tor non gira, quindi per la durata dell'aggiornamento si riapre la tabella manutenzione, e il fornitore di rete vedrà un collegamento ai server di FreeBSD. La procedura è stata provata su FreeBSD 15.1.

# in modalità utente singolo (chiede la password di root); la radice è in sola lettura mount -u -o rw / && mount -a service netif start && service routing start service pf start && service dnscrypt-proxy start pfctl -t manutenzione -T add 0.0.0.0/0 freebsd-update fetch install # set di distribuzione cbsd baseupdate ver=15.1 # la base condivisa dalle jail pkg upgrade -r FreeBSD-base # pkgbase pfctl -t manutenzione -T flush # i file sostituiti tornano con i flag del pacchetto, non con quelli messi a mano: # si rifà la sigillatura (i chflags di "Sigillare i file di avvio"), poi exit # si prosegue con l'avvio normale

Fuori dalla modalità utente singolo si aggiornano soltanto i port, che passano da Tor: con pkgbase un pkg upgrade senza -r proverebbe ad aggiornare anche il base e si fermerebbe sui file sigillati.

Orologio

Il nodo non usa NTP in chiaro (vedi Il tempo come vettore d'attacco), ma Tor e I2P hanno bisogno dell'ora giusta: Tor smette di funzionare con un'ora indietro o tre avanti, e la rotazione delle chiavi di I2P si basa sulla data. All'installazione l'ora si imposta a mano; da quel momento la si controlla attraverso Tor, come fa sdwdate di Whonix: si legge l'intestazione Date di tre onion service indipendenti e si prende la mediana, mai una fonte sola.

#!/bin/sh # /usr/local/sbin/ora-da-tor: mediana dell'ora di tre onion service, attraverso la jail tor ora=$(for o in primo.onion secondo.onion terzo.onion; do d=$(curl -sI --max-time 60 --socks5-hostname 10.0.0.2:59353 "http://$o/" \ | sed -n 's/^[Dd]ate: //p' | tr -d '\r') [ -n "$d" ] && LC_ALL=C TZ=UTC date -j -f "%a, %d %b %Y %T GMT" "$d" +%s # nomi inglesi done | sort -n | sed -n 2p) [ -n "$ora" ] || { echo "meno di due fonti: nessuna correzione" >&2; exit 1; } scarto=$(( ora - $(date +%s) )) echo "scarto: ${scarto} s" # al securelevel 2 il kernel sposta l'orologio al massimo di un secondo, non più di una volta al # secondo, e non lo riporta indietro: un orologio indietro recupera un secondo per ogni secondo if [ "$scarto" -gt 1 ]; then while [ "$scarto" -gt 1 ]; do date -f %s "$(( $(date +%s) + 1 ))" > /dev/null sleep 1 scarto=$(( scarto - 1 )) done elif [ "$scarto" -lt -30 ]; then echo "orologio avanti di $(( -scarto )) s: va corretto a mano in modalità utente singolo" >&2 fi # una volta al giorno, da root echo '17 4 * * * root /usr/local/sbin/ora-da-tor | logger -t ora-da-tor' | doas tee -a /etc/crontab

I tre indirizzi si scelgono tra servizi gestiti da gruppi diversi, che non hanno motivo di accordarsi. Un orologio avanti si riporta indietro soltanto a mano, in modalità utente singolo, dove il securelevel non è ancora salito; con il modulo RTC esterno del livello 1 capita di rado.

I2P

I2P è la rete di riferimento per i servizi tra nodi (vedi I2P e Configurare un nodo I2P).

doas pkg -C /usr/local/etc/pkg.conf -r /jails/jails/i2p install i2pd doas jexec i2p service ldconfig start doas jexec i2p sysrc i2pd_enable=YES # /usr/local/etc/i2pd/i2pd.conf (nella jail i2p) pidfile = /var/run/i2pd/i2pd.pid # dove lo cerca lo script rc: senza, "service i2pd stop" non ferma nulla ipv4 = true ipv6 = false port = 24567 # porta fissa: la stessa di i2p_port in pf.conf (sceglierne una propria) [http] enabled = true address = 10.0.0.3 # console web: pf la apre soltanto all'host port = 7070 auth = true # e chiede comunque una password user = operatore pass = una-passphrase-propria [httpproxy] enabled = false # nessun proxy verso I2P: alla jail serve soltanto SAM [socksproxy] enabled = false [sam] enabled = true address = 10.0.0.3 # bridge SAM per iop port = 7656 # i2pd.conf contiene la password della console, e i2pd crea le sue chiavi leggibili da tutti # (provato: router.keys, ntcp2.keys e ssu2.keys in 644): file e cartella si chiudono a mano doas jexec i2p chown root:_i2pd /usr/local/etc/i2pd/i2pd.conf doas jexec i2p chmod 640 /usr/local/etc/i2pd/i2pd.conf doas jexec i2p chmod 700 /var/db/i2pd

Un router I2P pubblica il proprio indirizzo nella netDB: chiunque può sapere che quell'indirizzo partecipa a I2P, non che cosa vi si ospita. È il prezzo della partecipazione alla rete, e vale la pena saperlo prima di scegliere dove collocare il nodo.

iop

iop condivide tra i nodi, attraverso I2P, i file tracciati con Git, senza alcun server di repository. Su un nodo Classico serve a tenere allineati i server ridondati che stanno su reti diverse e i dispositivi che escono di casa; per quelli della propria rete resta il server di repository (vedi Forge).

La jail iop parla soltanto con il bridge SAM e non può scaricare nulla. iop si compila quindi sull'host, come utente non privilegiato e attraverso Tor, da un commit preciso; nella jail entra solo l'eseguibile, e sull'host il compilatore non resta. Il repository oggi non pubblica rilasci firmati: si fissa il commit, se ne annota l'hash e lo si confronta con quello comunicato dall'autore per un canale diverso. Si compila soltanto la riga di comando (iop-cli): l'interfaccia grafica non serve a un nodo. Provato su FreeBSD 15.1 con pf attivo: tutto passa da Tor, e la compilazione richiede pochi minuti.

# sull'host, come operatore: sorgenti e dipendenze attraverso Tor doas pkg install rust git git -c http.proxy=socks5h://10.0.0.2:59353 clone https://gitlab.com/Rekcah/iop && cd iop git checkout <commit> # un commit preciso, mai il ramo che si muove git rev-parse HEAD # l'hash da confrontare; con i rilasci firmati: git verify-tag CARGO_HTTP_PROXY=socks5h://10.0.0.2:59353 cargo build -p iop-cli --release --locked doas mkdir -p /jails/jails-data/iop-data/usr/local/bin doas install -m 755 target/release/iop /jails/jails-data/iop-data/usr/local/bin/iop doas pkg delete -y rust git && doas pkg autoremove -y # nella jail, come utente non privilegiato doas jexec iop pw useradd gestore -m -s /bin/sh # -l: ambiente di login di gestore (HOME=/home/gestore), dove iop tiene identità e configurazione doas jexec -l -U gestore iop iop init # rispondere "s": identità cifrata con passphrase doas jexec -l -U gestore iop iop profile use paranoid # dopo init: un init ripetuto riscrive config.toml doas jexec -l -U gestore iop iop config set network.i2p_sam_addr 10.0.0.3:7656 doas jexec -l -U gestore iop iop doctor # tutti i controlli critici devono passare # il demone: iop legge la passphrase dal terminale, se ne ha uno. Questo comando nella jail la chiede # una volta e lo stacca dal terminale con daemon(8), che gliela passa dalla pipe (provato) # /usr/local/bin/avvia-iop (nella jail iop) #!/bin/sh pgrep -U "$(id -u)" -f "iop daemon" > /dev/null && { echo "iop daemon è già in esecuzione"; exit 0; } stty -echo; printf 'Passphrase di iop: '; read -r P; stty echo; echo printf '%s\n' "$P" | /usr/sbin/daemon -o "$HOME/.iop/daemon.log" /usr/local/bin/iop daemon unset P; sleep 5; tail -2 "$HOME/.iop/daemon.log" doas jexec -l -U gestore iop avvia-iop # dalla console, dopo ogni riavvio

Due avvertenze emerse provando. doctor segnala "chiavi cifrate" in base all'impostazione encrypt_key_files, non al file: se a init si risponde di no, la chiave resta in chiaro e il controllo risulta comunque superato; allo stesso modo "permessi file chiave == 0600" risulta superato anche quando la chiave non esiste ancora. E il controllo dell'orologio cerca strumenti NTP che sul nodo non ci sono per scelta, quindi risulta "non verificabile": l'ora si controlla con lo script di Orologio. Con l'identità cifrata il demone chiede la passphrase a ogni avvio, quindi dopo un riavvio va lanciato a mano dalla console, ed è il prezzo di una chiave che non sta in chiaro sul disco.

La sintassi dei comandi profile e config va confrontata con la guida alla CLI della versione installata. L'uso di iop per distribuire indicatori e configurazioni tra i nodi è descritto in Integrare iop.

Livello 6: servizi

Per le basi (didattica): Applicazioni e web, Il trasporto, La sicurezza di rete.
Per i motivi (discussione): Servizi esposti, Web server, SSH, Trasferimento file: SFTP, mai FTP, Forge: il server di repository.

Un sito come onion service

Il server web sta nella jail web e non ascolta su Internet: lo raggiunge soltanto Tor, dalla propria jail. Si usa nginx senza moduli superflui (vedi Web server).

doas pkg -C /usr/local/etc/pkg.conf -r /jails/jails/web install nginx doas jexec web service ldconfig start doas jexec web sysrc nginx_enable=YES doas jexec web mkdir -p /usr/local/www/sito1 # /usr/local/etc/nginx/nginx.conf (nella jail web) worker_processes 1; error_log /var/log/nginx/error.log; events { worker_connections 1024; } http { server_tokens off; # nessuna versione nelle intestazioni include mime.types; default_type application/octet-stream; access_log off; # nessun registro dei visitatori client_body_buffer_size 1k; client_header_buffer_size 1k; large_client_header_buffers 2 1k; client_max_body_size 1k; # sito statico: nessun caricamento sendfile on; keepalive_timeout 65; add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header Referrer-Policy no-referrer; server { listen 10.0.0.10:8080; server_name _; root /usr/local/www/sito1; index index.html; location /riservata/ { # una cartella che non deve esistere per nessuno deny all; return 404; } location /protetta/ { # una cartella sotto password auth_basic "Area protetta"; auth_basic_user_file /usr/local/etc/nginx/.htpasswd; } } } # password per l'area protetta (nella jail web) printf 'utente:%s\n' "$(openssl passwd -apr1)" | doas tee /usr/local/etc/nginx/.htpasswd doas chown root:www /usr/local/etc/nginx/.htpasswd && doas chmod 640 /usr/local/etc/nginx/.htpasswd doas service nginx start

Nella jail tor si dichiara il servizio. Ogni sito ha il proprio indirizzo onion, ma sulla stessa istanza di Tor i siti condividono il guard e la disponibilità: chi li osserva a lungo vede che si accendono e si spengono insieme, e può ricondurli allo stesso nodo. Siti che non devono essere collegati tra loro stanno su istanze di Tor separate, ciascuna nella propria jail, o meglio su nodi diversi.

# /usr/local/etc/tor/torrc (nella jail tor) HiddenServiceDir /var/db/tor/sito1 HiddenServiceVersion 3 HiddenServicePort 80 10.0.0.10:8080 HiddenServicePoWDefensesEnabled 1 # un secondo sito: un'altra cartella, un altro indirizzo, un'altra porta # HiddenServiceDir /var/db/tor/sito2 # HiddenServicePort 80 10.0.0.10:8081 doas jexec tor service tor restart doas jexec tor cat /var/db/tor/sito1/hostname # l'indirizzo .onion del sito

La cartella /var/db/tor/sito1 contiene la chiave hs_ed25519_secret_key: è l'identità del sito e non ha frase di recupero. Va copiata, cifrata, fuori dal nodo. I contenuti del sito non si scaricano da un servizio in chiaro: arrivano con iop o attraverso Tor. Una piattaforma pubblica come Codeberg può fare al massimo da specchio, raggiunto attraverso Tor con una chiave dedicata, per le ragioni spiegate in Codeberg e le piattaforme ospitate da altri.

SSH

L'amministrazione remota avviene solo dalla rete locale o attraverso un onion service dedicato, diverso da quello dei siti. La configurazione è quella di SSH: sola autenticazione a chiave, niente root, elenco esplicito degli utenti.

# /etc/ssh/sshd_config (sull'host), IN CIMA al file: sshd usa la prima occorrenza di ogni opzione, # e una riga già presente più in basso (o aggiunta da altri strumenti) vincerebbe sulla nostra ListenAddress 192.168.178.3 PermitRootLogin no PasswordAuthentication no KbdInteractiveAuthentication no PubkeyAuthentication yes AllowUsers operatore MaxAuthTries 3 LoginGraceTime 20 X11Forwarding no AllowAgentForwarding no AllowTcpForwarding no # niente tunnel: dall'host si raggiungerebbero i servizi delle jail AllowStreamLocalForwarding no PermitTunnel no ClientAliveInterval 300 # sessioni abbandonate chiuse dopo dieci minuti ClientAliveCountMax 2 # le impostazioni effettive, non quelle scritte: questo è ciò che sshd applica davvero doas sshd -T | grep -E "^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin|allowtcpforwarding|allowusers) " # atteso: no, no, no, no, operatore

Provando la guida in una macchina virtuale, un'immagine cloud aveva già una riga PasswordAuthentication yes più in alto: la nostra veniva ignorata e sshd accettava le password. Su un'installazione da DVD quella riga non c'è, ma la verifica con sshd -T costa un secondo. Gli inoltri sono spenti perché le connessioni che sshd aprirebbe per conto dell'utente partono dall'host, a cui pf lascia raggiungere i servizi delle jail.

L'onion service di amministrazione è riservato: senza la chiave di autorizzazione del client, Tor non rivela nemmeno che il servizio esiste. pf lascia passare soltanto la jail tor verso la porta 22 dell'host.

# /usr/local/etc/tor/torrc (nella jail tor): un indirizzo diverso da quello dei siti HiddenServiceDir /var/db/tor/amministrazione HiddenServiceVersion 3 HiddenServicePort 22 192.168.178.3:22 # sul computer di amministrazione: una coppia di chiavi x25519 per l'autorizzazione openssl genpkey -algorithm x25519 -out onion-auth.pem priv=$(openssl pkey -in onion-auth.pem -outform DER | tail -c 32 | base32 | tr -d '=') pub=$(openssl pkey -in onion-auth.pem -pubout -outform DER | tail -c 32 | base32 | tr -d '=') # nella jail tor: la sola chiave pubblica, in una cartella che deve già esistere, poi si riavvia Tor doas jexec tor install -d -o _tor -g _tor -m 700 /var/db/tor/amministrazione/authorized_clients echo "descriptor:x25519:<valore di $pub>" | doas tee /jails/jails-data/tor-data/var/db/tor/amministrazione/authorized_clients/operatore.auth # sul computer di amministrazione: la chiave privata, nella cartella indicata da ClientOnionAuthDir echo "<indirizzo-senza-.onion>:descriptor:x25519:$priv" > onion_auth/amministrazione.auth_private ssh -o ProxyCommand='nc -X 5 -x 127.0.0.1:9050 %h %p' operatore@<indirizzo>.onion

Altri servizi

Per il trasferimento di file si usa SFTP, mai FTP (vedi Trasferimento file). Per gli avvisi un server XMPP auto-ospitato (vedi Alerting). Ogni servizio nuovo segue lo stesso schema: una jail, un utente non privilegiato, un indirizzo onion o una destinazione I2P propri, una riga in pf.conf per ciò che gli serve e niente di più.

Livello 7: monitoraggio e manutenzione

Per le basi (didattica): File system, Processi e thread.
Per i motivi (discussione): Monitoraggio e difesa, Backup, Snapshot, Verifica e integrità del software, Anti-forensics ed emergenza.

Monitoraggio e difesa

Un nodo non osservato è un nodo di cui non si sa nulla. Gli strumenti e gli script sono descritti in Monitoraggio e difesa; l'ordine in cui attivarli è questo.

  1. Integrità: una base di riferimento con mtree, firmata e conservata fuori dal nodo (vedi Verifica e integrità del software).
  2. Audit: le classi di eventi indicate nella sezione sull'EDR.
  3. Contenimento: la tabella quarantena di ipfw, il dataset della quarantena e lo script tutanum-edr (vedi In pratica su FreeBSD).
  4. Sentinella: prima in modalità di solo avviso, poi di blocco.
  5. Esca: la jail esca e gli honeytoken (vedi Honeypot).
  6. Avvisi: un canale fuori banda e un segnale di vita periodico.
  7. Rapporti di sicurezza: FreeBSD ne produce già uno ogni notte (file setuid nuovi, account con UID 0 o senza password, accessi falliti, pacchetti scartati dal firewall, vulnerabilità note). Si scrive su file, da cui lo leggono il collettore e il canale di avviso. Il controllo delle vulnerabilità scarica l'elenco con pkg, quindi passa da Tor grazie a pkg.conf: senza, resterebbe bloccato da pf (provato).
# /etc/periodic.conf daily_status_security_output="/var/log/security.log" # 1. sul nodo: la base di riferimento, da rigenerare dopo ogni aggiornamento e copiare subito fuori doas mtree -c -K sha256digest -p /usr/local > base.mtree # 2. sul computer fidato: la si firma e la si conserva; sul nodo non resta ssh-keygen -Y sign -f ~/.ssh/id_ed25519_mtree -n mtree base.mtree # -> base.mtree.sig # 3. a ogni controllo: la firma si verifica sul computer fidato, poi la base torna sul nodo ssh-keygen -Y verify -f firmatari -I operatore -n mtree -s base.mtree.sig < base.mtree doas mtree -p /usr/local < base.mtree # nessun output = nessuna differenza

Una base conservata sul nodo stesso non prova nulla: chi può modificare i file può modificare anche lei.

Backup

Vale la regola 3-2-1 nella sua versione paranoica, con cifratura prima di ogni spostamento (vedi Backup). Oltre ai dati, tre cose non devono mancare: le chiavi degli onion service, il file state di Tor e le chiavi delle destinazioni I2P.

doas zfs snapshot -r zroot@$(date +%F) # istantanea mensile di tutto doas zfs list -t snapshot # copia cifrata su un supporto esterno, poi scollegato doas zfs send -R zroot/jails@$(date +%F) | age -r <chiave-pubblica> > /mnt/esterno/jails-$(date +%F).zfs.age

Un backup non provato non esiste: almeno una volta per trimestre si ripristina una jail su un'altra macchina. Le istantanee conservano anche ciò che si cancella dopo, registri e file rimossi compresi: un dato che deve poter sparire va tolto anche dalle istantanee e dai backup che lo contengono (vedi Anti-forensics ed emergenza).

Aggiornamenti

doas pkg audit -F # ogni settimana doas zfs snapshot -r zroot@prima-di-$(date +%F) # sempre, prima di aggiornare doas mtree -p /usr/local < base.mtree # l'integrità si controlla PRIMA # il sistema di base: in modalità utente singolo, vedi "Tor" al livello 5 doas pkg upgrade -r FreeBSD-ports # i port, attraverso Tor per via di pkg.conf for j in tor i2p web iop esca; do doas pkg -C /usr/local/etc/pkg.conf -r /jails/jails/$j upgrade -r FreeBSD-ports && doas jexec $j service ldconfig start done # e la base di riferimento si rigenera subito DOPO, nella stessa operazione

L'officina e lo scambio con il nodo

Il software che entra nel nodo — aggiornamenti dei port, le proprie applicazioni — non si compila sul nodo: lo fa un'officina separata, una seconda macchina che analizza e costruisce (il perché è in L'officina). Qui interessa come le due macchine si parlano, perché è la parte che decide la fiducia. Tre principi: è sempre il nodo a iniziare (l'officina risponde e basta, non ha né rotta né credenziali per raggiungere il nodo); l'officina esce solo dal SOCKS di Tor del nodo; la chiave di firma non lascia mai il nodo. Così un'officina compromessa non tocca il nodo né i segreti.

Il canale è SSH, ma non una shell: ogni chiave è legata a un solo comando imposto (command= in authorized_keys), quindi il nodo può fare soltanto quella cosa. Due chiavi, due usi: una parla con un dispatcher che accetta un elenco chiuso di richieste (consegna l'inventario, ritira le issue, conferma), l'altra ritira il repository dei pacchetti in sola lettura con rrsync.

# sul nodo: due chiavi dedicate e la chiave host dell'officina fissata ssh-keygen -t ed25519 -N "" -f /var/db/officina/chiave-scambio ssh-keygen -t ed25519 -N "" -f /var/db/officina/chiave-repo ssh-keyscan -t ed25519 <officina> > /var/db/officina/known_hosts # sull'officina, in ~scambio/.ssh/authorized_keys: una chiave = un comando restrict,command="/usr/local/libexec/officina/scambio" ssh-ed25519 AAAA...nodo-scambio restrict,command="rrsync -ro /var/db/officina/repository" ssh-ed25519 AAAA...nodo-repo

Il dispatcher legge la richiesta da SSH_ORIGINAL_COMMAND e la confronta con l'elenco chiuso (inventario, issue-elenco, issue-conferma): tutto ciò che arriva dall'officina è dato non fidato, quindi si valida con uno schema, si tronca a una dimensione massima e non si esegue mai. Da cron, una volta all'ora, il nodo esegue il giro completo: consegna l'inventario dei pacchetti, ritira le issue dell'analisi (le aggiunge all'agenda e, le più urgenti, al notificatore del telefono), e ritira il repository compilato.

# il nodo ritira i pacchetti, li RIFIRMA con la propria chiave (mai sull'officina) e li pubblica rsync -e "ssh -i /var/db/officina/chiave-repo -o UserKnownHostsFile=/var/db/officina/known_hosts" \ scambio@<officina>:/ /var/db/repository/ricevuto/ pkg repo /var/db/repository/ricevuto/ /var/db/chiavi/repo.key # ri-firma qui, non sull'officina # la copia firmata finisce nella copia di lavoro del progetto iop, che la diffonde ai client via I2P

Per le applicazioni costruite così (per esempio un demone proprio), lo stesso giro porta l'eseguibile già compilato e analizzato: il nodo lo ritira e lo installa nella jail giusta. Se l'applicazione ha un'identità cifrata — una chiave sbloccata da una passphrase all'avvio — lo scambio dell'eseguibile e il riavvio restano un gesto manuale alla console: è una proprietà di sicurezza, non un limite. E conviene schedulare un controllo che dica non solo se il processo è vivo, ma se funziona davvero (la sua stessa diagnostica, se ce l'ha), facendo confluire un fallimento critico negli avvisi.

Sigillare i file di avvio

È l'ultimo passo, da fare quando tutto il resto funziona: qui si alza anche il securelevel, che dal riavvio congela pf. Il securelevel impedisce di togliere il flag schg, ma i file vanno marcati: tutto ciò che viene eseguito prima che il livello salga, altrimenti basta modificare uno script di avvio e riavviare (vedi File immutabili).

Il livello sale per ultimo: rcorder mette securelevel dopo tutti gli altri script, rc.local e cbsdd compresi. Tutto ciò che rc legge prima va quindi marcato, non soltanto /etc/rc.d: anche /etc/rc.local, /etc/defaults, /etc/rc.conf.d, gli script del loader e i file che riempiono le tabelle. Un intruso che potesse scrivervi un pfctl -d avrebbe il firewall spento al riavvio successivo.

CBSD avvia le jail in modo asincrono, quando il livello è già salito: da lì in poi il kernel non carica più moduli. Le jail di CBSD montano la base con nullfs, che non è nel kernel GENERIC (provato: dopo la sigillatura nessuna jail partiva, mount_nullfs: Invalid fstype), e /dev/fd con fdescfs. Il primo si dichiara nel loader; il secondo si toglie alle jail, che non ne hanno bisogno.

# /boot/loader.conf nullfs_load="YES" # fdescfs: invece di caricarlo, non si monta (per ogni jail, a jail ferma) doas cbsd jset jname=web mount_fdescfs=0 doas sysrc kern_securelevel_enable=YES kern_securelevel=2 # prima dei flag: rc.conf diventa immutabile doas chflags -R schg /boot/kernel /boot/loader /boot/loader.conf /boot/loader.conf.d /boot/lua /boot/defaults doas chflags -R schg /sbin /bin /lib /libexec /usr/bin /usr/sbin /usr/lib /usr/libexec doas chflags -R schg /etc/rc.d /usr/local/etc/rc.d /etc/defaults /etc/rc.conf.d doas chflags schg /etc/rc /etc/rc.subr /etc/rc.conf /etc/sysctl.conf /etc/fstab /etc/ttys \ /etc/pf.conf /etc/ipfw.rules /etc/login.conf /usr/local/etc/doas.conf \ /etc/rc.local /etc/crontab /etc/rctl.conf /etc/ipfw.mai_bloccare doas reboot # dopo il riavvio: le jail devono essere partite da sole jls name securelevel # -> tutte, e tutte a 3

Resta fuori un rischio che la sigillatura non copre: i programmi in /usr/local lanciati da rc prima che il livello salga (sshguard, dnscrypt-proxy, CBSD). Marcarli fermerebbe gli aggiornamenti dei port; li sorveglia invece la base di riferimento di mtree, controllata prima di ogni riavvio.

Da questo momento un aggiornamento del sistema di base richiede un passaggio in più: riavviare in modalità utente singolo, togliere i flag con chflags -R noschg sugli stessi percorsi, aggiornare, rimetterli e riavviare. La modalità utente singolo chiede la password di root: è il motivo per cui root non si blocca. Gli aggiornamenti dei soli pacchetti e delle jail non ne sono toccati, salvo gli script in /usr/local/etc/rc.d.

Verifica finale

Il nodo è pronto quando ogni riga dà il risultato atteso.

Che cosaComandoRisultato atteso
IPv6 spentoifconfig | grep inet6Nessuna riga. Sulle loopback il kernel assegna comunque ::1 e fe80::1: si tolgono da /etc/rc.local (ifconfig lo0 inet6 ::1 -alias, lo stesso per i due fe80::1) con ifconfig_lo0_ipv6="inet6 -auto_linklocal ifdisabled" e l'equivalente per lo1 in rc.conf (provato)
MAC casualeifconfig em0 | grep etherDiverso a ogni riavvio
Securelevelsysctl kern.securelevel2 o superiore
File di avvio immutabilils -lo /sbin/init /etc/rc.conf, poi doas chflags noschg /etc/rc.confFlag schg presente, e il secondo comando rifiutato
Jail al livello 3, senza bpfdoas jexec web sysctl kern.securelevel, doas jexec web ls /dev3, e nessun bpf, mem, kmem
W^X e ASLRsysctl kern.elf64.allow_wx kern.elf64.aslr.enable0 e 1
Messaggio di accessoAprire una sessione sull'host e in una jailSolo il testo scelto: nessuna versione, nessun nome
Bando automaticoQuattro accessi SSH falliti da un altro computer della rete locale, poi doas ipfw table 22 listL'indirizzo in tabella; il computer di amministrazione e la jail tor mai
Utente singolo protettoRiavviare e scegliere la modalità utente singoloRichiesta della password di root
Regole di doasdoas -C /usr/local/etc/doas.confNessun output
sshd come lo applica davverodoas sshd -T | grep -E "^(passwordauthentication|allowtcpforwarding|permitrootlogin) "no per tutte e tre
Segreti di i2pddoas jexec i2p ls -ld /var/db/i2pd /usr/local/etc/i2pd/i2pd.confdrwx------ e -rw-r-----
Nessuna fuga in uscitaSul router, per dieci minuti di uso normale: tcpdump -ni <porta del nodo> "ip6 or port 53 or port 123 or icmp"Nessun pacchetto (provato: escono soltanto il traffico di Tor, di I2P e di DNSCrypt)
Nessun servizio sull'hostsockstat -4lSolo sshd sulla rete locale e dnscrypt-proxy su 127.0.0.1 e 10.0.0.1
pf congelatodoas pfctl -dRifiutato
L'host non esce in chiarodoas pfctl -t manutenzione -T show, poi doas fetch -o /dev/null https://www.freebsd.org senza proxyNessun indirizzo, e il collegamento fallisce
Porte viste dalla reteDa un altro computer: nmap -Pn -p- 192.168.178.3La 22, solo dalla rete locale, e la porta di I2P (i2p_port), che pf inoltra alla jail i2p (provato: nient'altro su 65535)
Niente timestampsysctl net.inet.tcp.rfc1323 net.inet.icmp.tstamprepl0 e 0
DNS cifratodrill freebsd.org @127.0.0.1Risposta, e nessuna richiesta in chiaro sulla porta 53 verso il router
Torcurl --socks5-hostname 10.0.0.2:59353 https://check.torproject.org/api/ip"IsTor":true
Isolamento tra le jaildoas jexec web nc -zv -w 3 10.0.0.3 7656Timeout, e una riga in pflog
Orologiodoas /usr/local/sbin/ora-da-torScarto di pochi secondi
Le jail dei servizi non esconodoas jexec web nc -zv -w 3 1.1.1.1 443Timeout, e una riga in pflog
Sito onionDa Tor Browser, l'indirizzo in hostnameLa pagina, senza intestazioni di versione
Pacchettidoas pkg audit -FNessuna vulnerabilità nota
Integritàdoas mtree -p /usr/local < base.mtreeNessun output

Sviluppi previsti

Parti del progetto non ancora coperte da questa guida:

  • Virtualizzazione: bhyve per i carichi che non possono stare in una jail, con switch virtuali al posto dei bridge. Proxmox VE non è previsto per il Classico: le ragioni sono in Proxmox VE, e il suo uso è descritto solo in Tutanum Lieve.
  • Cluster: più single board computer in parallelo per aumentare la capacità di calcolo.
  • Variante GNU/Linux: Gentoo o Alpine con musl, doas, OpenZFS e nftables, per chi non può usare un sistema BSD.
  • Infrastruttura fisica: refrigerazione e gruppo di continuità autocostruiti.
  • Servizi: XMPP, condivisione temporanea di file, videochiamate.