Browse Source

Fix SSL certificate reload: port conflict, redundant reload(), and watcher polling

pull/15301/head
Andrii Landiak 6 months ago
parent
commit
edcf3a9d22
  1. 25
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java
  2. 9
      common/coap-server/src/test/java/org/thingsboard/server/coapserver/CoapDtlsCertificateReloadTest.java
  3. 3
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/AbstractSslCredentials.java
  4. 4
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/CertificateReloadManager.java

25
common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java

@ -195,14 +195,31 @@ 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
if (oldDtlsEndpoint != null) {
log.info("Stopping old DTLS endpoint to release the port...");
server.getEndpoints().remove(oldDtlsEndpoint);
oldDtlsEndpoint.stop();
}
server.addEndpoint(newEndpoint);
try {
newEndpoint.start();
} catch (IOException e) {
log.error("Failed to start new DTLS endpoint, cleaning up", e);
log.error("Failed to start new DTLS endpoint, restoring old endpoint", e);
server.getEndpoints().remove(newEndpoint);
newEndpoint.destroy();
newConnector.destroy();
// Attempt to restore the old endpoint
if (oldDtlsEndpoint != null) {
try {
server.addEndpoint(oldDtlsEndpoint);
oldDtlsEndpoint.start();
log.info("Old DTLS endpoint restored successfully.");
} catch (IOException restoreEx) {
log.error("Failed to restore old DTLS endpoint", restoreEx);
}
}
throw e;
}
log.info("New DTLS endpoint started successfully.");
@ -212,15 +229,13 @@ public class DefaultCoapServerService implements CoapServerService, SmartInitial
dtlsCoapEndpoint = newEndpoint;
tbDtlsCertificateVerifier = (TbCoapDtlsCertificateVerifier) dtlsConnectorConfig.getAdvancedCertificateVerifier();
// Destroy old resources after successful swap
if (oldDtlsEndpoint != null) {
log.info("Stopping old DTLS endpoint...");
server.getEndpoints().remove(oldDtlsEndpoint);
oldDtlsEndpoint.stop();
if (oldDtlsConnector != null) {
oldDtlsConnector.destroy();
}
oldDtlsEndpoint.destroy();
log.info("Old DTLS endpoint stopped and destroyed.");
log.info("Old DTLS endpoint destroyed.");
}
}

9
common/coap-server/src/test/java/org/thingsboard/server/coapserver/CoapDtlsCertificateReloadTest.java

@ -189,7 +189,7 @@ public class CoapDtlsCertificateReloadTest {
}
@Test
public void givenReloadCallback_whenStartFails_thenNewResourcesCleaned() throws Exception {
public void givenReloadCallback_whenStartFails_thenNewResourcesCleanedAndOldRestored() throws Exception {
// GIVEN
when(mockCoapServerContext.getDtlsSettings()).thenReturn(mockDtlsSettings);
@ -210,6 +210,7 @@ public class CoapDtlsCertificateReloadTest {
doReturn(mockNewEndpoint).when(spyService).buildDtlsEndpoint(any(Configuration.class), any(DTLSConnector.class));
List<Endpoint> endpointsList = new CopyOnWriteArrayList<>();
endpointsList.add(mockDtlsEndpoint);
when(mockCoapServer.getEndpoints()).thenReturn(endpointsList);
// WHEN - the callback catches the IOException internally
@ -224,12 +225,12 @@ public class CoapDtlsCertificateReloadTest {
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);
// Old endpoint not touched
verify(mockDtlsEndpoint, never()).stop();
verify(mockDtlsEndpoint, never()).destroy();
}
}

3
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/AbstractSslCredentials.java

@ -60,8 +60,7 @@ public abstract class AbstractSslCredentials implements SslCredentials {
@Override
public void reload(boolean trustsOnly) throws IOException, GeneralSecurityException {
SslState newState = buildState(trustsOnly);
state.set(newState);
init(trustsOnly);
}
private SslState buildState(boolean trustsOnly) throws IOException, GeneralSecurityException {

4
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/CertificateReloadManager.java

@ -228,6 +228,10 @@ public class CertificateReloadManager implements SmartInitializingSingleton, Dis
}
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
// Update modification times to avoid re-checking mtime and re-computing checksums every poll cycle
for (Path path : paths) {
lastModifiedMap.put(path, getLastModifiedTime(path));
}
return;
}

Loading…
Cancel
Save