[#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
Merged
vharseko merged 1 commit intoSep 28, 2026
Conversation
maximthomas
approved these changes
Sep 28, 2026
maximthomas
left a comment
Contributor
There was a problem hiding this comment.
praise: The change is measured to pin what it fixes.
- Against the
HTTPConnectionHandlerof #1110's head (b4b0cad),RejectedSSLConfigurationChangeTestCaseat 3dd05f8 fails 9 of 24, exactly the nine cases the description names and each for the reason it gives; the other 15 pass. At the head it passes 24/24 on CI (build-maven ubuntu-latest, 11). enabled = config.isEnabled() && (noKeyReasons.isEmpty() || isListening())(HTTPConnectionHandler.java:263) keeps a running handler serving and a handler that is down for want of a key down, in one line.- The reasons name
config.name(), so the check on the new instance of an added or enabled handler (wherefriendlyNameisnull) still names it, and the new-instance rows pin that.
…ey the handler can present, and leave a running HTTP handler as it is when one is applied The SSL settings of the HTTP connection handler take effect only when it restarts: Grizzly takes the SSL engine configuration when the handler starts its HTTP server, and every SSL property is in anyChangeRequiresRestart. A change that left the handler without a key it can present (no key manager provider registered under the configured DN, a key store without a key, or none of the ssl-cert-nickname aliases in it) was accepted by the check, because wrapping an empty alias set does not throw. The apply then logged "Disabling ...", set enabled to false, and set it back from the configuration a few lines later, so the handler kept serving with its old certificate and was down only after the restart. A handler that its start had disabled for want of a key was even started by such a change, with an SSL context without a key. createSSLContext no longer touches enabled. It collects why the configuration leaves the handler without a key, and each caller decides what that means: - the check refuses the configuration with these reasons, for a change to a running handler and for a handler being added or enabled, which is checked on a new instance. A key store that cannot be loaded is still refused with ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE, as before; - the start logs the reasons and "Disabling ...", and disables the handler, as before; - the apply, reached when the key store changed after the check, adds the reasons and the new WARN_CONNHANDLER_NO_KEY_UNTIL_RESTART to the result, asks for administrative action, and logs no "Disabling". A running handler keeps serving with the SSL settings it started with; a handler that is down stays down. A change that gives a handler disabled at its start a key still starts it, as before. The reasons name the configuration by config.name() rather than by friendlyName, which is not set on the new instance the check of an added or enabled handler runs on. A missing alias is reported by its own name rather than by the whole alias set, and while another configured alias is in the key store it is only logged, as before. The forUse flag and disableAndWarn that OpenIdentityPlatform#1110 added to the HTTP handler are replaced by the collected reasons. The LDAP handlers are left as they are: they build an SSL engine for each new connection, so their SSL changes take effect at once, and an applied change without a key disables them at once. RejectedSSLConfigurationChangeTestCase gains 11 HTTP cases: the check refusing each of the three kinds of missing key on a running handler and on a new instance, with the reason naming the handler; the check accepting a configuration with one of its aliases in the key store; the apply of a change without a key, by a changed nickname and by a key store emptied under an unchanged configuration; and a handler disabled at its start staying down on a change still without a key and starting on one that gives it a key.
vharseko
force-pushed
the
issue-1111-http-ssl-change-at-restart
branch
from
September 28, 2026 13:18
3dd05f8 to
11968d3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1111
Stacked on #1110. The branch sits on the head of #1110 (b4b0cad), so until #1110 is merged this PR also shows its two commits. The change of this PR is the last commit, 3dd05f8. It replaces the
forUseflag that #1110 adds toHTTPConnectionHandler.Problem
The SSL settings of the HTTP connection handler take effect only when it restarts: Grizzly takes the SSL engine configuration when the handler starts its HTTP server, and every SSL property is in
anyChangeRequiresRestart. The LDAP handlers are different: they build an SSL engine for each new connection, so their SSL changes take effect at once.A change that leaves the HTTP handler without a key it can present (no key manager provider registered under the configured DN, a key store without a key, or none of the
ssl-cert-nicknamealiases in it) is accepted by the check, because wrapping an empty alias set does not throw. Then:Disabling …and setsenabled = false, and a few lines later sets it back from the configuration. The handler keeps serving with its old certificate, and is down only after the restart;enabled = true, and the handler starts listening with an SSL context that has no key.Change
HTTPConnectionHandler.createSSLContextno longer touchesenabled. It collects why the configuration leaves the handler without a key, and each caller decides what that means:isConfigurationAcceptable): refuses the configuration with these reasons. This covers a change to a running handler and a handler being added or enabled, whichConnectionHandlerConfigManagerchecks on a new instance. A key store that cannot be loaded is still refused withERR_CONNHANDLER_SSL_CANNOT_INITIALIZE, as in [#1109] Keep a connection handler listening when a change to it is rejected #1110. Disabling the handler is not affected: the SSL part of the check runs only for an enabled configuration;initializeConnectionHandler): logs the reasons andDisabling …, and disables the handler, as before;applyConfigurationChange): this path runs when the key store changed after the check. The apply adds the reasons and the newWARN_CONNHANDLER_NO_KEY_UNTIL_RESTARTto the result and asks for administrative action. It logs noDisabling. A running handler keeps serving with the SSL settings it started with, and a handler that is down stays down (enabled = config.isEnabled() && (noKeyReasons.isEmpty() || isListening())). A change that gives a handler disabled at its start a key still starts it, as before.Smaller changes in the same method:
config.name()rather than byfriendlyName, which isnullon the new instance the check of an added or enabled handler runs on;'no-such-cert'instead of'[no-such-cert]');The LDAP handlers are left as they are: an applied change without a key disables them at once, which matches their SSL changes taking effect at once (
appliedChangeWithoutItsCertificateStopsListeningfrom #1110 pins it forLDAPConnectionHandler2).New message:
WARN_CONNHANDLER_NO_KEY_UNTIL_RESTART_1541inprotocol.properties. No open PR claims 1541.Tests
RejectedSSLConfigurationChangeTestCasegains 11 HTTP cases (24 in all):httpCheckRefusesAConfigurationWithoutAKey: for each missing key (alias, key store loaded but empty, no provider registered), on a running handler and on a new instance. The check refuses the configuration with exactly one reason of the expected kind, the reason names the handler, and a running handler keeps serving TLS;httpCheckAcceptsAConfigurationWithOneOfItsCertificates:ssl-cert-nickname: no-such-certtogether withserver-certis accepted;httpAppliedChangeWithoutAKeyKeepsServingUntilTheRestart: covers a changed nickname, and a key store emptied under an unchanged configuration, where nothing but the missing key asks for administrative action. The result isSUCCESSand carriesadminActionRequired, the warning and the reason. The handler keeps serving TLS for 3 s, and noDisablingis logged;httpHandlerDisabledAtItsStartStaysDownOnAChangeStillWithoutAKey: the port stays closed for 3 s after the apply;httpHandlerDisabledAtItsStartStartsOnAChangeThatGivesItAKey: the check accepts a change toserver-cert, and after the apply the handler serves TLS.Results:
against the
HTTPConnectionHandlerof the head of [#1109] Keep a connection handler listening when a change to it is rejected #1110, 9 of the 11 new cases fail, each for the reason it pins:The case with one of the aliases present and the case with a key given back pass there too, because they pin behaviour that already exists. With the change, 24/24 pass, and
HTTPConnectionHandlerTestCasepasses;14 mutants of
HTTPConnectionHandler, each run as a separate class run, are each caught:enabledtaken from the configuration, disabling at once as the LDAP handlers do, no warning in the result, no administrative action (caught only by the emptied key store case), no reasons in the result, andDisablinglogged;friendlyNamein place ofconfig.name()in each of the three reasons (caught by the new-instance cases);packagewithattach-javadocs(doclint) passes.Once,
handlerWithoutItsCertificateDoesNotListen[LDAP2]failed withAddress already in useon the portfindFreePorthad returned. No case of the class held that port, and it passed on the rerun. That is the known race of the port scan, not this change.Related
listen-address.