OsGate.org Logo

Firewall failover: CARP e pfsync - carp openbsd failover firewall

Networks Networks

Date 30.04.2010

Visits 4306

"Nella molteplicità dei sistemi di ridondanza di cui disponiamo attualmente spicca sicuramente l'accoppiata OpenBSD CARP per garantire l'alta affidabilità di un sistema moderno con applisectionivi open source. Grazie al protocollo CARP possiamo gestire sistemi di più server in maniera ridondante in modo da evitare spiacevoli inconvenienti o semplificare per esempio le procedure di patching dei server."

Introduzione

Nella molteplicità dei sistemi di ridondanza di cui disponiamo attualmente spicca sicuramente l'accoppiata OpenBSD CARP per garantire l'alta affidabilità di un sistema moderno con applicativi open source. Grazie al protocollo CARP possiamo gestire sistemi di più server in maniera ridondante in modo da evitare spiacevoli inconvenienti o semplificare per esempio le procedure di patching dei server.

Nel nostro caso vedremo come implementare un firewall ridondante, ma in ogni caso è possibile scegliere qualsiasi servizio si voglia.

Materiale necessario

Ecco di cosa abbiamo bisogno per fare funzionare il nostro cluster:

Hardware

  • 2 PC, anche non troppo potenti
  • 3 NIC per ogni PC
  • 1 cavo cross over

Software

  • L'ultima versione di OpenBSD
  • pf, carp, pfsync, tutti presenti in un installazione di default

CARP

