Может ли Tomcat перезагрузить свой сертификат SSL без перезапуска?

11

У меня есть фоновый процесс, который может обновить хранилище ключей, которое Tomcat использует для своих учетных данных SSL. Я бы хотел, чтобы Tomcat автоматически перезагружал это без необходимости ручного перезапуска.

Возможно ли сделать так, чтобы Tomcat перезагрузил это без перезагрузки, или есть программный способ сделать это вместо этого?

sdeer
источник

Ответы:

6

Я не верю, что есть способ сделать это автоматически, хотя ваш фоновый процесс может автоматически перезапустить tomcat. Хранилище ключей читается только один раз при инициализации jvm. Возможно, было бы решение, если бы вы написали свой собственный обработчик, который периодически перепроверяет хранилище ключей, но я лично не нашел примеров этого в Интернете.

mahnsc
источник
8

Вы можете перезапустить отдельный коннектор Tomcat, т. Е. Перезапуск порта, например, 8443, возможен после изменения файла jssecacert.

Вот полный код / ​​метод, который я использую для перезапуска коннекторов Tomcat после добавления / удаления сертификатов.

// 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
Это будет работать, только если соединитель настроен с bindOnInit="false"опцией.
anilech
4

Теперь есть способ сделать это, начиная с tomcat v8.5.24.

Они представили 2 метода с именем:

  1. reloadSslHostConfig (String hostName) - перезагрузить определенный хост
  2. reloadSslHostConfigs () - перезагрузить все

Их можно назвать по-разному:

  1. Использование jmx
  2. Использование менеджера сервиса (в Tomcat v9.xx)
  3. Создавая собственный протокол - я нашел этот путь во время исследования

Подробная информация о способе 1 и способе 2 легко доступна в документации по tomcat.

Подробная информация о том, как использовать способ 3:

  1. Создайте класс, расширяющий выбранный вами протокол, например. Http11NioProtocol
  2. Переопределите необходимые методы и просто вызовите в них super, чтобы сохранить поведение по умолчанию
  3. Создайте поток в этом классе, чтобы время от времени вызывать метод reloadSslHostConfigs
  4. Упакуйте этот класс в jar и поместите этот jar в папку lib tomcat
  5. Отредактируйте протокол в соединителе в server.xml, чтобы использовать этот пользовательский протокол

Найдите пример кода ниже:

Основной класс протокола:

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

Соединитель в server.xml должен упомянуть это как протокол:

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

Надеюсь это поможет.

Mahesh.M
источник