Problème de connexion à Oracle
Deux problèmes assez courant peuvent survenir sur les connexions à une base de donnée Oracle. Ces problèmes peuvent être à l’origine de timeout sur les connexions HTTP, etc…
Ces deux problèmes touchent deux choses totalement différente, l’entropie du système et la coupure de connexion par un firewall.
Manque d’Entropie
Pourquoi l’entropie ? L’entropie est une mesure de l’incertitude d’un message par rapport à celui qui le précède.
Sur un système POSIX c’est /dev/random
qui est mesuré avec l’entropie. La JVM utilise ce device
pour la génération de nombres aléatoires avec la classe SecureRandom
qui est souvent utilisé pour des fonctions
cryptographique.
Le driver Oracle 11g (probablement dans les versions précédentes aussi) établit un lien sécurisé avec la base donnée pour protéger les échanges de credentials lors de l’initialisation d’une nouvelle connexion.
Le problème vient du fait que la lecture dans /dev/random
est bloquante tant que le système (Linux, BSD, etc.)
considère qu’il n’y a pas assez d’entropie. Heureusement en bon lecteur de la javadoc il y a une note
sur SecureRandom
pour nous aider à résoudre le problème.
Note: Depending on the implementation, the generateSeed and nextBytes methods may block as entropy is being gathered, for example, if they need to read from /dev/random on various unix-like operating systems.
La solution
Java permet de changer la source du random avec la propriété java.security.egd
(egd = entropy gathering device).
Sur les systèmes UNIX il y a deux devices:
/dev/random
Source bloquante de nombres aléatoires/dev/urandom
Source non-bloquante de nombre aléatoires
Sur la ligne de commande ça se passe comme ça :
-Djava.security.egd=file:/dev/./urandom
Ainsi si en choisissant le device /dev/urandom
on aurait résolu le problème d’entropie. Pas si vite !
Les deux sources /dev/random
et
/dev/urandom
utilisent le même mécanisme, basé sur un CSPRNG (cryptographic
pseudo-random number generator), la différence étant que /dev/urandom
peut fournir des nombres avec une entropie
moins élevée afin de ne pas être bloquant.
Source : http://www.2uo.de/myths-about-urandom/
A read from the /dev/urandom device will not block waiting for more entropy. As a result, if there is not sufficient entropy in the entropy pool, the returned values are theoretically vulnerable to a cryptographic attack on the algorithms used by the driver. Knowledge of how to do this is not available in the current unclassified literature, but it is theoretically possible that such an attack may exist. If this is a concern in your application, use /dev/random instead.
Quoiqu’il en soit ce n’est pas sensé être un soucis pour l’établissement de connexions SSH ou à la base de donnée. Pour la génération de clef SSH c’est une autre histoire.
D’après la page man
le scénario catastrophe, si un attaquant a accès à la machine, serait que celui-ci arrive
à connaitre ou à déterminer quel seraient les prochains nombres qui sortiront de /dev/urandom
, donc de SecureRandom
,
et ainsi de reconstruire les éléments cryptographiques tel que des clés privées.
Dans les faits ce scénario est très peu probable.
If you are unsure about whether you should use /dev/random or /dev/urandom, then probably you want to use the latter. As a general rule, /dev/urandom should be used for everything except long-lived GPG/SSL/SSH keys.
En revanche sur des serveurs fortement chargés et parceque cette modification touche la JVM, On peut vouloir entretenir le niveau d’entropie.
Ajouter de l’entropie
Les OS alimentent /dev/random
généralement à partir de plusieurs sources, comme :
- les sondes de température,
- l’activité du CPU,
- le trafic réseau,
De retour au problème initial, suivant les aléas de ces sources l’OS sera capable de maintenir une bonne entropie,
sinon il bloque la lecture de /dev/random
tant qu’il n’y a pas assez d’entropie (selon les euils configuré de l’OS).
En utilisant /dev/urandom
l’entropie est potentiellement moins bonne.
Pour celà l’idée est donc d’aider l’OS à entretenir l’entropie, plusieurs solutions existent, soit hardware soit software. Mais l’idée maître est que ces solutions contribuent leur entropie.
Coté logiciel, les démons suivant peuvent être mis en place
- Haveged
- Frandom
- rng-tools
HAVEGED utilise comme source les états des composants internes des processeurs modernes pour augmenter significativement l’entropie du système. À noter : il tourne dans le user space donc assez facile à installer.
Passons au deuxième soucis : les coupures de connexions.
Coupure de connexion par le Firewall
Suivant les infras, en fonction des besoins de légalité, il faut protéger les bases de données par un firewall. Ce firewall va inspecter et protéger les équipements derrière mais il fait aussi de l’assainissement de connexions TCP, c’est-à-dire qu’il va couper les connexions inactives au bout d’un certain temps.
Le pool de connexion
De manière générale un serveur d’application est configuré avec une DataSource
pour faire du connexion pooling ;
en effet la création d’une connexion est cher et augmente le temps de latence du service, le but du pool est donc
de partager les connexions une fois que celles-ci ne sont plus utilisées. En revanche quand le pool donne une
connexion il ne sait pas forcement qu’elle à été fermé par un composant tierce (un firewall).
Heureusement la plupart des pools implémente un mécanisme de vérification qui peut s’effectuer à différentes étapes
de l’utilsation de la connexion.
Pour traiter ce problème de coupure de connexion par le firewall il faudra activer les bonnes options
au niveau du pool, par exemple afin de vérifier les connexions régulièrement avec tomcat-jdbc
:
minEvictableIdleTimeMillis
: The minimum amount of time an object may sit idle in the pool before it is eligible for eviction. The default value is 60000 (60 seconds).timeBetweenEvictionRunsMillis
: The number of milliseconds to sleep between runs of the idle connection validation/cleaner thread. This value should not be set under 1 second. It dictates how often we check for idle, abandoned connections, and how often we validate idle connections. The default value is5000
(5 seconds).
Ci-dessous un exemple de déclaration de DataSource
, paramètres qui devront bien sûr être adpatés à votre instrastructure.
<Resource name="Locator"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
testOnBorrow="true" driverClassName="oracle.jdbc.OracleDriver"
validationInterval="30000"
validationQuery="SELECT 1 FROM DUAL"
...
timeBetweenEvictionRunsMillis="30000"
minEvictableIdleTimeMillis="60000"
url="jdbc:oracle:thin:@..."
...
>
</Resource>
Il y a plusieurs implémentations de pool, ceux-ci ont un fonctionnement plus ou moins abouti et donc offrent une configuration plus ou moins fine voire différente (ex: HirakiCP). Il faudra donc consulter la documentation des options pour corriger le problème.
Quoiqu’il en soit cette option n’est pas optimale car elle ne traite pas le problème de rupture de connexion à sa source.
TCP Keepalive
TCP, le protocole réseau sur lequel transite la connexion oracle, fourni un mécanisme de keepalive. Ce mécanisme va envoyer un paquet sur la connexion pour que les équipements de l’infrastructure réseau dont le fameux firewall maintiennent cette connexion ouverte.
Dans l’univers Java il faut passer la bonne option à l’ouverture de la socket
SopcketOptions.SO_KEEPALIVE
.
La plupart des drivers, frameworks exposent une API pour activer cette option. Il en est de même avec le driver
JDBC Oracle.
D’après la documentation Oracle il y a plusieurs moyens de configurer le driver JDBC pour travailler avec un firewall.
Dans le cas d’une configuration style TNSNAMES
il faudra utiliser la chaine suivante ENABLE=BROKEN
pour activer
le keepalive. Par exemple :
jdbc:oracle:thin:@(DESCRIPTION=(ENABLE=BROKEN)(ADDRESS=(PROTOCOL=tcp)(PORT=1521)(HOST=myhost))(CONNECT_DATA=(SID=orcl)))
Dans le cas d’une configuration sans TNSNAMES
il faut passer la propriété oracle.net.keepAlive
à true
programmatiquement via les properties passée à l’API JDBC
java.sql.DriverManager#getConnection(java.lang.String, java.util.Properties)
. Attention à la casse des caractères!
Les propriété non documentées du driver sont disponible sur la javadoc de oracle.jdbc.OracleConnection
Enfin, dans tout les cas il faut aussi configurer le keepalive sur l’OS Sur Linux par exemple, la documentation indique comment il faut faire our les configurer. Et pour vérifier qu’il correspondent aux valeurs souhaitées :
$ cat /proc/sys/net/ipv4/tcp_keepalive_time
2400
$ cat /proc/sys/net/ipv4/tcp_keepalive_intvl
75
$ cat /proc/sys/net/ipv4/tcp_keepalive_probes
6
Ces paramètres disent :
- la pile TCP enverra la première sonde (premier paquet de keepalive) au bout de
40min
, - attendra
75
millisecondes avant de renvoyer une sonde - dans la limite de
6
essais. - Si aucun pacquet
ACK
n’est reçu alors la connexion est reconnue par l’OS comme fermée.
Il faut régler ces paramètres en accord avec les réglages des équipements réseau qui constitue l’infrastructure.
À noter que la documention indique bien que le keepalive est un comportement à activer volontairement,
d’ou la présence de l’option SopcketOptions.SO_KEEPALIVE
dans le JDK.
Remember that keepalive support, even if configured in the kernel, is not the default behavior in Linux. Programs must request keepalive control for their sockets using the setsockopt interface.