Par défaut, les dataSources déclarées dans Tomcat affichent le mot de passe jdbc en clair dans conf/server.xml.
Dans pas mal d’entreprises, le répertoire de tomcat est accessible en lecture à de nombreuses personnes. C’est très pratique pour diagnostiquer, mais pose un problème de sécurité en exposant ce mot de passe.
Voici quelques pistes d’amélioration que j’ai investiguées, en essayant de trouver un équilibre entre les besoins des développeurs et ceux de l’exploitation (oui, ça ressemble à de la démarche devops ).
Sommaire
Les besoins des dev et des ops
Pour les développeurs, avoir un accès (en lecture seule) sur tout ce qui est installé est vraiment précieux en termes de diagnostic. Cela peut même permettre de faire un copier/coller d’une instance Tomcat de production, pour pouvoir reproduire certains problèmes difficiles.
Pour l’exploitation (ops), il ne faut pas mettre en péril la sécurité du SI (en exposant des mots de passe) pour du « confort » de diagnostic. Et il ne faut pas trop compliquer les procédures d’installation/mise à jour (même si elles sont automatisées, dans le meilleur des cas).
Limiter l’accès au fichier server.xml
C’est la solution la plus évidente, et celle préconisée par Tomcat (Cf https://wiki.apache.org/tomcat/FAQ/Password). Il s’agit simplement de restreindre les droits en lecture sur ce fichier, ou (d’une manière ou d’une autre) le rendre inaccessible au plus grand nombre.
Hélas, je trouve cette solution trop contraignante :
- Le fichier server.xml contient bien d’autres informations que ce mot de passe. Notamment les différents ports d’écoute, le dimensionnement des pools de connexion etc. On perd donc la visibilité sur tout ça, ce qui est gênant en diagnostic
- On ne peut pas facilement (en tant que développeur) copier/coller le tomcat complet (sans avoir des messages d’erreur)
- En tant qu’ops, ce paramétrage de droits fin est une contrainte, surtout s’il faut le refaire à chaque mise à jour
Mettre le mot de passe dans une variable d’environnement
C’est supporté en standard par Tomcat (cf https://tomcat.apache.org/tomcat-7.0-doc/config/index.html), et fonctionnerait bien. Il faudrait (bien sûr) que l’affectation de ces variables soit faite en-dehors des répertoires accessibles à tout le monde.
Mais surtout, dans notre cas, on affiche souvent (en diagnostic) l’ensemble de ces variables d’environnement, ce qui exposerait le mot de passe. Ces variables sont également accessibles en JMX.
Chiffrer le mot de passe
Chiffrer le mot de passe est ce que proposent spontanément la plupart des interlocuteurs. Ce n’est pas extrêmement compliqué à mettre en place. Cf http://blog.alexis-hassler.com/2014/10/tomcat-chiffrer-les-mots-de-passe.html
Oui mais la clé de chiffrement est en dur dans le code… Donc en ayant accès aux .jar, on peut la retrouver.
Alors j’ai codé une classe similaire qui externalise la clé de chiffrement dans un .properties (à placer dans un répertoire inaccessible).
J’étais tout fier, ça fonctionnait bien.
Oui, mais, dans ce cas, pourquoi s’embêter à faire du chiffrement? Quitte à mettre des choses secrètes dans un fichier non accessible, autant y mettre directement le mot de passe, non? (cela éviterait les étapes de chiffrement/déchiffrement : à l’exécution, mais surtout à l’installation pour les ops)
Externaliser le mot de passe via XML Entities
En lisant de plus près https://wiki.apache.org/tomcat/FAQ/Password, ils proposent justement d’utiliser les XML Entities pour faire ça. Exemple :
<!DOCTYPE server-xml [ <!ENTITY confjdbc SYSTEM "path/to/confjdbc.xml"> ]> <Server ... > ... <!-- Editable user database that can also be used by UserDatabaseRealm to authenticate users --> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml"/> &confjdbc; </GlobalNamingResources> ...
et avec un fichier confjdbc.xml qui contient :
<Resource name="jdbc/maDataSource" type="javax.sql.DataSource" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" username="xxx" password="yyy" driverClassName="..." url="..." ... />
Ca marche bien, mais j’aimerais pouvoir n’externaliser QUE le mot de passe. Pour que les développeurs aient toujours accès aux autres paramétrages de la datasource (taille du pool de connexion etc). Et apparemment ce n’est pas possible de cette manière.
Externaliser le mot de passe via Properties Replacement
En partant de la dernière suggestion de https://wiki.apache.org/tomcat/FAQ/Password (décidément, je n’ai rien inventé…), on peut utiliser des « Properties Replacement » dans le serveur XML (variables du type ${variable}).
Par défaut, les valeurs sont lues dans conf/catalina.properties, mais on peut modifier cela pour les faire lire dans un fichier externalisé (ce n’est apparemment valable qu’à partir de la version 7 de Tomcat)
En créant un classe implémentant PropertySource, on peut faire lire les propriétés où on veut. Ce que j’ai codé, c’est de les lire dans un fichier externalisé, dont l’emplacement est aussi spécifié dans conf/catalina.properties.
Dans conf/server.xml, on met :
<Resource name="jdbc/myDataSource" type="javax.sql.DataSource" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" auth="Container" username="xxx" password="${com.mycompany.package.myDataSource.jdbcpassword}" driverClassName="..." url="..." ... />
On crée un fichier externalproperties.properties :
com.mycompany.package.myDataSource.jdbcpassword=mon_mot_de_passe_jdbc_top_secret
Et on indique l’emplacement de ce fichier dans conf/catalina.properties :
org.apache.tomcat.util.digester.PROPERTY_SOURCE=fr.mossroy.tomcat.tools.ExternalPropertySource fr.mossroy.tomcat.tools.ExternalPropertySource.file=path/to/externalproperties.properties
Dans le répertoire lib de Tomcat, il faut placer le jar tomcat-tools-x.x.jar (à télécharger sur https://github.com/mossroy/tomcat-tools/releases). Le code source est sur GitHub : https://github.com/mossroy/tomcat-tools (petit projet Maven avec son test unitaire).
Au final, c’est cette méthode qui a été retenue, mais le choix dépend évidemment du contexte.
Hi,
I would like to use environment based parameter to load properties file.
Example:
fr.mossroy.tomcat.tools.ExternalPropertySource.file=conf/db-{org.env}.properties
set JAVA_OPTS=-Dorg.env=dev %JAVA_OPTS%
I will have app-dev.properties, app-test.properties files defined as per environment
However, fr.mossroy.tomcat.tools.ExternalPropertySource is getting invoked even before the JAVA_OPTS is set. Hence, file not found is thrown.
Any pointers please?
I did not try to do that.
JAVA_OPTS is set at the JVM startup, so the variable should be available in any class.
But it looks like variable substitution is not supported in catalina.properties (except for some parameters : see https://svn.apache.org/viewvc?view=revision&revision=1136043)
If you really need that, you might modify the ExternalPropertySource class (or extend it with a sub-class) in order to look for ${…} in the externalPropertiesFile and expand it, like it is done in method replace in https://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/Bootstrap.java?r1=1136043&r2=1136042&pathrev=1136043
Pull Requests are always welcome