From 5ced345128dde870c1087f7a398608e860072238 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Mon, 28 Sep 2026 13:19:19 +0000
Subject: [PATCH] [#1111] Refuse an HTTPS configuration without a key the handler can present, and leave a running HTTP handler as it is when one is applied (#1114)
---
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java | 127 +++++++++++++++++++++++++++++-------------
1 files changed, 88 insertions(+), 39 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
index 2b118d5..9f94105 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
@@ -24,6 +24,7 @@
import java.io.IOException;
import java.net.InetAddress;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -211,9 +212,10 @@
}
// Reconfigure SSL if needed.
+ final List<LocalizableMessage> noKeyReasons;
try
{
- configureSSL(config);
+ noKeyReasons = configureSSL(config);
}
catch (DirectoryException e)
{
@@ -222,6 +224,20 @@
ccr.addMessage(e.getMessageObject());
return ccr;
}
+ if (!noKeyReasons.isEmpty())
+ {
+ // The check refuses such a configuration, but the key store may have changed since.
+ // The SSL settings take effect only when the handler restarts, and it will not start with these.
+ for (LocalizableMessage reason : noKeyReasons)
+ {
+ logger.error(reason);
+ ccr.addMessage(reason);
+ }
+ final LocalizableMessage warning = WARN_CONNHANDLER_NO_KEY_UNTIL_RESTART.get(friendlyName);
+ logger.warn(warning);
+ ccr.addMessage(warning);
+ ccr.setAdminActionRequired(true);
+ }
if (config.isEnabled() && this.currentConfig.isEnabled() && isListening())
{
@@ -242,7 +258,9 @@
this.initConfig = config;
this.currentConfig = config;
- this.enabled = this.currentConfig.isEnabled();
+ // Without a key it can present, a running handler keeps serving with the SSL settings it started with,
+ // and a handler that is down does not start.
+ this.enabled = config.isEnabled() && (noKeyReasons.isEmpty() || isListening());
return ccr;
}
@@ -277,18 +295,22 @@
return b1 == b2;
}
- private void configureSSL(HTTPConnectionHandlerCfg config)
+ /**
+ * Sets the SSL engine configurator that the handler starts its HTTP server with.
+ *
+ * @param config
+ * the configuration to take the SSL settings from
+ * @return why the configuration leaves the handler without a key it can present, empty when it has one
+ * @throws DirectoryException
+ * if the SSL context cannot be created
+ */
+ private List<LocalizableMessage> configureSSL(HTTPConnectionHandlerCfg config)
throws DirectoryException
{
protocol = config.isUseSSL() ? "HTTPS" : "HTTP";
- if (config.isUseSSL())
- {
- sslEngineConfigurator = createSSLEngineConfigurator(config, true);
- }
- else
- {
- sslEngineConfigurator = null;
- }
+ final List<LocalizableMessage> noKeyReasons = new ArrayList<>();
+ sslEngineConfigurator = createSSLEngineConfigurator(config, noKeyReasons);
+ return noKeyReasons;
}
@Override
@@ -435,16 +457,26 @@
handlerName = getHandlerName(config);
// Configure SSL if needed.
+ final List<LocalizableMessage> noKeyReasons;
try
{
- // This call may disable the connector if wrong SSL settings
- configureSSL(config);
+ noKeyReasons = configureSSL(config);
}
catch (DirectoryException e)
{
logger.traceException(e);
throw new InitializationException(e.getMessageObject());
}
+ if (!noKeyReasons.isEmpty())
+ {
+ // A handler without a key it can present does not start.
+ for (LocalizableMessage reason : noKeyReasons)
+ {
+ logger.error(reason);
+ }
+ logger.warn(INFO_DISABLE_CONNECTION, friendlyName);
+ this.enabled = false;
+ }
// Create and register monitors.
statTracker = new HTTPStatistics(handlerName + " Statistics");
@@ -495,9 +527,10 @@
if (config.isEnabled() && config.isUseSSL())
{
+ final List<LocalizableMessage> noKeyReasons = new ArrayList<>();
try
{
- createSSLEngineConfigurator(config, false);
+ createSSLEngineConfigurator(config, noKeyReasons);
}
catch (DirectoryException e)
{
@@ -505,6 +538,12 @@
unacceptableReasons.add(e.getMessageObject());
return false;
}
+ if (!noKeyReasons.isEmpty())
+ {
+ // The SSL settings take effect only when the handler restarts, and it would not start with these.
+ unacceptableReasons.addAll(noKeyReasons);
+ return false;
+ }
}
return true;
@@ -822,17 +861,15 @@
*
* @param config
* the configuration to create the SSL engine configurator for
- * @param forUse
- * {@code true} when the handler is going to use the configurator: at its start a handler
- * without a usable key is disabled ({@link #applyConfigurationChange} sets {@code enabled}
- * from the configuration afterwards); {@code false} when the configurator only checks a
- * proposed configuration, which must leave the running handler as it is
+ * @param noKeyReasons
+ * receives why the configuration leaves the handler without a key it can present; the
+ * caller decides what that means for the handler, which this method leaves as it is
* @return the SSL engine configurator, or {@code null} if the configuration does not use SSL
* @throws DirectoryException
* if the SSL context cannot be created
*/
- private SSLEngineConfigurator createSSLEngineConfigurator(HTTPConnectionHandlerCfg config, boolean forUse)
- throws DirectoryException
+ private SSLEngineConfigurator createSSLEngineConfigurator(HTTPConnectionHandlerCfg config,
+ List<LocalizableMessage> noKeyReasons) throws DirectoryException
{
if (!config.isUseSSL())
{
@@ -841,7 +878,7 @@
try
{
- SSLContext sslContext = createSSLContext(config, forUse);
+ SSLContext sslContext = createSSLContext(config, noKeyReasons);
SSLEngineConfigurator configurator = new SSLEngineConfigurator(sslContext);
configurator.setClientMode(false);
@@ -889,16 +926,20 @@
}
}
- private void disableAndWarn(boolean forUse)
- {
- if (forUse)
- {
- logger.warn(INFO_DISABLE_CONNECTION, friendlyName);
- enabled = false;
- }
- }
-
- private SSLContext createSSLContext(HTTPConnectionHandlerCfg config, boolean forUse) throws Exception
+ /**
+ * Creates the SSL context for the provided configuration. The configuration is named by its own name
+ * rather than by {@code friendlyName}, which is not set yet when the check runs on a new instance.
+ *
+ * @param config
+ * the configuration to create the SSL context for
+ * @param noKeyReasons
+ * receives why the configuration leaves the handler without a key it can present
+ * @return the SSL context, or {@code null} if the configuration does not use SSL
+ * @throws Exception
+ * if the SSL context cannot be created
+ */
+ private SSLContext createSSLContext(HTTPConnectionHandlerCfg config, List<LocalizableMessage> noKeyReasons)
+ throws Exception
{
if (!config.isUseSSL())
{
@@ -909,14 +950,12 @@
KeyManagerProvider<?> keyManagerProvider = serverContext.getKeyManagerProvider(keyMgrDN);
if (keyManagerProvider == null)
{
- logger.error(ERR_NULL_KEY_PROVIDER_MANAGER, keyMgrDN, friendlyName);
+ noKeyReasons.add(ERR_NULL_KEY_PROVIDER_MANAGER.get(keyMgrDN, config.name()));
keyManagerProvider = new NullKeyManagerProvider();
- disableAndWarn(forUse);
}
else if (!keyManagerProvider.containsAtLeastOneKey())
{
- logger.error(ERR_INVALID_KEYSTORE, friendlyName);
- disableAndWarn(forUse);
+ noKeyReasons.add(ERR_INVALID_KEYSTORE.get(config.name()));
}
final SortedSet<String> aliases = new TreeSet<>(config.getSSLCertNickname());
@@ -927,18 +966,28 @@
}
else
{
+ final List<LocalizableMessage> missingAliases = new ArrayList<>();
final Iterator<String> it = aliases.iterator();
while (it.hasNext())
{
- if (!keyManagerProvider.containsKeyWithAlias(it.next()))
+ final String alias = it.next();
+ if (!keyManagerProvider.containsKeyWithAlias(alias))
{
- logger.error(ERR_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, aliases, friendlyName);
+ missingAliases.add(ERR_KEYSTORE_DOES_NOT_CONTAIN_ALIAS.get(alias, config.name()));
it.remove();
}
}
if (aliases.isEmpty())
{
- disableAndWarn(forUse);
+ noKeyReasons.addAll(missingAliases);
+ }
+ else
+ {
+ // One of the other aliases is there, so the handler still has a key it can present.
+ for (LocalizableMessage missingAlias : missingAliases)
+ {
+ logger.error(missingAlias);
+ }
}
keyManagers = SelectableCertificateKeyManager.wrap(keyManagerProvider.getKeyManagers(), aliases, friendlyName);
}
--
Gitblit v1.10.0