Tomcat può ricaricare il suo certificato SSL senza essere riavviato?


11

Ho un processo in background che può aggiornare il keystore che Tomcat utilizza per le sue credenziali SSL. Vorrei poter ricaricare Tomcat automaticamente senza bisogno di un riavvio manuale.

È possibile chiedere a Tomcat di ricaricarlo senza riavviarlo, oppure esiste un modo programmatico per farlo?

Risposte:


6

Non credo che ci sia un modo per farlo automaticamente anche se il processo in background potrebbe riavviare Tomcat automaticamente. Il keystore viene letto solo una volta quando jvm è inizializzato. Potrebbe esserci una soluzione se dovessi scrivere il tuo gestore che periodicamente ricontrolla il keystore ma personalmente non ho trovato alcun esempio di questo su Internet.


8

È possibile riavviare il singolo connettore Tomcat, ad esempio il riavvio della porta come 8443 è possibile dopo aver modificato il file jssecacert.

Ecco il codice / metodo completo che sto usando per riavviare i connettori Tomcat dopo aver aggiunto / eliminato i certificati.

// 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
    }
}

1
Funzionerà solo se il connettore è configurato con l' bindOnInit="false"opzione.
anilech,

4

Ora c'è un modo per farlo a partire da Tomcat v8.5.24.

Hanno introdotto 2 metodi chiamati:

  1. reloadSslHostConfig (String hostName) - per ricaricare un host specifico
  2. reloadSslHostConfigs () - ricarica tutto

Possono essere chiamati in vari modi:

  1. Utilizzando jmx
  2. Utilizzo del servizio di gestione (in tomcat v9.xx)
  3. Creando un protocollo personalizzato - l'ho trovato in questo modo durante la mia ricerca

I dettagli del modo 1 e del modo 2 sono facilmente disponibili nei documenti tomcat.

Dettagli su come utilizzare il modo 3:

  1. Crea una classe estendendo il protocollo di tua scelta per es. Http11NioProtocol
  2. Sovrascrivi i metodi richiesti e chiama super in essi per mantenere il comportamento predefinito
  3. Crea un thread in questa classe per chiamare il metodo reloadSslHostConfigs di volta in volta
  4. Imballa questa classe in un barattolo e inseriscilo nella cartella lib di tomcat
  5. Modifica il protocollo nel connettore in server.xml per utilizzare questo protocollo personalizzato

Trova il codice di esempio di seguito:

Classe di protocollo 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");
                }
            }
        }
   }
}

Il connettore in server.xml dovrebbe menzionarlo come protocollo:

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

Spero che sia di aiuto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.