Layer-7 Traffic Switching

OSI Model

Aggiungere alla propria rete un proxy che possa servire da web cache e, per chi lo desiderasse, filtrare qualche tipo di traffico, non è una operazione banale e presenta spesso qualche difficoltà.

Sebbene al giorno d’oggi i service provider non siano più così interessati a risparmiare banda mettendo in cache i contenuti web, nelle reti aziendali risparmiare sul traffico HTTP potrebbe essere economicamente conveniente. Senza considerare le molte leggi che i vari stati europei stanno promulgando, con lo scopo di impedire ai minori accesso a materiale vietato.

Purtroppo quando si tratta di inserire un proxy nel flusso del traffico si può incorrere in problemi di design anche abbastanza seri. Vediamo in questo articolo quale potrebbero essere eventuali soluzioni.


Transparent proxy

La rete è piena di tutorial a riguardo, per cui non ci soffermeremo a parlarne estensivamente. Vediamo solamente perché un transparent proxy non è la soluzione ideale.

Il termine transparent, riferito ai dispositivi di rete, significa essenzialmente che l’apparecchio in questione non introduce nessun hop a layer-3 dello stack ISO/OSI, il livello per intenderci dove opera il protocollo IP. Eseguendo cioè un traceroute su una rete che presenta un dispositivo transparent, non riceveremo nessun pacchetto di ritorno per TTL expired passandovi attraverso.

Tale risultato si raggiunge facendo operare il transparent proxy a livello 2 dello stack, ossia – nella maggioranza dei casi – il livello Ethernet. Per chi mastica un po’ di networking, questa è essenzialmente la differenza tra uno switch ed un router.

A titolo di esempio, vediamo come configurare OpenBSD in transparent mode. Dalle FAQ leggiamo:

$ cat /etc/hostname.fxp0
dhcp NONE NONE NONE
...
$ cat /etc/hostname.ep0
up media 10base2
...
$ cat /etc/bridgename.bridge0
add fxp0
add ep0
up

Una volta impostato il BRIDGE, possiamo decidere quale applicazione usare. Le FAQ proseguono spiegando come configurare un Transparent Firewall con PF:


pass in on ep0 all
pass out on ep0 all
pass in on fxp0 all
pass out on fxp0 all

# Pass all traffic through ep0
pass in quick on ep0 all
pass out quick on ep0 ll

# Block fxp0 traffic
block in on fxp0 all
block out on fxp0 all

pass in quick on fxp0 proto tcp from any to any port {22, 80} \
flags S/SA keep state

Il problema sorge nel momento in cui, per operare a livello transparent (cioè a layer-2 come abbiamo già detto), il proxy deve accettare in ingresso e processare tutto il flusso di traffico che attraversa la rete. Se per una rete aziendale questo potrebbe non essere un grosso problema, pensiamo ad un service provider di medie dimensioni (come, per inciso, la società per cui lavoro).

In uno scenario nemmeno troppo pessimista potremmo arrivare ad avere un 2% di utenti collegati contemporaneamente. Naturalmente il vero design su cui sto lavorando prevede una percentuale decisamente maggiore, ma questi dati serviranno allo scopo dell’articolo.

Su un totale di circa un milione di abbonati, avremo quindi 20.000 utenti nel sistema in un determinato istante. Pensando di garantire un flusso di circa 64kbps (l’equivalente delle vecchie linee ISDN) ad ogni utente, necessiteremo (per farla semplice) di

20.000 utenti * 64 kbps = 1280000 kbps = 1,28 Gbps

di banda disponibile.

In uno scenario scarso come quello che abbiamo descritto, dovremmo arrivare a garantire almeno 1,28 Gbps di banda sul nostro transparent proxy, ed una capacità di elaborazione che permetta di sostenere tale volume di traffico. Scalando i requirement verso l’alto, capiamo bene come un transparent proxy sia totalmente inadeguato.

Layer-7 switching

La soluzione è quella di non usare il server proxy in transparent mode ed effettuare lo switching del traffico a layer-7 in modo da inviargli esclusivamente il traffico web. Per questa operazione ci serviremo di un prodotto che personalmente considero tra i migliori oggi esistenti sul mercato, il modulo per il content switching ACE (Application Control Engine) prodotto da Cisco.

