Kann Tomcat sein SSL-Zertifikat neu laden, ohne neu gestartet zu werden?

11

Ich habe einen Hintergrundprozess, der den Keystore aktualisieren kann, den Tomcat für seine SSL-Anmeldeinformationen verwendet. Ich möchte, dass Tomcat dies automatisch neu lädt, ohne dass ein manueller Neustart erforderlich ist.

Ist es möglich, dass Tomcat dies ohne Neustart neu lädt, oder gibt es eine programmatische Möglichkeit, dies stattdessen zu tun?

sdeer
quelle

Antworten:

6

Ich glaube nicht, dass es eine Möglichkeit gibt, dies automatisch zu tun, obwohl Ihr Hintergrundprozess Tomcat automatisch neu starten könnte. Der Keystore wird nur einmal gelesen, wenn der JVM initialisiert wird. Es könnte eine Lösung geben, wenn Sie Ihren eigenen Handler schreiben, der den Keystore regelmäßig überprüft, aber ich persönlich habe im Internet keine Beispiele dafür gefunden.

mahnsc
quelle
8

Sie können den einzelnen Tomcat-Connector neu starten, dh ein Neustart des Ports wie 8443 ist möglich, nachdem Sie Ihre jssecacert-Datei geändert haben.

Hier ist der vollständige Code / die vollständige Methode, mit der ich Tomcat-Connectors neu starte, nachdem ich Zertifikate hinzugefügt / gelöscht habe.

// 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
quelle
1
Dies funktioniert nur, wenn der Connector mit bindOnInit="false"Option konfiguriert ist .
Anilech
4

Es gibt jetzt eine Möglichkeit, dies ab Tomcat v8.5.24 zu tun.

Sie führten zwei Methoden mit dem Namen ein:

  1. reloadSslHostConfig (String hostName) - um einen bestimmten Host neu zu laden
  2. reloadSslHostConfigs () - lade alle neu

Sie können auf verschiedene Arten aufgerufen werden:

  1. Mit jmx
  2. Verwenden des Manager-Dienstes (in Tomcat v9.xx)
  3. Durch die Erstellung eines benutzerdefinierten Protokolls habe ich diesen Weg während meiner Recherche gefunden

Details zu Weg 1 und Weg 2 sind in Tomcat-Dokumenten leicht verfügbar.

Einzelheiten zur Vorgehensweise bei Weg 3:

  1. Machen Sie eine Klasse, die das Protokoll Ihrer Wahl erweitert, z. Http11NioProtocol
  2. Überschreiben Sie die erforderlichen Methoden und rufen Sie sie einfach super auf, um das Standardverhalten beizubehalten
  3. Erstellen Sie in dieser Klasse einen Thread, um von Zeit zu Zeit die Methode reloadSslHostConfigs aufzurufen
  4. Packen Sie diese Klasse in ein Glas und legen Sie dieses Glas in den lib-Ordner von tomcat
  5. Bearbeiten Sie das Protokoll im Connector in server.xml, um dieses benutzerdefinierte Protokoll zu verwenden

Unten finden Sie Beispielcode:

Hauptprotokollklasse:

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

Der Connector in server.xml sollte dies als Protokoll angeben:

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

Hoffe das hilft.

Mahesh.M
quelle