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.
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!
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.
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:
Molto meglio un sistema Unix!
Il database Oracle funziona lo stesso. E spesso e' meglio non utilizzare
l'MTS che e' indicato solo in configurazioni particolari
con numero molto elevato di utenti interattivi.
Ma chi ve lo fa fare di usare l'SSL!
Alcuni Firewall contengono regole specifiche per la
connessione ad Oracle che tengono conto del port redirection.
Basta saperlo, eventualmente effettuare un'upgrade del software
del firewall, ed utilizzarle!
Altri Firewall effettuano un esame a livello di protocollo...
in qualche caso i pacchetti SQL*Net possono non
essere riconosciuti correttamente
(eg. utilizzando Oracle Data Guard): se questo avviene le relative opzioni vanno disabilitate.
Alcuni Firewall richiedono la programmazione delle regole con
un linguaggio specifico.
Ad esempio puo' essere programmata una regola che consente l'apertura
di una porta socket libera da parte di un client solo
dopo che e' avvenuta una connessione sulla porta 1521...
Non e' una cosa cosi' strana, l'ftp funziona in modo simile.
Con Net8 e' possibile utilizzare il Connection Manager.
Tale software e' un proxy di chiamate per l'Oracle listener.
Quindi il client attivera' una connessione con il Connection
Manager e questi, a sua volta, attivera' un connessione verso
il listener del Database Server. Naturalmente il client deve
essere in versione 8 o sucessiva. Nel seguito e' riportato
un esempio di configurazione del TNSNAMES.ORA del client:
fwcman =
(description =
(address_list =
(address =
(protocol=tcp)
(host=cman_server)
(port=1610)
)
(address=
(protocol=tcp)
(host=db_server)
(port=1521)
)
)
(connect_data = (sid = sid_name))
(source_route = yes)
)
Il primo indirizzo e' quello del Connection Manager il secondo e'
quello del Database Server ed il parametro source_route indica che
deve essere effettuato il routing delle connessioni ad Oracle.
E' possibile modificare il comportamento dello stack TCP sul
server NT inserendo nel Registry
in LOCAL_MACHINE\Software\Oracle\OracleHome
il parametro:
USE_SHARED_SOCKET = TRUE
In questo caso le sessioni condividono la porta socket.
Controllate pero' la versione di NT, deve essere installato
almeno il Service Pack 3 (il Port Redirection per default
era stato introdotto per aggirare un baco del SO).
Nel caso di utilizzo dell'MTS i dispatcher assegnano
una porta di connessione ai processi client.
Dalla versione 7.3.3 e' stata introdotta una nuova sintassi
che consente la definizione di alcuni parametri tra cui la
porta utilizzata dai processi di dispacting. Una configurazione
di esempio del file INIT.ORA e' quella che segue:
MTS_DISPATCHERS="(ADDRESS=(PARTIAL=TRUE)(PROTOCOL=TCP)\
(HOST=192.168.1.101)(PORT=4000))(DISPATCHERS=1)"
MTS_DISPATCHERS="(ADDRESS=(PARTIAL=TRUE)(PROTOCOL=TCP)\
(HOST=192.168.1.101)(PORT=4001))(DISPATCHERS=1)"
E' naturalmente necessario aprire anche tali porte
sul Firewall.
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.
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:
Nel log si vedera' il dump di tutti i messaggi scambiati
e l'eventuale indicazione del Port Redirection.
trace_level_client = 16
trace_file_client = client
trace_directory_client = c:\temp
Risolto il problema non dimenticate di togliere il trace.
E' molto dettagliato e consuma in fretta un intero disco!
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:
Le due modalita' (EXCLUDED/INVITED) sono tipicamente utilizzate in alternativa...
TCP.VALIDNODE_CHECKING=yes
TCP.EXCLUDED_NODES=(10.11.12.13,10.11.12.14)
TCP.INVITED_NODES=(10.11.12.69)
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;
/
Sull'argomento si trovano parecchie informazioni su web.
Li ho definiti bellissimi poiche' li ho scritti io,
in realta'... insomma giudicate voi!
Sulla configurazione ed utilizzo di SQL*Net vi sono alcuni
bellissimi documenti in italiano:
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.