From 82ce5e5b992424636c7dbb201e2061a3f2298a8f Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Tue, 10 Jan 2023 18:17:39 +0100 Subject: [PATCH] Add reload credentials to demo-server. Signed-off-by: Achim Kraus --- .../eclipse/californium/cloud/BaseServer.java | 86 +++++++++++++++---- .../eclipse/californium/cloud/DemoServer.java | 6 +- .../californium/cloud/HttpService.java | 61 ++++++++++--- .../src/main/resources/logback.xml | 3 + 4 files changed, 125 insertions(+), 31 deletions(-) diff --git a/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/BaseServer.java b/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/BaseServer.java index 2170763af1..6e1ae5d940 100644 --- a/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/BaseServer.java +++ b/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/BaseServer.java @@ -64,6 +64,8 @@ import org.eclipse.californium.elements.util.NetworkInterfacesUtil.SimpleInetAddressFilter; import org.eclipse.californium.elements.util.SslContextUtil; import org.eclipse.californium.elements.util.StringUtil; +import org.eclipse.californium.elements.util.SystemResourceMonitors; +import org.eclipse.californium.elements.util.SystemResourceMonitors.SystemResourceMonitor; import org.eclipse.californium.scandium.DTLSConnector; import org.eclipse.californium.scandium.DtlsHealthLogger; import org.eclipse.californium.scandium.MdcConnectionListener; @@ -133,7 +135,9 @@ public enum InterfaceType { */ public static final TimeDefinition UDP_DROPS_READ_INTERVAL = new TimeDefinition("UDP_DROPS_READ_INTERVAL", "Interval to read upd drops from OS (currently only Linux).", 2000, TimeUnit.MILLISECONDS); - + /** + * Maximum device in cache. + */ public static final IntegerDefinition CACHE_MAX_DEVICES = new IntegerDefinition("CACHE_MAX_DEVICES", "Cache maximum devices.", 5000, 100); /** @@ -144,10 +148,17 @@ public enum InterfaceType { + "if at least for that threshold no messages are exchanged using that device.", 24, TimeUnit.HOURS); /** - * Interval to reload credentials. + * Interval to reload https credentials. + */ + public static final TimeDefinition HTTPS_CREDENTIALS_RELOAD_INTERVAL = new TimeDefinition( + "HTTPS_CREDENTIALS_RELOAD_INTERVAL", + "Reload https credentials interval. 0 to load credentials only on startup.", 30, TimeUnit.MINUTES); + /** + * Interval to reload device credentials. */ - public static final TimeDefinition CREDENTIALS_RELOAD_INTERVAL = new TimeDefinition("CREDENTIALS_RELOAD_INTERVAL", - "Reload credentials interval. 0 to load credentials only on startup.", 10, TimeUnit.MINUTES); + public static final TimeDefinition DEVICE_CREDENTIALS_RELOAD_INTERVAL = new TimeDefinition( + "DEVICE_CREDENTIALS_RELOAD_INTERVAL", + "Reload device credentials interval. 0 to load credentials only on startup.", 30, TimeUnit.SECONDS); public static DefinitionsProvider DEFAULTS = new DefinitionsProvider() { @@ -182,6 +193,8 @@ public void applyDefinitions(Configuration config) { config.set(UDP_DROPS_READ_INTERVAL, 2000, TimeUnit.MILLISECONDS); config.set(CACHE_MAX_DEVICES, 5000); config.set(CACHE_STALE_DEVICE_THRESHOLD, 24, TimeUnit.HOURS); + config.set(HTTPS_CREDENTIALS_RELOAD_INTERVAL, 30, TimeUnit.MINUTES); + config.set(DEVICE_CREDENTIALS_RELOAD_INTERVAL, 30, TimeUnit.SECONDS); } }; @@ -381,6 +394,11 @@ public static void start(String[] args, String name, ServerConfig config, BaseSe if (http) { if (HttpService.startHttpService()) { LOGGER.info("HTTPS service at port {} started", config.httpsPort); + long interval = server.getConfig().get(HTTPS_CREDENTIALS_RELOAD_INTERVAL, TimeUnit.MINUTES); + if (interval > 0) { + SystemResourceMonitor httpsCredentialsMonitor = HttpService.getHttpService().getFileMonitor(); + server.monitor.addMonitor("https", interval, TimeUnit.MINUTES, httpsCredentialsMonitor); + } } } long interval = server.getConfig().get(SystemConfig.HEALTH_STATUS_INTERVAL, TimeUnit.MILLISECONDS); @@ -489,12 +507,26 @@ public static void exportPsk(ServerConfig.PskStore config) { } } + protected SystemResourceMonitors monitor; + public BaseServer(Configuration config) { super(config); setVersion(CALIFORNIUM_BUILD_VERSION); setTag("CLOUD-DEMO"); } + @Override + public void start() { + super.start(); + monitor.start(); + } + + @Override + public void stop() { + super.stop(); + monitor.stop(); + } + public void initialize(ServerConfig cliConfig) throws SocketException { Configuration config = getConfig(); // executors @@ -504,6 +536,8 @@ public void initialize(ServerConfig cliConfig) throws SocketException { ScheduledExecutorService secondaryExecutor = ExecutorsUtil .newDefaultSecondaryScheduler("CoapServer(secondary)#"); + monitor = new SystemResourceMonitors(secondaryExecutor); + addEndpoints(cliConfig); addResource(cliConfig, executor); @@ -538,7 +572,6 @@ public void addEndpoints(ServerConfig cliConfig) { boolean healthLogger = config.get(SystemConfig.HEALTH_STATUS_INTERVAL, TimeUnit.MILLISECONDS) > 0; KeyManager[] serverCredentials = null; Certificate[] trustedCertificates = null; - AdvancedPskStore pskStore = null; // load certificate credentials try { @@ -552,18 +585,7 @@ public void addEndpoints(ServerConfig cliConfig) { e.printStackTrace(); } - // load PSK credentials - if (cliConfig.pskStore != null) { - if (cliConfig.pskStore.password64 != null) { - byte[] secret = StringUtil.base64ToByteArray(cliConfig.pskStore.password64); - SecretKey key = SecretUtil.create(secret, "PW"); - Bytes.clear(secret); - pskStore = new MultiPskFileStore().loadPskCredentials(cliConfig.pskStore.file, key); - SecretUtil.destroy(key); - } else { - pskStore = new MultiPskFileStore().loadPskCredentials(cliConfig.pskStore.file); - } - } + AdvancedPskStore pskStore = setupPskStore(cliConfig); // Context matcher load PSK credentials EndpointContextMatcher customContextMatcher = null; @@ -640,6 +662,36 @@ public void addEndpoints(ServerConfig cliConfig) { } } + public AdvancedPskStore setupPskStore(ServerConfig cliConfig) { + AdvancedPskStore pskStore = null; + // load PSK credentials + if (cliConfig.pskStore != null) { + MultiPskFileStore pskFileStore = new MultiPskFileStore(); + long interval = getConfig().get(DEVICE_CREDENTIALS_RELOAD_INTERVAL, TimeUnit.SECONDS); + if (cliConfig.pskStore.password64 != null) { + byte[] secret = StringUtil.base64ToByteArray(cliConfig.pskStore.password64); + SecretKey key = SecretUtil.create(secret, "PW"); + Bytes.clear(secret); + pskFileStore.loadPskCredentials(cliConfig.pskStore.file, key); + if (interval > 0) { + SystemResourceMonitor fileMonitor = pskFileStore.getMonitor(cliConfig.pskStore.file, key); + monitor.addMonitor("encrypted PSK-store", interval, TimeUnit.SECONDS, fileMonitor); + } + SecretUtil.destroy(key); + LOGGER.info("Load encrypted PSK-store from file {}", cliConfig.pskStore.file); + } else { + pskFileStore.loadPskCredentials(cliConfig.pskStore.file); + if (interval > 0) { + SystemResourceMonitor fileMonitor = pskFileStore.getMonitor(cliConfig.pskStore.file, null); + monitor.addMonitor("PSK-store", interval, TimeUnit.SECONDS, fileMonitor); + } + LOGGER.info("Load PSK-store from file {}", cliConfig.pskStore.file); + } + pskStore = pskFileStore; + } + return pskStore; + } + public void setupHttpService(ServerConfig config) { if (config.httpsCredentials != null) { HttpService httpService = HttpService.getHttpService(); diff --git a/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/DemoServer.java b/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/DemoServer.java index a19c0f9445..63a35eedfc 100755 --- a/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/DemoServer.java +++ b/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/DemoServer.java @@ -43,10 +43,12 @@ public class DemoServer extends CoapServer { " DemoServer --psk-file device.psk --store-file dtls.bin --store-max-age 168 \\", " --store-password64 ZVhiRW5pdkx1RUs2dmVoZg==", " (DemoServer with PSK credentials from file and dtls-graceful restart.", - " Devices/sessions with no exchange for more then a week (168 hours)", " are skipped when saving.)", + " Devices/sessions with no exchange for more then a week (168 hours)", + " are skipped when saving.)", "", " DemoServer --psk-file device.psk \\", " --psk-file-export-password64 V3plQUdkTnFLQjRnZWtSeg==", - " (DemoServer encrypts plain PSK credentials file (in place).", " Exits afterwards.)", "", }) + " (DemoServer encrypts plain PSK credentials file (in place).", + " Exits afterwards.)", "", }) public static class Config extends ServerConfig { } diff --git a/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/HttpService.java b/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/HttpService.java index b6bb776ecc..fb149c951f 100644 --- a/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/HttpService.java +++ b/demo-apps/cf-cloud-demo-server/src/main/java/org/eclipse/californium/cloud/HttpService.java @@ -44,6 +44,10 @@ import org.eclipse.californium.elements.util.Bytes; import org.eclipse.californium.elements.util.SslContextUtil; import org.eclipse.californium.elements.util.StandardCharsets; +import org.eclipse.californium.elements.util.SystemResourceMonitors.FileMonitor; +import org.eclipse.californium.elements.util.SystemResourceMonitors.SystemResourceMonitor; +import org.eclipse.californium.scandium.dtls.x509.CertificateConfigurationHelper; +import org.eclipse.californium.elements.util.SystemResourceMonitors.SystemResourceCheckReady; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -275,20 +279,51 @@ public void stop() { } public boolean reloadCredentials() { - boolean reloaded = false; - try { - if (loadCredentials(credentials)) { - LOGGER.info("restart - certificates reloaded."); - stop(); - start(); - reloaded = true; + for (int loop = 0; loop < 3; ++loop) { + try { + if (loadCredentials(credentials)) { + LOGGER.info("restart - certificates reloaded."); + stop(); + start(); + return true; + } + } catch (IllegalArgumentException ex) { + LOGGER.info("Error", ex); + // key pair mismatch + // race condition reading new file pair? + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + } catch (IOException e) { + LOGGER.error("I/O error", e); + break; + } catch (GeneralSecurityException e) { + LOGGER.error("Crypto error", e); + break; } - } catch (IOException e) { - LOGGER.error("I/O error", e); - } catch (GeneralSecurityException e) { - LOGGER.error("Crypto error", e); } - return reloaded; + return false; + } + + /** + * Get resource monitor for automatic credentials reloading. + * + * @return resource monitor + */ + public SystemResourceMonitor getFileMonitor() { + return new FileMonitor(credentials.fullChainUrl) { + + @Override + protected void update(MonitoredValues values, SystemResourceCheckReady ready) { + if (httpService.reloadCredentials()) { + ready(values); + } else { + ready(null); + } + ready.ready(false); + } + }; } /** @@ -545,6 +580,8 @@ public static boolean loadCredentials(Credentials credentials) throws IOExceptio if (certificateChain.length > 0) { if (credentials.node == null || !credentials.node.equals(certificateChain[0])) { PrivateKey privateKey = SslContextUtil.loadPrivateKey(credentials.privateKeyUrl, null, null, null); + CertificateConfigurationHelper helper = new CertificateConfigurationHelper(); + helper.verifyKeyPair(privateKey, certificateChain[0].getPublicKey()); KeyManager[] keyManager = SslContextUtil.createKeyManager("server", privateKey, certificateChain); TrustManager[] trustManager = SslContextUtil.createTrustAllManager(); SSLContext sslContext = SSLContext.getInstance(credentials.tls12Only ? TLS_1_2 : TLS); diff --git a/demo-apps/cf-cloud-demo-server/src/main/resources/logback.xml b/demo-apps/cf-cloud-demo-server/src/main/resources/logback.xml index 0e4b0ce46f..15d3b614c2 100644 --- a/demo-apps/cf-cloud-demo-server/src/main/resources/logback.xml +++ b/demo-apps/cf-cloud-demo-server/src/main/resources/logback.xml @@ -92,6 +92,9 @@ + + +