Tomcat สามารถโหลดใบรับรอง SSL อีกครั้งโดยไม่ต้องรีสตาร์ทหรือไม่


11

ฉันมีกระบวนการพื้นหลังที่สามารถอัปเดตที่เก็บคีย์ Tomcat ใช้สำหรับข้อมูลรับรอง SSL ฉันต้องการให้ Tomcat ทำการโหลดซ้ำโดยอัตโนมัติโดยไม่ต้องรีสตาร์ทเอง

เป็นไปได้หรือไม่ที่จะโหลด Tomcat ใหม่โดยไม่ต้องเริ่มต้นใหม่หรือมีวิธีการเขียนโปรแกรมที่สามารถทำได้แทนหรือไม่?

คำตอบ:


6

ฉันไม่เชื่อว่ามีวิธีการทำโดยอัตโนมัติแม้ว่ากระบวนการพื้นหลังของคุณสามารถรีสตาร์ท Tomcat โดยอัตโนมัติ ที่เก็บคีย์จะอ่านเพียงครั้งเดียวเมื่อ jvm ถูกเตรียมข้อมูลเบื้องต้น อาจมีวิธีแก้ปัญหาหากคุณต้องเขียน handler ของคุณเองที่ตรวจสอบ keystore เป็นระยะ แต่ฉันเองไม่พบตัวอย่างใด ๆ ของสิ่งนี้บนอินเทอร์เน็ต


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 (ชื่อโฮสต์สตริง) - เพื่อโหลดโฮสต์เฉพาะ
  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"
 ..........

หวังว่านี่จะช่วยได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.