# Esempio di /etc/pf.conf per un nodo Tutanum su FreeBSD (filtro dei livelli 3 e 4). # E' il file a cui rimanda Tutanum Classico: nomi e indirizzi sono quelli della guida. # # Impianto: # - default-deny in ingresso e in uscita, anche tra le jail e tra le jail e l'host; # - l'host esce soltanto con dnscrypt-proxy: aggiornamenti e git passano dalla jail tor; # - verso Internet escono soltanto le jail tor e i2p. Dopo il NAT l'indirizzo di origine # e' quello di em0, quindi le due jail si riconoscono dall'etichetta messa dal NAT; # - le jail (non VNET) hanno gli indirizzi su lo1. Il kernel instrada il traffico verso # gli indirizzi di un'interfaccia di loopback attraverso quell'interfaccia stessa: tra # le jail, e tra host e jail su 10.0.0.x, si passa da lo1; verso l'indirizzo dell'host # sulla LAN (che sta su em0) si passa da lo0. Nessuna delle due si salta: "set skip" # annullerebbe l'isolamento tra un servizio e l'altro. # # L'ordine delle sezioni e' obbligatorio: macro, tabelle, opzioni, normalizzazione, # traduzione, filtro. Con le regole "quick" vince la prima che corrisponde: i blocchi # delle tabelle stanno quindi prima di ogni "pass". # Prima di caricarlo: doas pfctl -nf /etc/pf.conf (nessun output = nessun errore). ### Macro ext_if = "em0" jail_if = "lo1" # interfaccia degli indirizzi delle jail lan = "192.168.178.0/24" nodo = "192.168.178.3" # indirizzo statico dell'host admin = "192.168.178.10" # il computer di amministrazione host_jail = "10.0.0.1" # l'host visto dalle jail (lo1): dnscrypt-proxy j_tor = "10.0.0.2" j_i2p = "10.0.0.3" j_web = "10.0.0.10" j_iop = "10.0.0.11" j_esca = "10.0.0.66" tor_socks = "59353" # SOCKSPort della jail tor i2p_port = "24567" # porta fissa di i2pd (port = in i2pd.conf): sceglierne una propria ### Tabelle # Le negazioni si scrivono sempre con una tabella: "! { a, b }" diventerebbe "!a OR !b". table const { $j_i2p, $j_web, $j_iop, $j_esca } # jail che non usano il SOCKS di Tor table const { $j_tor, $j_web, $j_iop, $j_esca } # jail che non vedono la console di i2pd # Dal securelevel 2 FreeBSD 15 congela pf: niente regole nuove, niente indirizzi aggiunti alle # tabelle (si possono solo togliere), niente chiusura delle connessioni, niente "pfctl -d". # pf e' quindi la politica fissa, caricata all'avvio; cio' che cambia a sistema avviato # (sshguard, quarantena dell'EDR, esche) vive nelle tabelle di ipfw (vedi ipfw.rules). table persist # riempita dal kernel con la regola di overload di SSH table persist # vuota; si riempie solo prima che il securelevel salga ### Opzioni set block-policy drop # nessuna risposta: il nodo non conferma la propria esistenza set loginterface $ext_if ### Normalizzazione scrub in on $ext_if all fragment reassemble no-df max-mss 1440 scrub out on $ext_if all random-id no-df ### Traduzione # Solo tor e i2p escono, e il NAT le marca per il filtro in uscita. 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) # Porta di I2P inoltrata alla jail (va inoltrata anche dal router; senza, i2pd funziona # lo stesso, ma come router "firewalled" e con prestazioni peggiori). rdr on $ext_if inet proto { tcp, udp } from any to ($ext_if) port $i2p_port -> $j_i2p port $i2p_port # Esca (honeypot): solo da fuori della LAN, che sulla porta 22 trova l'sshd vero. rdr on $ext_if inet proto tcp from ! $lan to ($ext_if) port 22 -> $j_esca port 2222 # cowrie rdr on $ext_if inet proto tcp from ! $lan to ($ext_if) port 2222 -> $j_esca port 2223 # endlessh ### Filtro block log all block drop quick inet6 all antispoof quick for $ext_if inet # Banditi: prima di qualunque pass. Le voci si fanno scadere con # pfctl -t bruteforce -T expire 86400 (togliere indirizzi resta permesso) block drop in quick on $ext_if from # Impronte di scansione: combinazioni di flag TCP illecite (anti OS fingerprint). # I flag validi in pf sono F S R P A U E W: la maschera di tutti i flag e' WEUAPRSF. block drop in log quick on $ext_if proto tcp flags FUP/WEUAPRSF block drop in log quick on $ext_if proto tcp flags WEUAPRSF/WEUAPRSF block drop in log quick on $ext_if proto tcp flags SRAFU/WEUAPRSF block drop in log quick on $ext_if proto tcp flags /WEUAPRSF block drop in log quick on $ext_if proto tcp flags SR/SR block drop in log quick on $ext_if proto tcp flags SF/SF # --- Host --- # Loopback dell'host (dnscrypt-proxy, strumenti locali) pass quick on lo0 from 127.0.0.1 to 127.0.0.1 # SSH: il computer di amministrazione non passa dall'overload, che altrimenti puo' chiudere # fuori proprio lui (bastano quattro connessioni in quindici secondi); il resto della LAN si' pass in quick on $ext_if inet proto tcp from $admin to $nodo port ssh flags S/SA keep state pass in quick on $ext_if inet proto tcp from $lan to $nodo port ssh flags S/SA keep state \ (max-src-conn 10, max-src-conn-rate 3/15, overload flush global) # L'host esce soltanto con dnscrypt-proxy (DNSCrypt usa UDP e TCP, su porte diverse). # 978 e' l'UID fisso di _dnscrypt-proxy nei port: con il numero il file si carica anche # prima che il pacchetto (e quindi l'utente) esista. # Gli aggiornamenti non hanno una regola propria: se Tor e' fermo, falliscono invece di # uscire in chiaro. pass out quick on $ext_if inet proto tcp from $nodo to any user 978 modulate state pass out quick on $ext_if inet proto udp from $nodo to any user 978 keep state # Manutenzione: la tabella e' vuota, quindi le due regole non lasciano passare nulla. Per il # primo aggiornamento (prima che il securelevel salga) e per freebsd-update (in modalita' # utente singolo, dove il securelevel non e' ancora salito): # pfctl -t manutenzione -T add 0.0.0.0/0 e, finito, pfctl -t manutenzione -T flush pass out quick on $ext_if inet proto tcp from $nodo to port { http, https } modulate state pass out quick on $ext_if inet proto { tcp, udp } from $nodo to port domain keep state # --- Jail --- # Ogni jail parla con se stessa (in una jail non VNET 127.0.0.1 e' l'indirizzo della jail) # Il traffico verso 10.0.0.x corre su lo1, quello verso l'indirizzo LAN dell'host su lo0. pass quick on $jail_if from $j_tor to $j_tor pass quick on $jail_if from $j_i2p to $j_i2p pass quick on $jail_if from $j_web to $j_web pass quick on $jail_if from $j_iop to $j_iop pass quick on $jail_if from $j_esca to $j_esca # L'esca non apre mai connessioni: risponde soltanto, grazie allo stato delle regole in ingresso block drop log quick from $j_esca to any # Le sole eccezioni della tabella delle jail pass quick on $jail_if proto tcp from $j_tor to $j_web port 8080 keep state # onion service -> nginx pass quick on lo0 proto tcp from $j_tor to $nodo port ssh keep state # onion service di amministrazione -> sshd pass quick on $jail_if proto tcp from $j_iop to $j_i2p port 7656 keep state # iop -> SAM di i2pd pass quick on $jail_if proto { tcp, udp } from $j_i2p to $host_jail port domain keep state # reseed di i2pd # Ogni jail può raggiungere l'esca: una jail compromessa che la tocca si tradisce (vedi Honeypot) pass quick on $jail_if proto tcp from { $j_tor, $j_i2p, $j_web, $j_iop } to $j_esca port { 2222, 2223 } keep state # L'host usa il SOCKS di Tor e la console di i2pd; le jail dei servizi no pass quick on $jail_if proto tcp from ! to $j_tor port $tor_socks keep state pass quick on $jail_if proto tcp from ! to $j_i2p port 7070 keep state # --- Verso Internet: solo tor (TCP) e i2p (TCP e UDP) --- pass out quick on $ext_if proto tcp tagged TOR modulate state pass out quick on $ext_if proto tcp tagged I2P modulate state pass out quick on $ext_if proto udp tagged I2P keep state pass in quick on $ext_if proto { tcp, udp } to $j_i2p port $i2p_port keep state pass in quick on $ext_if inet proto tcp to $j_esca port { 2222, 2223 } keep state (max-src-conn 4) # Tutto il resto (web, iop ed esca verso Internet compresi) cade nel "block log all" ed # e' visibile in pflog: doas tcpdump -n -e -ttt -i pflog0