Connessione ad Oracle attraverso un Firewall

In questo documento vengono trattate alcune problematiche che emergono quando si accede ad un RDBMS Oracle attraverso una rete protetta da Firewall. Tra le altre cose vedremo anche un paio dei problemi piu' tipici come il Port Redirection (sintomo: non funziona piu' nulla) ed il Firewall Blackout (sintomo: le connessioni cadono per timeout). Ma anche come mettere il trace SQL*Net, utilizzare il listener come un firewall, controllare gli accessi degli utenti con un trigger, ...

L'RDBMS Oracle e' stato il primo database relazionale a consentire un accesso remoto, via rete, alla base dati. Dall'introduzione di tale possibilita' di accesso Client/Server al DB sono molte le evoluzioni introdotte: sono supportati innumerevoli protocolli di comunicazione, e' stato implementato il 2PC che consente la creazione di Database distribuiti, e' possibile definire la copia di tabelle remote (snapshot) ed utilizzarle come tabelle locali, ...
Naturalmente per fornire tali funzionalita' e' cambiata l'architettura (ed il nome) del software di connessione: SQL*Net, SQL*Net v.2, Net8, ... Ma il meccanismo di base e' rimasto lo stesso: il listener attende le richieste di servizio da parte dei client e, quando necessario, attiva i necessari processi per rispondere alle richieste.

Nel caso del protocollo TCP-IP, il listener e' in attesa su un socket che, con le versioni piu' recenti di SQL*Net, di default corrisponde alla porta 1521.
La configurazione di un Firewall per consentire tale dialogo e' molto semplice. Tuttavia vi sono alcuni casi in cui la configurazione piu' semplice non funziona ed e' necessario adottare configurazioni diverse un poco piu' complesse.

Firewall

I Firewall sono apparecchiature HW/SW che proteggono i sistemi da accessi non desiderati attraverso la rete.

La tipica configurazione di un Firewall per Oracle richiede la definizione di una sola semplice regola che consenta l'accesso dai client verso il DB server sulla porta su cui ascolta il listener (eg. 1521).

Funziona nella maggior parte dei casi... ma non sempre!
Infatti il protocollo sqlnet prevede il port redirection. Dopo il dialogo iniziale sulla porta definita dal TNSNAMES.ORA, il listener puo' indicare al client un nuovo numero di porta su cui avverranno i sucessivi scambi di dati. In questo caso la semplice regola di configurazione del Firewall indicata in precedenza non e' sufficiente poiche' la comunicazione sucessiva viene, giustamente, bloccata dal Firewall.
Per risolvere il problema vi sono diverse tecniche e possibilita'... continuate a leggere!

Port Redirection

Il port redirection fa utilizzare, per il dialogo dei Client che accedono ad Oracle, una porta socket differente da quella definita e libera quindi la porta originale. Nei casi delle configurazioni piu' comuni non avviene alcun port redirection. Vi sono tuttavia casi significativi in cui il port redirection viene attivato. In particolare:

In questi casi la semplice apertura di una porta sul Firewall non basta, la connessione ad Oracle non avviene e vengono riportati gli errori ORA-12203 o ORA-12535.

Configurazioni alternative per il port redirection

Vi sono diverse possibilita' per risolvere il problema. Naturalmente dipendono dalle configurazioni ed esigenze presenti. Iniziamo da quelle piu' banali:

Fino ad ora abbiamo scherzato (o quasi ;-). Passiamo ora a qualche consiglio piu' serio:

Firewall Blackout

I firewall piu' recenti (statefull firewall) mantengono una cache delle connessioni TCP presenti. Se una connessione non viene utilizzata per troppo tempo il Firewall effettua un blackout e non accetta piu' comunicazioni provenienti dal "destinatario" spesso con il risultato di far cadere la connessione con un'indicazione di timeout.
Tale problema e' relativamente recente poiche' qualche tempo fa pochi Firewall erano statefull... tuttavia sono noti alcuni tipi di attacchi che sfruttano tale possibilita' e quindi praticamente tutti i dispositivi piu' recenti presentano questa problematica.

