mercoledì 7 marzo 2018

Configurare Wireguard

Per la serie: "perché non ci hanno pensato prima?"

Con le versioni più recenti del kernel Linux si può configurare un'interfaccia virtuale che crittografa point-to-point tutto il traffico in entrata e uscita incanalandolo in pacchetti UDP da far transitare su un'interfaccia reale.

Questa feature si chiama Wireguard, è open source scritto da cinesi stufi di essere spiati (e milioni di occhi già stanno scrutando da anni a caccia di vulnerabilità: lavoro facile, l'implementazione è appena quattromila righe di codice), è stata scritta per essere veloce e leggera ("consuma" solo un 5-8% in più di banda a parità di traffico) e interna al kernel. Presto manderà in pensione le darknet e le Vuppi Enne (VPN), notoriamente complicate da configurare, lente, e talvolta dipendenti da un servizio esterno non sempre onesto.

L'unico requisito di due sistemi connessi attraverso Wireguard è che nell'attivare le rispettive interfacce virtuali usino la stessa password (più esattamente, coppie di chiavi crittografiche). Funzionalmente simili a un tunnel TLS/SSL, anche se virtuali, in qualità di interfacce di rete sono utilizzabili con tutte le facility del kernel: routing, firewalling, traffic shaping, forwarding, etc. Per ora nelle maggiori distribuzioni il modulo Wireguard è fornito attraverso il DKMS ma verrà presto integrato nel kernel.

Esempio: ho due board Beagleboard xM sulla rete locale (rispettivamente 192.168.1.121 e 192.168.1.122) che parlano tra di loro usando servizi "in chiaro" (tipo http e ftp). Creo una rete Wireguard tra le due board inventando indirizzi 192.168.6.xyz sui quali far transitare quei servizi (che così non saranno più "sniffabili" dal resto della rete locale 192.168.1.xyz), mostrando che il traffico non vi transita in chiaro. Sulle board c'è Arch Linux ARM con systemd per cui su ognuna di loro ho fatto come segue.
Esatto, nel seguente esempio due board saranno connesse tra loro sullo stesso cavo fisico sia attraverso la rete locale 192.168.1.xyz, sia attraverso la nuova rete Wireguard 192.168.6.xyz. Dato che systemd è ormai ovunque (temuto solo da coloro che credono che le cose vecchie sono sempre migliori di quelle nuove, tipo quelli che leggono i libri al lume di candela), non vale la pena seguire i vecchi esempi che costringono a una battaglia frenetica coi vari comandi ip, ifconfig, route, ecc.
Non è necessario che le due board siano sulla stessa rete locale o che siano strettamente IPv4, è sufficiente che siano raggiungibili a vicenda - ad avere un indirizzo IP statico si possono fare cose simpatiche...

Prerequisiti: systemd versione 237, modulo wireguard e relativi tools.

Esempio di installazione pacchetti con ArchLinux: sudo pacman -S wireguard-dkms wireguard-tools

Generazione di una chiave crittografica casuale: wg genkey
Tale comando restituisce una "chiave privata" (da usare nella configurazione locale).
Esempio: OLu2pvKIOVYeJErLacH0iqYdMeOZwbv6SCcNL9AlMHE=
In ambito Wireguard, la "chiave privata" contiene anche la "chiave pubblica": quest'ultima, da usare nella configurazione del sistema remoto, va estratta dandola in pasto al tool wg:
echo OLu2pvKIOVYeJErLacH0iqYdMeOZwbv6SCcNL9AlMHE= | wg pubkey

Anche la "chiave pubblica" ha una codifica base64, qui per esempio ci ha restituito:
XmQUASiyksa2O//CY3NM0/p9UbT8+jWwlyyQptQaeU8=
A questo punto creo il file /etc/systemd/network/wg0.netdev per indicare al systemd che l'interfaccia wg0 vale per gli indirizzi 192.168.6.xyz e deve appoggiarsi all'indirizzo preesistente 192.168.1.xxx dell'altra board sulla porta UDP 51820 (la porta tipicamente utilizzata per Wireguard, ma va grosso modo va bene qualsiasi porta tra 1024 e 60000), e ci aggiungo la "chiave pubblica" dell'altra board:
[NetDev]
Name=wg0
Kind=wireguard
Description=interfaccia protetta

[WireGuard]
PrivateKey=OLu2pvKIOVYeJErLacH0iqYdMeOZwbv6SCcNL9AlMHE=
ListenPort=51820

[WireGuardPeer]
PublicKey=HIVoNJ3CdpjGFg2eDGTbkH/JewfmiB6wThIzqwiq9yc=
AllowedIPs=192.168.6.0/24
Endpoint=192.168.1.121:51820
Il file di configurazione va protetto per evitare che qualcuno dei tools consideri compromessa la chiave privata: chown root.systemd-network /etc/systemd/network/wg0.netdev ; chmod 640 /etc/systemd/network/wg0.netdev

Dopodiché creo il file /etc/systemd/network/wg0.network per descrivere al systemd la configurazione di rete - ci basterà solo l'indirizzo (abbiamo deciso 192.168.6.1 per la prima board e 192.168.6.2 per la seconda) e la classe (24 bit, cioè "192.168.6.xyz"):
[Match]
Name=wg0

[Network]
Address=192.168.6.1/24
Dal prossimo reboot l'interfaccia verrà attivata (che funzionerà solo se anche sull'altra board sono stati fatti i passaggi).