CARP (Common Address Redundancy Protocol) è il protocollo che provvede a garantire la ridondanza attraverso un gruppo di host sulla rete ai livelli 2 e 3. È stato creato come un'alternativa valida e gratuita ai protocolli VRRP e HSRP sviluppati da Cisco. Supporta IPv4 e IPv6 e include nel suo funzionamento meccanismi di autenticazione SHA1 HMAC. Per via di vicissitudini con la IANA (organo responsabile dell'assegnazione degli indirizzi IP), CARP non possiede un internet protocol number. Per ovviare a questo i creatori di CARP si sono auto assegnati un internet protocol, ovvero il 112(http://www.iana.org/assignments/protocol-numbers/), numero detenuto tra l'altro da VRRP. CARP è esente da brevetti e licenze.

È anche disponibile un implementazione del protocollo su piattaforma linux a questo indirizzo, http://www.ucarp.org/project/ucarp.

Concetti e Funzionamento

Il suo funzionamento è dettato dai cosiddetti "gruppi di ridondanza", ovvero una serie di host che condividono un IP e un MAC address virtuali. All'interno di questi gruppi di ridondanza si devono eleggere un solo host MASTER e più host BACKUP, che in caso di down di quello MASTER ed in base alla configurazione ne prenderanno le veci.

L'host definito MASTER detiene l'IP e il MAC virtuale ed è lui che all'interno del cluster è responsabile di servire le richieste in arrivo. In caso di malfunzionamenti dell'host MASTER, quest'ultimo verrà rimpiazzato dal server BACKUP garantendo il continuo funzionamento del servizio.

La comunicazione all'interno del gruppo di ridondanza, avviene attraverso dei messaggi multicast mandati dal MASTER all'indirizzo 224.0.0.18 usando il protocollo IP 112, in modo che gli host registrati (o in ascolto) nel gruppo multicast vengano a conoscenza di chi è l'host MASTER. Chi invia messaggi più frequentemente (default 1 secondo, configurabile) ottiene lo stato MASTER.

Per evitare che le comunicazioni multicast vengano intercettate, sono cifrate attraverso l¿algoritmo SHA1 HMAC e inoltre gli host di un gruppo di ridondanza sono a conoscenza di una password che serve per verificare che l¿host appartenga effettivamente al gruppo.

pfsync

Pfsync è il secondo elemento chiave di un firewall ridondante con OpenBSD. Grazie al protocollo pfsync è possibile sincronizzare la tabella di stato delle connessioni dei firewall, cosi da mantenere attive le connessioni quando avviene un cambiamento di ruolo tra i firewall.

Per esempio, viene iniziata una connessione dall'interno della rete verso internet passando tramite il firewall MASTER, appena quest'ultimo ha un problema il firewall BACKUP ne assume l'identità in maniera trasparente e grazie a pfsync possiamo sincronizzare anche la tabella di stato del vecchio MASTER così da non perdere la connessione che avevamo iniziato precedentemente. La sincronizzazione avviene in maniera istantanea, infatti nessuno si accorgerà di ciò che è accaduto.

Per utilizzare al meglio pfsync è consigliato vivamente l'utilizzo di una rete dedicata, siccome attualmente non si dispone di un meccanismo di autenticazione legato al protocollo. Un esempio di rete dedicata è rappresentato dall'utilizzo di un cavo cross over collegato ad una terza scheda di rete che funga da ponte tra i due firewall.

Schema di rete

				            fw1			
EXT ----MASTER ---- INT |------Host
| | | |
WWW-----GW------------ | -------------GW--- |--Host
/ | | PRIV | |
/ EXT ----BACKUP ---- INT |----Host
/ fw2
VIP EXT VIP INT

Nella configurazione di un firewall ridondante, necessitiamo di due host, uno che detenga lo stato MASTER e l'altro BACKUP. Si verranno a creare due reti, la rete esterna (EXT) e quella interna(INT). Tutti i nostri host andranno posizionati nella rete interna. In questo tipo di configurazione si può prendere in considerazione l'idea di implementare una DMZ protetta anche essa dai firewall, cosa che noi non faremo. Il collegamento tra i due firewall è dato dal cavo cross over che ci permette di sincronizzare la tabella di stato delle connessioni tramite pfsync. I nostri due firewall oltre al normale filtraggio dei pacchetti fungeranno anche da NAT per gli host della rete interna.

Ricapitolando:

  • WWW
    • Internet
  • GW
    • Il nostro gateway DNS, 172.27.1.1.
  • EXT
    • La rete esterna, con indirizzo 172.27.1.0/24.
  • VIP EXT
    • L'indirizzo IP virtuale che si affaccia sulla rete esterna, 172.27.1.2.
  • PRIV
    • La rete privata con il cavo cross over, con indirizzo 192.168.1.0/24, le interfacce di sincronizzazione avranno l'IP 192.168.1.1 per fw1, 192.168.1.2 per fw2
  • INT
    • La rete interna, 10.0.0.0/24.
  • VIP INT
    • L'indirizzo IP virtuale della rete interna, 10.0.0.1, è anche il gateway per gli host della rete INT
  • fw1
    • MASTER, IP sulla rete EXT 172.27.1.3 mentre sulla rete interna 10.0.0.2
  • fw2
    • BACKUP, IP sulla rete EXT 172.27.1.4, rete interna 10.0.0.3

Configurazioni di base

Abilitare il forwarding

Per fare funzionare il firewall dobbiamo abilitare il packet forwarding. Questo serve per inoltrare i pacchetti da una rete all'altra trasformando in poche parole i firewall in dei router. Editiamo il file /etc/sysctl.conf e de commentiamo questa linea:

#net.inet.ip.forwarding=1 # 1=Permit forwarding (routing) of IPv4 packets 

Abilitare pf

Per abilitare pf editiamo /etc/rc.conf come segue:

pf=YES  # Packet filter / NAT 

Riavviamo le macchine per rendere effettivi i cambiamenti.

Configurazioni di rete

IP fissi

Per configurare le schede di rete dobbiamo prima venire a conoscenza del loro nome tramite il comando ifconfig:

#ifconfig       
fxp0: flags=8843 mtu 1500
lladdr 08:00:27:04:66:61
priority: 0
groups: egress
media: Ethernet none
status: active
inet6 fe80::a00:27ff:fe04:6661%pcn0 prefixlen 64 scopeid 0x1
inet 192.168.1.33 netmask 0xffffff00 broadcast 192.168.1.255
bge0: flags=8802 mtu 1500
lladdr 08:00:27:d7:20:82
priority: 0
media: Ethernet none
status: active
em0: flags=8802 mtu 1500
lladdr 08:00:27:69:a2:2a
priority: 0
media: Ethernet none
status: active
enc0: flags=0 mtu 1536
priority: 0
pflog0: flags=141 mtu 33204
priority: 0
groups: pflog

A noi interessano le tre interfacce fxp0, bge0, em0. Il nome di un interfaccia è dato dai driver che utilizza. Per impostare un IP fisso dobbiamo creare in /etc un file per ciascuna NIC con all'interno l'IP.

Ecco un riassunto delle interfacce:

  fw1 fw2
INT bge0 bge0
EXT fxp0 fxp0
SYN em0 em0

fw1

Fxp0 è l'interfaccia che da sulla rete esterna:

vi /etc/hostname.fxp0
inet 172.27.1.3 255.255.255.0 NONE

Abbiamo inserito un indirizzo IPv4(inet), con subnet 255.255.255.0 e nessun broadcast definito(in questo caso sarà di default 172.27.1.255.

bge0 è l'interfaccia interna:

vi /etc/hostname.bge0
inet 10.0.0.2 255.255.255.0 NONE

em0 serve per pfsync, per sincronizzare la tabella di stato:

vi /etc/hostname.em0
inet 192.168.1.1 255.255.255.0 NONE

fw2

Fxp0, esterna:

vi /etc/hostname.fxp0
inet 172.27.1.4 255.255.255.0 NONE

bge0, interna:

vi /etc/hostname.bge0
inet 10.0.0.3 255.255.255.0 NONE

em0, pfsync:

vi /etc/hostname.em0
inet 192.168.1.2 255.255.255.0 NONE

Applicare le modifiche

Dopodiché riavviamo la rete su entrambi i firewall per rendere effettive le modifiche:

sh /etc/netstart

Gateway e DNS

Impostiamo il gateway su entrambi i firewall andando a creare in /etc il file mygate:

vi /etc/mygate
172.27.1.1

Impostiamo anche il dns:

vi /etc/resolv.conf
172.27.1.1

Nota: il DNS per gli host della rete interna è l'indirizzo 172.27.1.1

CARP

Dopo avere impostato i parametri di rete basilari è arrivato il momento di configurare le interfacce virtuali, carp0 per la rete esterna e carp1 per quella interna.

Per creare un interfaccia CARP basta creare il file specifico in /etc:

vi /etc/hostname.carp0
inet 172.27.1.2 255.255.255.0 172.27.1.255 vhid 1 advskew 100 pass extpass

Nel file vengono specificati dapprima i parametri normali come gli indirizzi IP, poi le varie opzioni:

  • vhid value: specifica il gruppo di ridondanza a cui l'interfaccia deve appartenere. È importante che num sia uguale su tutte le interfacce di host diversi che si vuole fare appartenere a carp0.
    Quindi sull'altro host nel file di carp0 il valore deve essere obbligatoriamente 1. Sono accettati valori da 1 a 255.
  • advskew value: indica il delay di tempo che andrà a agguingersi ad advbase (default 1 secondo) per l'invio degli avvertimenti CARP. Tramite questo valore si decide chi è e sarà sempre MASTER, infatti chi invia messaggi più frequentemente diventa MASTER. La formula per l'invio dei messaggi è questa: 1sec val/255. Sull'altro host configureremo un advskew più alto cosi che fw1(da noi designato sempre MASTER) ridiventi sempre MASTER dopo essere tornato attivo al momento di un malfunzionameto. Se avremo agito diversamente, per esempio configurando un adwskew uguale per entrambi i firewall, fw1 sarebbe rimasto con il ruolo di BACKUP fino a quando su fw2 si sarebbe verificato un problema. In caso non venga specificato il valore advskew, il primo server che sarà up diventerà MASTER, siccome il valore di invio dei messaggi è lo stesso.
  • pass password: la password con cui criptare i messaggi CARP inviati all'interno del gruppo di ridondanza. Ogni host all'interno del gruppo(vhid) deve avere la stessa password.

Ora carp1:

vi /etc/hostname.carp1
inet 10.0.0.1 255.255.255.0 10.0.0.255 vhid 2 advskew 100 pass intpass

Come potete vedere il gruppo di ridondanza (vhid) è differente da carp0 cosi come la password.

I file /etc/hostname.carp0 e /etc/hostname.carp1 vanno creati su entrambi i server, con l'unica differenza che per il server che deve restare sempre di BACKUP, il valore advskew dovrà essere maggiore, per esempio 110.

Ora basta riavviare la rete con sh /etc/netstart e controllare che tutto funzioni:

Sull'host che deve essere sempre MASTER:

#ifconfig carp0
carp0: flags=41 mtu 1500
carp: MASTER vhid 1 advbase 1 advskew 100
inet 172.27.1.2 netmask 0xffffff00

Su quello BACKUP:

ifconfig carp0
carp0: flags=41 mtu 1500
carp: BACKUP vhid 1 advbase 1 advskew 110
inet 172.27.1.2 netmask 0xffffff00

Esistono anche dei parametri specifici per controllare CARP via sysctl:

  • net.inet.carp.preempt: abilita un host all'interno del gruppo di ridondanza con un migliore advbase e advskew a diventatre MASTER. Abilita anche il failover generale su tutte le interfacce CARP di un server qualora solo una di esse(fisica o virtuale) dovesse cadere così da permettere ad un altro host di diventare MASTER. Il failover viene eseguito impostando advskew a 240. È disabilitato di default.
  • net.inet.carp.log: abilita il log di pacchetti CARP. Di default è disabilitato.
  • net.inet.carp.allow: accetta i pacchetti CARP in entrata. Di default è yes, quindi abilitato.

pfsync

Pfsync ci permette di sincronizzare la tabella di stato tra i firewall in modo da continuare le connessioni aperte precedentemente su un server che per esempio non funziona più. È necessario creare il file relativo all'interfaccia di sincronizzazione:

vi /etc/hostname.pfsync0
up syncdev em0

Si verrà così a creare un interfaccia chiamata pfsync0.

Abbiamo così abilitato pfsync sull'interfaccia fisica em0.

Regole

Dopo avere configurato tutto ciò che riguarda la rete e le interfacce di sincronizzazione dobbiamo concentrarci sulle regole di filtraggio attraverso /etc/pf.conf.

È importante sapere che nel ruleset vanno usate le interfacce fisiche come riferimento per effettuare il filtering. Il traffico di rete passa comunque su un interfaccia fisica e non su una virtuale quindi per evitare inconvenienti è meglio utilizzare le interfacce fisiche.

Eseguiremo una configurazione d'esempio per un firewall casalingo.

Prima di iniziare definiamo le linee guida del ruleset:

  • seguiremo un approccio "default deny", ovvero tutto a parte quello che non è esplicitamente definito è bloccato.
  • Gli host della rete LAN devono poter navigare in internet
  • Dovremo fornire il NAT per i client della LAN
  • Raccogliere dati e statistiche riguardanti i pacchetti sull'interfaccia esterna
  • Il traffico FTP deve essere filtrato a dovere
  • Lasceremo passare il traffico SSH per il controllo remoto del firewall
  • Lasciare passare il traffico SMPT e POP3 verso il nostro mail server nella LAN interna
  • Permettere il giusto funzionamento di pfsync e carp

Macro

Definiamo le macro:

ext_if="fxp0"
int_if="bge0"
sync_if="em"

mail_ports="{ 110, 25 }"
mail_server="10.0.0.10"

Semplicemente dichiariamo le tre interfacce del server seguite da una list contenente le porte relative ai servizi mail e l'indirizzo IP del mail server interno.

Options

Tramite le options impostiamo dei parametri di pf, come per esempio l'abilitazione del logging.

Ecco le options nel nostro firewall:

set loginterface $ext_if
set skip on lo

Abbiamo abilitato il logging sull'interfaccia esterna (è possibile abilitarlo solo su un interfaccia alla volta) e disabilitato il filtering su "lo" (è consigliabile disabilitare il filtering sull'interfaccia di loopback).

Nota: è possibile raccogliere statistiche sui pacchetti ed il traffico eseguendo dal terminale "pfctl -s info".

Scrub

Tramite lo scrubbing normalizziamo i pacchetti cosi da evitare incomprensioni dovute a modifiche dell'header dei pacchetti.

scrub in all

Ci permette di eseguire lo scrubbing su tutto il traffico in entrata.

NAT

Nota: la regola NAT utilizzata è valida solo sulle versioni di OpenBSD precedenti la 4.7.

Grazie all'attivazione del NAT possiamo permettere ai client della rete interna di navigare verso l'esterno, quindi la sua attivazione nel nostro caso diventa fondamentale.

nat on $ext_if from !($ext_if) to any -> ($ext_if) 

Cosi permettiamo il nat del traffico proveniente dalla rete esterna verso internet su $ext_if. Nella configurazione del NAT impostiamo anche una regola di natting per l'FTP:

nat-anchor "ftp-proxy/*" 

Siccome sul protocollo FTP è difficile sia fare filtering che eseguire del NAT, all'interno di pf è stato implementato un proxy FTP che è in grado di gestire al meglio FTP.

Per maggiori informazioni consultate questa pagina sul sito ufficiale di openbsd: http://www.openbsd.org/faq/pf/ftp.html.

Redirection

Una redirection serve per reindirizzare del traffico su un altro host e come abbiamo visto prima nei punti da rispettare per la creazione del nostro ruleset è presente nella LAN un mail server. Questo vuol dire che il traffico proveniente da internet verso la nostra rete va incanalato verso il mail server. Inoltre le altre redirection servono per permettere di utilizzare il protocollo FTP:

rdr-anchor "ftp-proxy/*"

rdr pass on $int_if proto tcp to port ftp -> 127.0.0.1 port 8021
rdr on $ext_if proto tcp from any to any port $mail_ports -> $mail_server

Le prime due regole rdr servono per fare funzionare il protocollo FTP. La terza regola invece reindirizza il traffico mail (porte 25 e 110 specificate nella list in cima) proveniente da internet verso il mail server della rete interna.

Filtering rules

Queste sono le regole che determinano cosa passa e cosa no. Tramite esse definiamo il tipo di traffico che può entrare e viceversa. Verrà utilizzato un approccio "default deny", verrà negato l'accesso a tutto il traffico permettendo in un secondo momento l'accesso solo del traffico desiderato.

block in

pass out

pass quick on { $sync_if } proto pfsync
pass on { $int_if $ext_if } proto carp

anchor "ftp-proxy/*"

pass in on $ext_if inet proto tcp from any to $mail_server port $mail_ports

pass in on $int_if

All'inizio neghiamo tutto il traffico cosi da essere sicuri che non entri niente di indesiderato.

Con pass out permettiamo al traffico in uscita di andare verso internet

Le regole 3 e 4 sono fondamentali per fare funzionare pfsync. Infatti diciamo a pf di lasciare passare il traffico di tipo pfsync sull'interfaccia di sincronizzazione e quello di tipo carp sull'interfaccia interna ed esterna.

L'ancora ftp-proxy fa parte del meccanismo di pf per fare funzionare correttamente FTP.

La penultima regola permette al traffico mail (25, 110) verso il mail server di passare attraverso il firewall.

L'ultima regola permette al traffico di passare tramite l'interfaccia interna così da poter raggiungere i client della rete interna.

Ruleset completo

Ecco pf.conf tutto in un pezzo:

#macro
ext_if="fxp0"
int_if="bge0"
sync_if="em0"

mail_ports="{ 110, 25 }"
mail_server="10.0.0.10"

#options
set loginterface $ext_if
set skip on lo

#scrub
scrub in all

#nat e redirection
nat on $ext_if from !($ext_if) to any -> ($ext_if)
rdr-anchor "ftp-proxy/*"

rdr pass on $int_if proto tcp to port ftp -> 127.0.0.1 port 8021
rdr on $ext_if proto tcp from any to any port $mail_ports -> $mail_server

#filtering rules
block in

pass out

pass quick on { $sync_if } proto pfsync
pass on { $int_if $ext_if } proto carp

anchor "ftp-proxy/*"

pass in on $ext_if inet proto tcp from any to $mail_server port $mail_ports

pass in on $int_if

Dopo aver scritto il tutto in pf.conf possiamo ricaricare il file all'interno di pf:

pfctl -f /etc/pf.conf

Fatto questo se tutto è stato configurato correttamente si avrà un cluster di firewall con funzionalità failover.

Per testare se tutto funziona basta semplicemente staccare il cavo di rete dal server MASTER e controllare che la rete mantenga la sua funzionalità.