La soluzione piu' elegante dal punto di vista Oracle, che opera sulle sole connessioni SQL*Net, e' quella di impostare la Dead Connection Detection, ovvero impostare l'SQL*Net in modo che controlli l'effettiva presenza del client. Tale impostazione deve avere un intervallo di tempo piu' breve dell'impostazione di blackout del firewall e si definisce con il parametro SQLNET.EXPIRE_TIME nel file sqlnet.ora (il parametro e' in minuti).
Non e' necessario riavviare il Database o il Listener: l'impostazione viene utilizzata immediatamente dalle nuove connessioni.

Un risultato analogo si ottiene anche agendo a livello di sistema operativo (ma con un impatto su tutte le sessioni TCP e non solo quelle di Oracle). In pratica si modifica il parametro di keepalive del TCP-IP definito a livello di sistema operativo. Naturalmente ogni OS/versione ha modalita' ed impostazioni differenti. Su Linux i parametri del kernel da controllare/modificare sono: tcp_keepalive_time, tcp_keepalive_intvl e tcp_keepalive_probes. Per avere un probe ogni 10 minuti su Linux dare il comando immediato
  echo 600> /proc/sys/net/ipv4/tcp_keepalive_time
oppure, meglio, configurare nel file /etc/sysctl.conf i valori seguenti e farli rileggere con sysctl -p:

net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 60
net.ipv4.tcp_keepalive_probes = 20

Poiche' agisce a livello di sistema operativo questa tecnica puo' essere utilizzata con tutti i Database che, a differenza di Oracle, non hanno parametri di keepalive (eg. MySQL, PostgreSQL, ...).

Non c'e' bisogno di uno stateful firewall per lasciare centinaia di connessioni di database appese: ci riescono benissimo anche le applicazioni che non chiudono le sessioni! A parte l'ovvia indicazione di correggere le applicazioni... e' possibile utilizzare la stessa tecnica per risolvere il problema.

Trace

Come fare ad essere sicuri che il problema di connessione ad Oracle sia veramente un problema di Firewall? Naturalmente gli strumenti sono molti (eg. Log sul Firewall, trace con un datascope, snoop, ...) ma e' possibile utilizzare anche il trace dell'SQL*Net sul client configurando nel file sqlnet.ora i seguenti parametri:

trace_level_client = 16
trace_file_client = client
trace_directory_client = c:\temp

Nel log si vedera' il dump di tutti i messaggi scambiati e l'eventuale indicazione del Port Redirection.
Risolto il problema non dimenticate di togliere il trace. E' molto dettagliato e consuma in fretta un intero disco!

Oracle listener come firewall

Anche il listener di Oracle ha alcune possibilita' di configurazione con funzionalita' simili a quelle di un firewall (o di un wrapper). La configurazione ed i parametri sono molto semplici:

TCP.VALIDNODE_CHECKING=yes
TCP.EXCLUDED_NODES=(10.11.12.13,10.11.12.14)

TCP.INVITED_NODES=(10.11.12.69)

Le due modalita' (EXCLUDED/INVITED) sono tipicamente utilizzate in alternativa...

Trigger

E' possibile controllare l'accesso degli utenti anche direttamente da Oracle sfruttando un trigger di logon...

CREATE OR REPLACE TRIGGER global_logon_trg AFTER logon ON DATABASE
DECLARE
   p_session_user  varchar2(64);
   p_module        varchar2(64);
   p_term          varchar2(64);
BEGIN
   SELECT UPPER(SYS_CONTEXT('USERENV', 'SESSION_USER')) INTO p_session_user FROM DUAL;
   SELECT UPPER(SYS_CONTEXT('USERENV', 'MODULE')) INTO p_module FROM DUAL;
   SELECT UPPER(SYS_CONTEXT('USERENV', 'TERMINAL')) INTO p_term FROM DUAL;

   DBMS_SESSION.SET_IDENTIFIER(p_session_user || '-' || p_module || '-' || p_term);

   IF ((p_session_user = 'SCOTT') AND (p_module IN ('MYAPP.EXE'))) THEN
      DBMS_SESSION.SET_IDENTIFIER('about to raise app_error..');
      RAISE_APPLICATION_ERROR(-20069,'You are not allowed to connect to the database');
   END IF;
END;
/

Riferimenti

Sull'argomento si trovano parecchie informazioni su web.
Sulla configurazione ed utilizzo di SQL*Net vi sono alcuni bellissimi documenti in italiano:

Li ho definiti bellissimi poiche' li ho scritti io, in realta'... insomma giudicate voi!

Su Metalink vi sono diversi documenti sull'argomento: Note:125021.1 Oracle Connectivity with Firewalls, Note:44693.1 MTS_DISPATCHERS parameters, Bug:217746, Note:2077721.6 Connection Manager, Note:1016349.102 init.ora Configuration.
Questi sono i riferimenti definitivi per un DBA Oracle.


Testo: Connessione ad Oracle attraverso un Firewall
Data: 14 Febbraio 2003
Versione: 1.0.6 - 17 Marzo 2003
Autore: mail@meo.bogliolo.name