Tomcat peut-il recharger son certificat SSL sans être redémarré?

11

J'ai un processus d'arrière-plan qui peut mettre à jour le magasin de clés utilisé par Tomcat pour ses informations d'identification SSL. J'aimerais pouvoir recharger Tomcat automatiquement sans avoir besoin d'un redémarrage manuel.

Est-il possible de faire recharger cela par Tomcat sans redémarrer, ou existe-t-il une manière programmatique de le faire à la place?

sdeer
la source

Réponses:

6

Je ne crois pas qu'il existe un moyen de le faire automatiquement, bien que votre processus d'arrière-plan puisse redémarrer Tomcat automatiquement. Le fichier de clés n'est lu qu'une seule fois lorsque le jvm est initialisé. Il pourrait y avoir une solution si vous deviez écrire votre propre gestionnaire qui revérifie périodiquement le magasin de clés, mais je n'ai personnellement trouvé aucun exemple de cela sur Internet.

mahnsc
la source
8

Vous pouvez redémarrer un connecteur Tomcat individuel, c'est-à-dire qu'un redémarrage de port comme le 8443 est possible après avoir modifié votre fichier jssecacert.

Voici le code / la méthode complète que j'utilise pour redémarrer les connecteurs tomcat après avoir ajouté / supprimé des certificats.

// Stop and restart the SSL connection so that the tomcat server will
// re-read the certificates from the truststore file.
public void refreshTrustStore() throws Exception 
{
    try 
    {
        //following line should be replaced based on where you get your port number. You may pass in as argument to this method
        String httpsPort = configurationManager.getHttpsPort();
        String objectString = "*:type=Connector,port=" + httpsPort + ",*";

        final ObjectName objectNameQuery = new ObjectName(objectString); 

        for (final MBeanServer server: MBeanServerFactory.findMBeanServer(null))
        {
            if (!server.queryNames(objectNameQuery, null).isEmpty())
            {
                MBeanServer mbeanServer = server;
                ObjectName objectName = (ObjectName) server.queryNames(objectNameQuery, null).toArray()[0];

                mbeanServer.invoke(objectName, "stop", null, null);

                // Polling sleep to reduce delay to safe minimum.
                // Use currentTimeMillis() over nanoTime() to avoid issues
                // with migrating threads across sleep() calls.
                long start = System.currentTimeMillis();
                // Maximum of 6 seconds, 3x time required on an idle system.
                long max_duration = 6000L;
                long duration = 0L;
                do
                {
                    try
                    {
                        Thread.sleep(100);
                    }
                    catch (InterruptedException e)
                    {
                        Thread.currentThread().interrupt();
                    }

                    duration = (System.currentTimeMillis() - start);
                } while (duration < max_duration &&
                        server.queryNames(objectNameQuery, null).size() > 0);

                // Use below to get more accurate metrics.
                String message = "TrustStoreManager TrustStore Stop: took " + duration + "milliseconds";
                logger.information(message);

                mbeanServer.invoke(objectName, "start", null, null);

                break;
            }
        }
    } 
    catch (Exception exception) 
    {
        // Log and throw exception
            throw exception
    }
}
Sagar Zaveri
la source
1
Cela ne fonctionnera que si le connecteur est configuré avec l' bindOnInit="false"option.
anilech du
4

Il existe maintenant un moyen de le faire à partir de tomcat v8.5.24.

Ils ont introduit 2 méthodes nommées:

  1. reloadSslHostConfig (String hostName) - pour recharger un hôte spécifique
  2. reloadSslHostConfigs () - tout recharger

Ils peuvent être appelés de différentes manières:

  1. Utilisation de jmx
  2. Utilisation du service de gestion (dans tomcat v9.xx)
  3. En créant un protocole personnalisé - j'ai trouvé cette voie lors de mes recherches

Les détails des voies 1 et 2 sont facilement disponibles dans les documents tomcat.

Détails sur la façon d'utiliser la méthode 3:

  1. Faites une classe étendant le protocole de votre choix, par exemple. Http11NioProtocol
  2. Remplacez les méthodes requises et appelez-les simplement super pour conserver le comportement par défaut
  3. Créez un thread dans cette classe pour appeler de temps en temps la méthode reloadSslHostConfigs
  4. Empaqueter cette classe dans un bocal et mettre ce bocal dans le dossier lib de tomcat
  5. Modifier le protocole dans le connecteur dans server.xml pour utiliser ce protocole défini personnalisé

Trouvez un exemple de code ci-dessous:

Classe de protocole principale:

package com.myown.connector;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentMap;

import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.net.ssl.SSLSessionContext;

import org.apache.coyote.http11.Http11NioProtocol;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AbstractJsseEndpoint;
import org.apache.tomcat.util.net.GetSslConfig;
import org.apache.tomcat.util.net.SSLContext;
import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.SSLHostConfigCertificate;
import org.apache.tomcat.util.net.SSLImplementation;
import org.apache.tomcat.util.net.SSLUtil;

public class ReloadProtocol extends Http11NioProtocol {

    private static final Log log = LogFactory.getLog(Http12ProtocolSSL.class);

    public ReloadProtocol() {
        super();
        RefreshSslConfigThread refresher = new 
              RefreshSslConfigThread(this.getEndpoint(), this);
        refresher.start();
    }

    @Override
    public void setKeystorePass(String s) {
        super.setKeystorePass(s);
    }

    @Override
    public void setKeyPass(String s) {
        super.setKeyPass(s);
    }

    @Override
    public void setTruststorePass(String p) {
        super.setTruststorePass(p);
    }

    class RefreshSslConfigThread extends Thread {

        AbstractJsseEndpoint<?> abstractJsseEndpoint = null;
        Http11NioProtocol protocol = null;

        public RefreshSslConfigThread(AbstractJsseEndpoint<?> abstractJsseEndpoint, Http11NioProtocol protocol) {
            this.abstractJsseEndpoint = abstractJsseEndpoint;
            this.protocol = protocol;
        }

        public void run() {
            int timeBetweenRefreshesInt = 1000000; // time in milli-seconds
            while (true) {
                try {
                        abstractJsseEndpoint.reloadSslHostConfigs();
                        System.out.println("Config Updated");
                } catch (Exception e) {
                    System.out.println("Problem while reloading.");
                }
                try {
                    Thread.sleep(timeBetweenRefreshesInt);
                } catch (InterruptedException e) {
                    System.out.println("Error while sleeping");
                }
            }
        }
   }
}

Le connecteur dans server.xml doit le mentionner comme protocole:

<Connector protocol="com.myown.connector.ReloadProtocol"
 ..........

J'espère que cela t'aides.

Mahesh.M
la source