Dopo il reboot, per verificare che i moduli kernel sono stati caricati: lsmod | grep wireguard

Per verificare che l'interfaccia wg0 è stata configurata, è attiva ed ha il suo indirizzo di rete: networkctl; ip link; ifconfig; wg

Per verificare che le interfacce comunichino è sufficiente il ping attraverso il loro Wireguard; secondo l'esempio di cui sopra, dalla board configurata con 192.168.6.1 eseguire ping 192.168.6.2

Per verificare che il traffico transita crittografato al punto di sembrare munnezza si può eseguire sulla board che riceve i ping un tcpdump della sua interfaccia di rete reale (per esempio, come detto sopra, eth0) riguardo ai pacchetti remoti UDP. Il tcpdump può anche mostrare il contenuto dei pacchetti in esadecimale: tcpdump -x -i eth0 host 192.168.1.122 and udp port 51820
(e dall'altra parte effettuare il ping attraverso il Wireguard)
La seconda board manda i ping alla prima attraverso la Wireguard (192.168.6.xyz). Quei ping vengono criptati e fatti transitare sull'interfaccia di rete eth0 (per cui "sul cavo" risulteranno originati dall'host 192.168.1.122 come da esempio sopra citato, e perciò sulla board 121 occorre ascoltare i pacchetti ufficialmente provenienti dalla 122, ignorando quelli di altri protocolli e indirizzi). Indipendentemente dal protocollo, verranno trasmessi in UDP (non sto qui a spiegare i motivi per cui i progettisti di Wireguard hanno preferito la trasmissione stateless via UDP).

Il contenuto dei ping dopo l'header è riconoscibilmente criptato. Si può fare una prova più convincente con altri protocolli che trasmettono in chiaro.

Sulla prima board lanciamo un webserver e un tcpdump:
tcpdump -A -i eth0 host 192.168.1.122 &
ruby -run -ehttpd /tmp -p8000 &

Dalla seconda board lanciamo una normale richiesta:
curl 192.168.1.121:8000

Dal tcpdump vediamo in chiaro le stringhe HTTP (GET, headers, contenuto) nei pacchetti TCP.

Dalla seconda board lanciamo la stessa richiesta ma attraverso il Wireguard:
curl 192.168.6.1:8000

Dallo stesso tcpdump vediamo che non c'è nulla di leggibile, che è tutto transitato via UDP, e che sono cambiate anche le dimensioni dei pacchetti.

Se per analizzare il traffico si usa il Wireshark sulla eth0 si vedranno dei bizzarri pacchetti "DCERPC" con numeretti indecifrabili:



A titolo di curiosità (non lo consiglio in ambienti di lavoro), aggiungiamo una terza board che avrà come endpoint la prima e come chiavi le stesse della seconda.
Il suo wg0.netdev sarà uguale a quello della seconda (endpoint e chiavi), mentre il suo wg0.network avrà un altro indirizzo (diciamo 192.168.6.3). Ovviamente la seconda e la terza, avendo scambiato le chiavi solo con la prima, potranno comunicare solo con la prima (la prima avrà un singolo peer consistente in... due board diverse).
Si lascia come esercizio al lettore la configurazione di una rete wifi senza password in cui gli unici client che riescono a navigare sono quelli connessi via wireguard al nodo che fa forwarding, mentre i furbacchioni tutti contenti di sniffare il traffico si ritrovano nel Wireshark una immane catasta di munnezza googlando a tutta forza DCERPC decriptare DCERPC sproteggere DCERPC.

Nessun commento:

Posta un commento