Ecco un diagramma su come apparirà la nostra rete, con i flussi del traffico evidenziati.

Diagramma rete aziendale

I lettori più attenti si saranno forse domandati come mai vogliamo agire a layer-7 (application layer) e non a livello 4, basandoci sulla porta di destinazione. Dopotutto il traffico HTTP usa la porta 80/tcp come destinazione e potremmo voler inviare solo questo tipo di flussi al server proxy.

Certo questa è una soluzione, ed è personalmente quella che mi sentirei di suggerire. Utilizzeremmo ancora un content switch per questa operazione e quindi la topologia della rete rimarrebbe intatta.

Purtroppo durante la riunione con il management ho commesso un grossolano errore, lasciandomi sfuggire la possibilità che qualche utente in gamba avrebbe potuto aggirare il proxy (che noi usiamo anche per filtrare i contenuti pornografici) semplicemente dirigendo il traffico web su un’altra porta, bypassando così il controllo. Terrorizzati da questa possibilità, dai piani alti mi è arrivata la richiesta di progettare una soluzione che andasse ad ispezionare il traffico su qualunque porta, redirigendo al proxy il traffico HTTP.

Naturalmente questo approccio è inutile, dal momento che ci sono protocolli (come HTTPS) che non potendo essere ispezionati vengono lasciati passare senza filtraggio. La prossima volta imparo a stare zitto.

Vediamo come configurare il modulo ACE per effettuare lo switching del traffico. Anzitutto definiamo le class-map a layer-4 per catalogare il tipo di traffico, usando una route di default (0.0.0.0/0) cone Virtual IP:

class-map match-all TCP-VIP
2 match virtual-address 0.0.0.0 0.0.0.0 tcp any
class-map match-all UDP-VIP
2 match virtual-address 0.0.0.0 0.0.0.0 udp any

Il traffico UDP verrà inviato direttamente su Internet, mentre il traffico TCP dovrà essere ispezionato. Definiamo quindi un’altra class-map, questa volta a layer-7, per il traffico HTTP:

class-map type http loadbalance match-any L7-HTTP
2 match http url .*

Scriviamo infine due ulteriori class-map per gestire il traffico ICMP di controllo e per il servizio di NAT sull’interfaccia di uscita:

class-map type management match-all ICMP-ALLOW
2 match protocol icmp any
class-map match-any NAT_CLASS
2 match access-list ISP-NAT

Il NAT non è strettamente necessario se il proxy può agire in modalità non transparent e richiedere le pagine non in cache usando il proprio indirizzo IP. Nella nostra configurazione di prova tuttavia il proxy non aveva questa possibilità ed ho dovuto effettuare il NAT del traffico per gestire le route di ritorno.

Il passo successivo consiste nel definire i server Proxy ed inserirli nella serverfarm:

probe icmp Proxy

rserver host Proxy01
ip address 10.128.5.97
probe Proxy
inservice
rserver host Proxy02
ip address 10.128.5.98
probe Proxy
inservice

serverfarm host ProxyCluster
rserver Proxy01
inservice
rserver Proxy02
inservice

Essendo questa una configurazione di test, abbiamo abilitato esclusivamente il probe basato su pacchetti ICMP. Poiché stiamo usando il content switch con una serverfarm di due server (Proxy01 e Proxy02 in questo esempio), aggiungeremo anche il servizio di Load-Balancing per il traffico diretto ai Proxy e, soprattutto, il servizio di Failover: nel caso un server dovesse non rispondere a tre successivi ping, verrebbe dichiarato come non più disponibile ed il traffico indirizzato solo al server attivo.

Definiamo anche una serverfarm virtuale che punta al router di uscita verso Internet:

rserver host Internet
ip address 10.128.6.206
inservice

serverfarm host Internet
transparent
rserver Internet
inservice

Naturalmente nessun probe è configurato per la serverfarm Internet, non essendo un servizio vero e proprio.

