Sécuriser les mots de passe jdbc du server.xml d’un Tomcat

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.

2 réflexions sur « Sécuriser les mots de passe jdbc du server.xml d’un Tomcat »

  1. 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?

    1. 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 :-)

Répondre à mossroy Annuler la réponse

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *