Bisakah Tomcat memuat ulang sertifikat SSL-nya tanpa dihidupkan ulang?

11

Saya memiliki proses latar belakang yang dapat memperbarui keystore yang digunakan Tomcat untuk kredensial SSL-nya. Saya ingin Tomcat memuat ulang ini secara otomatis tanpa perlu restart manual.

Mungkinkah Tomcat memuat ulang ini tanpa memulai ulang, atau adakah cara terprogram yang dapat dilakukan sebagai gantinya?

Sdeer
sumber

Jawaban:

6

Saya tidak percaya ada cara untuk melakukannya secara otomatis walaupun proses latar belakang Anda dapat memulai kembali kucing jantan secara otomatis. Keystore hanya bisa dibaca sekali ketika jvm diinisialisasi. Mungkin ada solusi jika Anda menulis handler Anda sendiri yang secara berkala memeriksa ulang keystore tetapi saya pribadi belum menemukan contoh ini di Internet.

mahnsc
sumber
8

Anda dapat me-restart setiap konektor Tomcat yaitu port restart seperti 8443 dimungkinkan setelah Anda mengubah file jssecacert Anda.

Berikut adalah kode / metode lengkap yang saya gunakan untuk me-restart konektor kucing jantan setelah saya menambah / menghapus sertifikat.

// 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
sumber
1
Ini hanya akan berfungsi jika konektor dikonfigurasi dengan bindOnInit="false"opsi.
anilech
4

Sekarang ada cara untuk melakukan ini dimulai dengan tomcat v8.5.24.

Mereka memperkenalkan 2 metode bernama:

  1. reloadSslHostConfig (String hostName) - untuk memuat ulang host tertentu
  2. reloadSslHostConfigs () - muat ulang semua

Mereka dapat dipanggil dengan berbagai cara:

  1. Menggunakan jmx
  2. Menggunakan layanan manajer (di tomcat v9.xx)
  3. Dengan membuat protokol khusus - saya menemukan cara ini selama penelitian

Rincian cara 1 dan cara 2 mudah tersedia di dokumen kucing jantan.

Rincian cara menggunakan cara 3:

  1. Buat kelas memperluas protokol pilihan Anda untuk misalnya. Http11NioProtocol
  2. Ganti metode yang diperlukan dan panggil super di dalamnya untuk mempertahankan perilaku default
  3. Buat utas di kelas ini untuk memanggil metode reloadSslHostConfigs dari waktu ke waktu
  4. Kemas kelas ini dalam toples dan masukkan toples itu ke folder lib kucing jantan
  5. Edit protokol dalam konektor di server.xml untuk menggunakan protokol yang ditentukan khusus ini

Temukan kode contoh di bawah ini:

Kelas protokol utama:

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

Connector di server.xml harus menyebutkan ini sebagai protokol:

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

Semoga ini membantu.

Mahesh
sumber