Dobbiamo ora scrivere le policy-map associate al tipo di traffico da gestire. Ad esempio, i pacchetti ICMP verranno autorizzati a passare in modo incondizionato:

policy-map type management first-match ICMP
class ICMP-ALLOW
permit

Il traffico TCP invece verrà ispezionato da una policy-map di tipo load-balance che userà la classe di traffico Layer-7 definita in precendenza:

policy-map type loadbalance first-match TRAFFIC-REDIRECTION
class L7-HTTP
serverfarm ProxyCluster
class class-default
serverfarm Internet

La classe class-default è una classe di traffico predefinita (non ha bisogno cioè di dichiarazione) e serve per effettuare il match di tutto il traffico non precedentemente processato. La regola associata a questo traffico di default è l’invio incondizionato verso Internet.

La policy-map associata al NAT:

policy-map multi-match NAT_POLICY
class NAT_CLASS
nat dynamic 1 vlan 901

ed ecco la policy-map associata all’interfaccia di ingresso, dove il nostro traffico verrà ispezionato e diretto alle interfacce di uscita:

policy-map multi-match INGRESS
class TCP-VIP
loadbalance vip inservice
loadbalance policy TRAFFIC-REDIRECTION
loadbalance vip icmp-reply
loadbalance vip advertise

Notiamo che il traffico UDP non viene gestito esplicitamente (nemmeno dalla class-default) poiché il VIP ad esso associato non è stato incluso nella policy-map attraverso la class-map precedentemente definita.

Ecco infine la configurazione delle interfacce di rete:

interface vlan 873
ip address 10.128.5.110 255.255.255.240
no normalization
no icmp-guard
access-group input ALL
access-group output ALL
service-policy input ICMP
no shutdown
interface vlan 901
ip address 10.128.6.201 255.255.255.248
no normalization
access-group input ALL
access-group output ALL
nat-pool 1 10.128.6.202 10.128.6.202 netmask 255.255.255.255 pat
service-policy input ICMP
no shutdown
interface vlan 902
ip address 192.168.1.14 255.255.255.240
no normalization
no icmp-guard
access-group input ALL
access-group output ALL
service-policy input ICMP
service-policy input NAT_POLICY
service-policy input INGRESS
no shutdown

La tabella di routing contiene due rotte statiche:

ip route 0.0.0.0 0.0.0.0 10.128.6.206
ip route 10.0.0.0 255.0.0.0 192.168.1.1

La prima per la route di default e la seconda per indirizzare il traffico interno.

Esclusione di porte

La configurazione appena discussa presenta dei problemi quando si debbano utilizzare dei protocolli che dopo il 3-way handshake si aspettino che sia il server ad inviare il successivo (quando, generalmente, dovrebbe essere il client). Ciò accade in generale per tutti i protocolli che richiedono autenticazione, dovendo il server in questo caso inviare il pacchetto di prompt username/password.

Un tipico esempio di protocollo non funzionante è FTP. In questo caso l’unica soluzione è quella di escludere la porta dalla lista dei protocolli ispezionati. Scriviamo quindi una nuova class-map:

class-map match-any TCP-VIP-2
2 match virtual-address 0.0.0.0 0.0.0.0 tcp range 20 21

e facciamo in modo che il traffico indirizzato alle porte 20/tcp e 21/tcp sia inviato direttamente su internet:

policy-map type loadbalance first-match SECOND
class class-default
serverfarm Internet

La policy-map relativa al traffico di ingresso dovrà allora essere riscritta in questo modo:

policy-map multi-match INGRESS
class TCP-VIP-2
loadbalance vip inservice
loadbalance policy SECOND
loadbalance vip icmp-reply
loadbalance vip advertise
class TCP-VIP
loadbalance vip inservice
loadbalance policy TRAFFIC-REDIRECTION
loadbalance vip icmp-reply
loadbalance vip advertise

La clausola multi-match ci permette di specificare più di una classe di traffico: in questo caso un pacchetto inviato alla porta 20 o 21 avrà un match sulla prima classe, mentre ogni altro pacchetto verrà processato dalla seconda.

Conclusioni

