From 22d9506206b8b588fe342f3a38dcd81faa590c39 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 25 Mar 2026 16:28:18 +0200 Subject: [PATCH] Improve SSL certificate reload: rollback safety, defaults, and test encapsulation --- .../src/main/resources/thingsboard.yml | 2 +- .../coapserver/DefaultCoapServerService.java | 10 +- .../CoapDtlsCertificateReloadTest.java | 120 ++++++++++-------- .../LwM2MTransportBootstrapService.java | 17 ++- .../server/DefaultLwM2mTransportService.java | 38 +++--- .../LwM2mBootstrapCertificateReloadTest.java | 21 ++- .../LwM2mServerCertificateReloadTest.java | 3 +- .../service/CertificateReloadManager.java | 4 +- .../src/main/resources/tb-coap-transport.yml | 2 +- .../src/main/resources/tb-http-transport.yml | 2 +- .../src/main/resources/tb-lwm2m-transport.yml | 2 +- .../src/main/resources/tb-mqtt-transport.yml | 2 +- 12 files changed, 120 insertions(+), 103 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 3001ea4187..e4bd13cdb3 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1400,7 +1400,7 @@ transport: # X.509 certificate configuration to auto-detect and reload certificate used by transport protocols in real-time (MQTT, CoAP, LwM2M, etc.) reload: # Enable/disable automatic SSL certificates reload - enabled: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_ENABLED:true}" + enabled: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_ENABLED:false}" # Check interval in seconds for certificates reload check_interval_seconds: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_CHECK_INTERVAL_SECONDS:60}" diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java index 34e7e2cfde..087f8137c7 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java @@ -154,14 +154,14 @@ public class DefaultCoapServerService implements CoapServerService, SmartInitial return networkConfig; } - DtlsConnectorConfig buildDtlsConnectorConfig(Configuration networkConfig) throws UnknownHostException { + private DtlsConnectorConfig buildDtlsConnectorConfig(Configuration networkConfig) throws UnknownHostException { TbCoapDtlsSettings dtlsSettings = coapServerContext.getDtlsSettings(); DtlsConnectorConfig dtlsConnectorConfig = dtlsSettings.dtlsConnectorConfig(networkConfig); networkConfig.set(CoapConfig.COAP_SECURE_PORT, dtlsConnectorConfig.getAddress().getPort()); return dtlsConnectorConfig; } - CoapEndpoint buildDtlsEndpoint(Configuration networkConfig, DTLSConnector connector) { + private CoapEndpoint buildDtlsEndpoint(Configuration networkConfig, DTLSConnector connector) { CoapEndpoint.Builder dtlsCoapEndpointBuilder = new CoapEndpoint.Builder(); dtlsCoapEndpointBuilder.setConfiguration(networkConfig); dtlsCoapEndpointBuilder.setConnector(connector); @@ -179,7 +179,7 @@ public class DefaultCoapServerService implements CoapServerService, SmartInitial tbDtlsCertificateVerifier = (TbCoapDtlsCertificateVerifier) dtlsConnectorConfig.getAdvancedCertificateVerifier(); } - DTLSConnector createDtlsConnector(DtlsConnectorConfig config) { + private DTLSConnector createDtlsConnector(DtlsConnectorConfig config) { return new DTLSConnector(config); } @@ -195,7 +195,7 @@ public class DefaultCoapServerService implements CoapServerService, SmartInitial DTLSConnector newConnector = createDtlsConnector(dtlsConnectorConfig); CoapEndpoint newEndpoint = buildDtlsEndpoint(networkConfig, newConnector); - // Stop old endpoint first to release the port before starting the new one + // Stop the old endpoint first to release the port before starting the new one if (oldDtlsEndpoint != null) { log.info("Stopping old DTLS endpoint to release the port..."); server.getEndpoints().remove(oldDtlsEndpoint); @@ -229,7 +229,7 @@ public class DefaultCoapServerService implements CoapServerService, SmartInitial dtlsCoapEndpoint = newEndpoint; tbDtlsCertificateVerifier = (TbCoapDtlsCertificateVerifier) dtlsConnectorConfig.getAdvancedCertificateVerifier(); - // Destroy old resources after successful swap + // Destroy old resources after a successful swap if (oldDtlsEndpoint != null) { if (oldDtlsConnector != null) { oldDtlsConnector.destroy(); diff --git a/common/coap-server/src/test/java/org/thingsboard/server/coapserver/CoapDtlsCertificateReloadTest.java b/common/coap-server/src/test/java/org/thingsboard/server/coapserver/CoapDtlsCertificateReloadTest.java index a7413bdadd..f22aaf5c7e 100644 --- a/common/coap-server/src/test/java/org/thingsboard/server/coapserver/CoapDtlsCertificateReloadTest.java +++ b/common/coap-server/src/test/java/org/thingsboard/server/coapserver/CoapDtlsCertificateReloadTest.java @@ -18,7 +18,6 @@ package org.thingsboard.server.coapserver; import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.core.network.CoapEndpoint; import org.eclipse.californium.core.network.Endpoint; -import org.eclipse.californium.elements.config.Configuration; import org.eclipse.californium.scandium.DTLSConnector; import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.junit.jupiter.api.BeforeEach; @@ -26,22 +25,23 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.Mockito; +import org.mockito.MockedConstruction; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.springframework.test.util.ReflectionTestUtils; import java.io.IOException; +import java.net.InetSocketAddress; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -155,37 +155,42 @@ public class CoapDtlsCertificateReloadTest { // GIVEN when(mockCoapServerContext.getDtlsSettings()).thenReturn(mockDtlsSettings); - CoapEndpoint mockNewEndpoint = mock(CoapEndpoint.class); - DTLSConnector mockNewConnector = mock(DTLSConnector.class); DtlsConnectorConfig mockDtlsConfig = mock(DtlsConnectorConfig.class); TbCoapDtlsCertificateVerifier mockNewVerifier = mock(TbCoapDtlsCertificateVerifier.class); when(mockDtlsConfig.getAdvancedCertificateVerifier()).thenReturn(mockNewVerifier); + when(mockDtlsConfig.getAddress()).thenReturn(new InetSocketAddress("localhost", 5684)); + when(mockDtlsSettings.dtlsConnectorConfig(any())).thenReturn(mockDtlsConfig); - DefaultCoapServerService spyService = Mockito.spy(coapServerService); - ReflectionTestUtils.setField(spyService, "coapServerContext", mockCoapServerContext); - ReflectionTestUtils.setField(spyService, "server", mockCoapServer); - ReflectionTestUtils.setField(spyService, "dtlsCoapEndpoint", mockDtlsEndpoint); - ReflectionTestUtils.setField(spyService, "dtlsConnector", mockDtlsConnector); - - doReturn(mockDtlsConfig).when(spyService).buildDtlsConnectorConfig(any(Configuration.class)); - doReturn(mockNewConnector).when(spyService).createDtlsConnector(any(DtlsConnectorConfig.class)); - doReturn(mockNewEndpoint).when(spyService).buildDtlsEndpoint(any(Configuration.class), any(DTLSConnector.class)); + ReflectionTestUtils.setField(coapServerService, "server", mockCoapServer); + ReflectionTestUtils.setField(coapServerService, "dtlsCoapEndpoint", mockDtlsEndpoint); + ReflectionTestUtils.setField(coapServerService, "dtlsConnector", mockDtlsConnector); List endpointsList = new CopyOnWriteArrayList<>(); endpointsList.add(mockDtlsEndpoint); when(mockCoapServer.getEndpoints()).thenReturn(endpointsList); - // WHEN - ReflectionTestUtils.invokeMethod(spyService, "recreateDtlsEndpoint"); - - // THEN - assertThat(endpointsList).doesNotContain(mockDtlsEndpoint); - verify(mockDtlsEndpoint).stop(); - verify(mockDtlsEndpoint).destroy(); - verify(mockDtlsConnector).destroy(); - verify(mockCoapServer).addEndpoint(mockNewEndpoint); - verify(mockNewEndpoint).start(); - assertThat(ReflectionTestUtils.getField(spyService, "dtlsCoapEndpoint")).isSameAs(mockNewEndpoint); + CoapEndpoint mockNewEndpoint = mock(CoapEndpoint.class); + + try (MockedConstruction dtlsMock = mockConstruction(DTLSConnector.class); + MockedConstruction builderMock = mockConstruction(CoapEndpoint.Builder.class, + (builder, context) -> { + when(builder.build()).thenReturn(mockNewEndpoint); + when(builder.setConfiguration(any())).thenReturn(builder); + when(builder.setConnector(any(DTLSConnector.class))).thenReturn(builder); + })) { + + // WHEN + ReflectionTestUtils.invokeMethod(coapServerService, "recreateDtlsEndpoint"); + + // THEN + assertThat(endpointsList).doesNotContain(mockDtlsEndpoint); + verify(mockDtlsEndpoint).stop(); + verify(mockDtlsEndpoint).destroy(); + verify(mockDtlsConnector).destroy(); + verify(mockCoapServer).addEndpoint(mockNewEndpoint); + verify(mockNewEndpoint).start(); + assertThat(ReflectionTestUtils.getField(coapServerService, "dtlsCoapEndpoint")).isSameAs(mockNewEndpoint); + } } @Test @@ -193,44 +198,49 @@ public class CoapDtlsCertificateReloadTest { // GIVEN when(mockCoapServerContext.getDtlsSettings()).thenReturn(mockDtlsSettings); - CoapEndpoint mockNewEndpoint = mock(CoapEndpoint.class); - DTLSConnector mockNewConnector = mock(DTLSConnector.class); DtlsConnectorConfig mockDtlsConfig = mock(DtlsConnectorConfig.class); + when(mockDtlsConfig.getAddress()).thenReturn(new InetSocketAddress("localhost", 5684)); + when(mockDtlsSettings.dtlsConnectorConfig(any())).thenReturn(mockDtlsConfig); - doThrow(new IOException("start failed")).when(mockNewEndpoint).start(); - - DefaultCoapServerService spyService = Mockito.spy(coapServerService); - ReflectionTestUtils.setField(spyService, "coapServerContext", mockCoapServerContext); - ReflectionTestUtils.setField(spyService, "server", mockCoapServer); - ReflectionTestUtils.setField(spyService, "dtlsCoapEndpoint", mockDtlsEndpoint); - ReflectionTestUtils.setField(spyService, "dtlsConnector", mockDtlsConnector); - - doReturn(mockDtlsConfig).when(spyService).buildDtlsConnectorConfig(any(Configuration.class)); - doReturn(mockNewConnector).when(spyService).createDtlsConnector(any(DtlsConnectorConfig.class)); - doReturn(mockNewEndpoint).when(spyService).buildDtlsEndpoint(any(Configuration.class), any(DTLSConnector.class)); + ReflectionTestUtils.setField(coapServerService, "server", mockCoapServer); + ReflectionTestUtils.setField(coapServerService, "dtlsCoapEndpoint", mockDtlsEndpoint); + ReflectionTestUtils.setField(coapServerService, "dtlsConnector", mockDtlsConnector); List endpointsList = new CopyOnWriteArrayList<>(); endpointsList.add(mockDtlsEndpoint); when(mockCoapServer.getEndpoints()).thenReturn(endpointsList); - // WHEN - the callback catches the IOException internally - spyService.afterSingletonsInstantiated(); - - ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(mockDtlsSettings).registerReloadCallback(callbackCaptor.capture()); - Runnable reloadCallback = callbackCaptor.getValue(); - reloadCallback.run(); + CoapEndpoint mockNewEndpoint = mock(CoapEndpoint.class); + doThrow(new IOException("start failed")).when(mockNewEndpoint).start(); - // THEN - new resources cleaned up - verify(mockNewEndpoint).destroy(); - verify(mockNewConnector).destroy(); - assertThat(endpointsList).doesNotContain(mockNewEndpoint); - // Old endpoint was stopped to release port, then restored after new one failed - verify(mockDtlsEndpoint).stop(); - verify(mockDtlsEndpoint).start(); - // Old fields preserved - assertThat(ReflectionTestUtils.getField(spyService, "dtlsCoapEndpoint")).isSameAs(mockDtlsEndpoint); - assertThat(ReflectionTestUtils.getField(spyService, "dtlsConnector")).isSameAs(mockDtlsConnector); + try (MockedConstruction dtlsMock = mockConstruction(DTLSConnector.class); + MockedConstruction builderMock = mockConstruction(CoapEndpoint.Builder.class, + (builder, context) -> { + when(builder.build()).thenReturn(mockNewEndpoint); + when(builder.setConfiguration(any())).thenReturn(builder); + when(builder.setConnector(any(DTLSConnector.class))).thenReturn(builder); + })) { + + // WHEN + coapServerService.afterSingletonsInstantiated(); + + ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(Runnable.class); + verify(mockDtlsSettings).registerReloadCallback(callbackCaptor.capture()); + Runnable reloadCallback = callbackCaptor.getValue(); + reloadCallback.run(); + + // THEN - new resources cleaned up + DTLSConnector constructedConnector = dtlsMock.constructed().get(0); + verify(mockNewEndpoint).destroy(); + verify(constructedConnector).destroy(); + assertThat(endpointsList).doesNotContain(mockNewEndpoint); + // Old endpoint was stopped to release port, then restored after new one failed + verify(mockDtlsEndpoint).stop(); + verify(mockDtlsEndpoint).start(); + // Old fields preserved + assertThat(ReflectionTestUtils.getField(coapServerService, "dtlsCoapEndpoint")).isSameAs(mockDtlsEndpoint); + assertThat(ReflectionTestUtils.getField(coapServerService, "dtlsConnector")).isSameAs(mockDtlsConnector); + } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java index a9a4e364b5..9b370d0b71 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java @@ -185,10 +185,10 @@ public class LwM2MTransportBootstrapService implements SmartInitializingSingleto log.info("Creating new LwM2M Bootstrap server with updated certificates..."); LeshanBootstrapServer newServer = getLhBootstrapServer(); - // Stop the old server first to release the ports before starting the new one + // Stop (not destroy) the old server to release ports but keep it restartable for rollback if (oldServer != null) { log.info("Stopping old LwM2M Bootstrap server to release ports..."); - oldServer.destroy(); + oldServer.stop(); } try { @@ -196,13 +196,11 @@ public class LwM2MTransportBootstrapService implements SmartInitializingSingleto } catch (Exception e) { log.error("Failed to start new LwM2M Bootstrap server", e); newServer.destroy(); - // Attempt to restore the old server + // Attempt to restart the old server (only stopped, not destroyed) if (oldServer != null) { try { - LeshanBootstrapServer restoredServer = getLhBootstrapServer(); - restoredServer.start(); - this.server = restoredServer; - log.info("Restored LwM2M Bootstrap server with previous configuration."); + oldServer.start(); + log.info("Restored old LwM2M Bootstrap server successfully."); } catch (Exception restoreEx) { log.error("Failed to restore old LwM2M Bootstrap server", restoreEx); } @@ -211,6 +209,11 @@ public class LwM2MTransportBootstrapService implements SmartInitializingSingleto } this.server = newServer; log.info("New LwM2M Bootstrap server started successfully."); + + // Destroy the old server only after a successful swap + if (oldServer != null) { + oldServer.destroy(); + } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index b0d25729b3..5815a47555 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -236,7 +236,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService, Smar log.info("Creating new LwM2M server with updated certificates..."); LeshanServer newServer = getLhServer(); - // Stop old server first to release the ports before starting the new one + // Stop (not destroy) old server to release ports but keep it restartable for rollback if (oldServer != null) { log.info("Stopping old LwM2M server to release ports..."); if (oldListener != null) { @@ -245,7 +245,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService, Smar oldServer.getObservationService().removeListener(oldListener.observationListener); oldServer.getSendService().removeListener(oldListener.sendListener); } - oldServer.destroy(); + oldServer.stop(); } try { @@ -253,21 +253,20 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService, Smar } catch (Exception e) { log.error("Failed to start new LwM2M server", e); newServer.destroy(); - // Attempt to restore the old server - try { - LeshanServer restoredServer = getLhServer(); - restoredServer.start(); - LwM2mServerListener restoredListener = new LwM2mServerListener(handler); - restoredServer.getRegistrationService().addListener(restoredListener.registrationListener); - restoredServer.getPresenceService().addListener(restoredListener.presenceListener); - restoredServer.getObservationService().addListener(restoredListener.observationListener); - restoredServer.getSendService().addListener(restoredListener.sendListener); - this.server = restoredServer; - this.context.setServer(restoredServer); - this.serverListener = restoredListener; - log.info("Restored LwM2M server with previous configuration."); - } catch (Exception restoreEx) { - log.error("Failed to restore old LwM2M server", restoreEx); + // Attempt to restart the old server (only stopped, not destroyed) + if (oldServer != null) { + try { + oldServer.start(); + if (oldListener != null) { + oldServer.getRegistrationService().addListener(oldListener.registrationListener); + oldServer.getPresenceService().addListener(oldListener.presenceListener); + oldServer.getObservationService().addListener(oldListener.observationListener); + oldServer.getSendService().addListener(oldListener.sendListener); + } + log.info("Restored old LwM2M server successfully."); + } catch (Exception restoreEx) { + log.error("Failed to restore old LwM2M server", restoreEx); + } } throw e; } @@ -282,6 +281,11 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService, Smar this.context.setServer(newServer); this.serverListener = newListener; log.info("New LwM2M server started successfully."); + + // Destroy old server only after successful swap + if (oldServer != null) { + oldServer.destroy(); + } } @Override diff --git a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2mBootstrapCertificateReloadTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2mBootstrapCertificateReloadTest.java index 84bc44b03b..bbcbc921f6 100644 --- a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2mBootstrapCertificateReloadTest.java +++ b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2mBootstrapCertificateReloadTest.java @@ -122,6 +122,7 @@ public class LwM2mBootstrapCertificateReloadTest { // The old server should NOT be destroyed since the new server was never created. reloadCallback.run(); + verify(mockBootstrapServer, never()).stop(); verify(mockBootstrapServer, never()).destroy(); assertThat(ReflectionTestUtils.getField(bootstrapService, "server")).isSameAs(mockBootstrapServer); } @@ -164,18 +165,15 @@ public class LwM2mBootstrapCertificateReloadTest { } @Test - public void givenReloadCallback_whenNewServerStartFails_thenNewServerDestroyedAndRestorationAttempted() { + public void givenReloadCallback_whenNewServerStartFails_thenOldServerRestarted() { // GIVEN ReflectionTestUtils.setField(bootstrapService, "server", mockBootstrapServer); LeshanBootstrapServer mockNewServer = mock(LeshanBootstrapServer.class); doThrow(new RuntimeException("start failed")).when(mockNewServer).start(); - LeshanBootstrapServer mockRestoredServer = mock(LeshanBootstrapServer.class); - LwM2MTransportBootstrapService spyService = Mockito.spy(bootstrapService); - // First call returns the failing server, second call returns the restoration server - doReturn(mockNewServer).doReturn(mockRestoredServer).when(spyService).getLhBootstrapServer(); + doReturn(mockNewServer).when(spyService).getLhBootstrapServer(); ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(Runnable.class); spyService.afterSingletonsInstantiated(); @@ -187,13 +185,14 @@ public class LwM2mBootstrapCertificateReloadTest { reloadCallback.run(); // THEN - // Old server is destroyed to release ports - verify(mockBootstrapServer).destroy(); - // New server fails to start and is destroyed + // Old server is stopped (not destroyed) to release ports + verify(mockBootstrapServer).stop(); + verify(mockBootstrapServer, never()).destroy(); + // The new server fails to start and is destroyed verify(mockNewServer).destroy(); - // Restoration server is started and becomes the active server - verify(mockRestoredServer).start(); - assertThat(ReflectionTestUtils.getField(spyService, "server")).isSameAs(mockRestoredServer); + // Old server is restarted (not rebuilt from potentially stale credentials) + verify(mockBootstrapServer).start(); + assertThat(ReflectionTestUtils.getField(spyService, "server")).isSameAs(mockBootstrapServer); } } diff --git a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerCertificateReloadTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerCertificateReloadTest.java index c8b7f0d060..93b74447fd 100644 --- a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerCertificateReloadTest.java +++ b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerCertificateReloadTest.java @@ -139,9 +139,10 @@ public class LwM2mServerCertificateReloadTest { // Force getLhServer() to fail by returning null host (causes InetSocketAddress to throw) when(mockConfig.getHost()).thenReturn(null); - // With create-then-swap, the old server should NOT be destroyed if the new one fails. + // With create-then-swap, the old server should NOT be stopped/destroyed if the new one fails to build. reloadCallback.run(); + verify(mockLeshanServer, never()).stop(); verify(mockLeshanServer, never()).destroy(); // Old server should still be the active one assertThat(ReflectionTestUtils.getField(lwm2mTransportService, "server")).isSameAs(mockLeshanServer); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/CertificateReloadManager.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/CertificateReloadManager.java index 0dd1fae724..f243c64c50 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/CertificateReloadManager.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/CertificateReloadManager.java @@ -48,7 +48,7 @@ public class CertificateReloadManager implements SmartInitializingSingleton, Dis private static final int MAX_CONSECUTIVE_FAILURES = 10; - @Value("${transport.ssl.certificate.reload.enabled:true}") + @Value("${transport.ssl.certificate.reload.enabled:false}") private boolean reloadEnabled; @Value("${transport.ssl.certificate.reload.check_interval_seconds:60}") @@ -103,7 +103,7 @@ public class CertificateReloadManager implements SmartInitializingSingleton, Dis List filePaths = credentials.getCertificateFilePaths(); if (filePaths == null || filePaths.isEmpty()) { - log.debug("No certificate files to watch for: {} ({})", config.getName(), beanName); + log.debug("No file-system certificate paths to watch for: {} ({}) — certificates may be classpath-based", config.getName(), beanName); continue; } diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 3c1ef94f09..f9827d488f 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -176,7 +176,7 @@ transport: # X.509 certificate configuration to auto-detect and reload certificate used by transport protocols in real-time (MQTT, CoAP, LwM2M, etc.) reload: # Enable/disable automatic SSL certificates reload - enabled: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_ENABLED:true}" + enabled: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_ENABLED:false}" # Check interval in seconds for certificates reload check_interval_seconds: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_CHECK_INTERVAL_SECONDS:60}" diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index 1b221d1fd9..7efa428a90 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -207,7 +207,7 @@ transport: # X.509 certificate configuration to auto-detect and reload certificate used by transport protocols in real-time (MQTT, CoAP, LwM2M, etc.) reload: # Enable/disable automatic SSL certificates reload - enabled: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_ENABLED:true}" + enabled: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_ENABLED:false}" # Check interval in seconds for certificates reload check_interval_seconds: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_CHECK_INTERVAL_SECONDS:60}" diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 6140122062..543b11859b 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -307,7 +307,7 @@ transport: # X.509 certificate configuration to auto-detect and reload certificate used by transport protocols in real-time (MQTT, CoAP, LwM2M, etc.) reload: # Enable/disable automatic SSL certificates reload - enabled: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_ENABLED:true}" + enabled: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_ENABLED:false}" # Check interval in seconds for certificates reload check_interval_seconds: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_CHECK_INTERVAL_SECONDS:60}" diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index ac02fa396b..60d3da8f8e 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -240,7 +240,7 @@ transport: # X.509 certificate configuration to auto-detect and reload certificate used by transport protocols in real-time (MQTT, CoAP, LwM2M, etc.) reload: # Enable/disable automatic SSL certificates reload - enabled: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_ENABLED:true}" + enabled: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_ENABLED:false}" # Check interval in seconds for certificates reload check_interval_seconds: "${TB_TRANSPORT_SSL_CERTIFICATE_RELOAD_CHECK_INTERVAL_SECONDS:60}"