In questo articolo abbiamo potuto osservare un esempio di traffic switching (o content switching come talvolta viene chiamato) applicato ad un problema reale ed abbastanza comune.

Il vantaggio rispetto ad una soluzione basata su transparent proxy diventa poi evidente quando si guarda alle statistiche del traffico ed alle performance dell’intero sistema. Senza contare poi la possibilità di scalare ulteriormente verso l’alto semplicemente aggiungendo altri proxy al cluster ed aumentando le dimensioni della serverfarm nella configurazione dell’ACE.

Per chi volesse approfondire ulteriormente consiglio la lettura della documentazione ufficiale Cisco sul modulo ACE.

Comments

  1. Walter Franzini says:

    Articolo interessante anche se un po’ troppo orientato ad un prodotto che non conosco. In realta non ne conosco nessuno essendo abbastanza ignorante in materia :-)

    Ho qualche domanda:
    1) perche` hai totalmente abbandonato il layer 4? Il traffico HTTP normale e` comunque sulla porta 80 e in base a questa sola informazione lo puoi dirottare verso il proxy. Immagino possa fare differenza a livello di performance.

    2) Non ho capito perche` devi escludere le porte dell’FTP visto che il traffico corrispondente non dovrebbe essere classificato come HTTP e quindi prendere la strada verso internet direttamente.

    3) Non ho capito perche` il protocollo FTP e` problematico.

  2. Ciao Walter,
    grazie per il commento, ti rispondo punto per punto.
    1) Il punto fondamentale del problema era proprio quello di dover ispezionare il traffico a livello 7. Se redirigi solo la porta 80 il proxy si aggira facilmente inviando il traffico HTTP su un’altra porta.
    2) Come conseguenza del punto 1 dobbiamo ispezionare il traffico che passa su tutte le porte, indipendentemente da quale esse siano e se siano associate ad un altro protocollo.
    3) Il motore dell’ACE, per poter ispezionare il protocollo a layer-7, si finge il server nei confronti del client, terminando il 3-way handshake usando l’IP del server. Il client si convince di stare parlando col server reale, mentre invece è il content switch a fare da proxy per le richieste. In questo modo quando arriva il pacchetto successivo lo ispeziona e ne decide la destinazione (content switching). Purtroppo alcuni protocolli (come FTP) dopo il 3-way handshake aspettano una risposta dal server, che in questo caso non arriva, poiché in realtà è l’ACE che sta fingendo di esserlo.
    Magari potremmo pensare ad un futuro articolo sul funzionamento interno dei content switch. Potrebbe interessare?

  3. Alan Franzoni says:

    La mia impressione, come tu stesso ammetti, è che il filtraggio del traffico a livello 7 sia decisamente poco utile: un utente esperto avrà mille e un modi di effettuare il tunneling del traffico attraverso un altro protocollo (potrebbe semplicemente avviare una connessione VPN verso un server esterno, o usare un tool di DNS tunneling, tanto per fare un paio di esempi banali) che non verrebbe intercettato in nessun caso dall’ACE.

    Quindi i casi sono due:
    a) si suppone che gli utenti interni non siano malintenzionati, e si vuole usare il proxying solo per difenderli da potenziali siti malevoli o per fare il caching; in tale situazione il reindirizzamento della porta 80 sarebbe più che sufficiente.
    b) si suppone che gli utenti interni siano malintenzionati. In tale situazione ci vorrebbe una scelta più drastica, come (ad esempio) impedire in toto le connessioni verso l’esterno se non attraverso un proxy autenticato, limitando di fatto quindi le porte esterne accessibili all’utente (a meno di non usare un tool di http tunneling che si appoggi ad un server esterno, of course).

    In ogni caso, mi pare che la soluzione di switching a livello 7 che proponi introduca un’inutile complessità nella rete, senza produrre grandi vantaggi. Senza offesa naturalmente: so bene che delle volte è necessario prodursi in grandi porcate per soddisfare il management.

    Bye!

Policy per i commenti: Apprezzo moltissimo i vostri commenti, critiche incluse. Per evitare spam e troll, e far rimanere il discorso civile, i commenti sono moderati e prontamente approvati poco dopo il loro invio.