Ca certificate reload#3568
Open
afreof wants to merge 6 commits into
Open
Conversation
|
@ralight Any hints on when this can be merged? The fix looks pretty simple and straightforward. We are experiencing similar issues: Certificate rotation requires a restart, which results into all connections being dropped. Need a softer way to handle this. |
afreof
force-pushed
the
ca-certificate-reload
branch
from
May 18, 2026 10:05
48303ab to
b5b9ac9
Compare
Author
|
Rebased on top of latest commit from master branch. While doing this, I tried to adapt the code to follow the new conventions for tests which got merged 2 days ago. I hope this simplifies the re-view and allows to merge it soon. |
ralight
force-pushed
the
master
branch
2 times, most recently
from
May 18, 2026 23:07
73e732b to
db988a6
Compare
afreof
force-pushed
the
ca-certificate-reload
branch
from
May 22, 2026 06:32
b5b9ac9 to
01c523b
Compare
added 6 commits
June 1, 2026 10:22
OpenSSL 3.x on some distributions (e.g. Fedora 42) does not ship engine.h as part of openssl-devel because the ENGINE API is deprecated. When the header is absent, the OpenSSL configuration header defines OPENSSL_NO_ENGINE to signal that the ENGINE API is unavailable. Three files in lib/ included <openssl/engine.h> unconditionally inside #ifdef WITH_TLS blocks, causing a fatal build error on those systems: lib/tls_mosq.h lib/net_mosq.c lib/options.c src/net.c already guarded its engine usage correctly with Apply the same OPENSSL_NO_ENGINE guard to the three lib/ files. In options.c, where no other OpenSSL header was included before the engine.h line, add <openssl/opensslconf.h> first so that the macro is defined before it is tested. Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
listeners__reload_all_certificates() called only net__load_certificates() on the existing SSL_CTX without calling net__tls_load_verify(). As a result, any rotation of the CA bundle (cafile/capath) had no effect until a full broker restart: clients whose certificate was signed by the new CA were rejected, and clients whose certificate was signed by a revoked CA continued to be accepted. Fix by recreating the SSL_CTX from scratch: net__tls_server_ctx() frees and reallocates a fresh context, then net__tls_load_verify() loads CA certs and CRLs into it. As a secondary benefit this also stops a resource leak: net__load_crl_file() calls X509_STORE_add_lookup() on every reload, which appends a new X509_LOOKUP_file() entry to the store's internal lookup-methods stack. There is no public API to remove entries, so each SIGHUP grew the stack by one. (CRL objects themselves are not duplicated because X509_STORE_add_crl() deduplicates by issuer name.) Existing TLS connections are unaffected: SSL objects hold a reference to their parent SSL_CTX, so the old context is kept alive until the last connection derived from it is closed. Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
This test covers the fix in the previous commit
("tls: fix stale CA store not updated on SIGHUP certificate reload")
and fails without it.
Without the SSL_CTX recreation fix, swapping the cafile symlink and
sending SIGHUP leaves the broker using the old CA store. Clients whose
certificate is signed by the old CA continue to connect successfully
even though the cafile now points at an entirely unrelated root CA.
The test atomically replaces a cafile symlink with one pointing at
test-fake-root-ca.crt (a self-signed root with no relation to the
test PKI used to issue client.crt) and then confirms that a subsequent
TLS handshake with client.crt is rejected.
Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
Add 08-ssl-hup-reload.py as a regression guard for properties that must hold across SIGHUP reloads and could be broken by a careless implementation of the SSL_CTX recreation introduced by the fix in "tls: fix stale CA store not updated on SIGHUP certificate reload". The test passes both before and after that fix; its role is to catch regressions introduced by it or future changes to the reload path. The two scenarios covered had no dedicated test before: 1. After SIGHUP, new TLS connections with a valid client certificate are still accepted. The existing 08-ssl-hup-disconnect.py only verifies that an already-established connection is not dropped; it does not exercise a fresh TLS handshake against the reloaded SSL_CTX. 2. After SIGHUP, revoked certificates (crlfile) are still rejected. The existing CRL tests (08-ssl-connect-cert-auth-crl.py, 08-ssl-connect-cert-auth-revoked.py) never send a SIGHUP, so they do not verify that the CRL store survives the reload. The test repeats the SIGHUP cycle three times to catch any accumulation behaviour that could degrade subsequent reloads. Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
…eloads Add 08-ssl-hup-chain.py as a regression guard for chain correctness across SIGHUP reloads. The test passes both before and after the fix in "tls: fix stale CA store not updated on SIGHUP certificate reload"; its role is to catch regressions that the fix or future changes to the SSL_CTX recreation path could introduce, such as presenting the wrong leaf certificate or accumulating duplicate intermediate entries. Configure a certfile containing both the leaf (server.crt) and the intermediate signing CA (test-signing-ca.crt). After each SIGHUP the test uses openssl s_client -showcerts to extract every certificate sent by the broker and compares their SHA-256 fingerprints against the expected values: - chain[0] must be server.crt (correct leaf, not stale or wrong cert) - chain[1] must be test-signing-ca.crt (correct intermediate) - no further certificates (no accumulation of duplicate intermediates) Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
Before the SSL_CTX recreation fix, only certfile and keyfile were effectively reloaded on SIGHUP. The mosquitto.conf(5) man page reflected this: cafile, capath, and crlfile had no reload note, while certfile mentioned only itself. Now that net__tls_load_verify() is called on every reload, all four options take effect on SIGHUP. Update the man page accordingly: - cafile, capath, crlfile: add 'Reloaded when Mosquitto receives a SIGHUP signal.' - certfile: broaden the existing note to mention keyfile, cafile, and crlfile alongside certfile. Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
afreof
force-pushed
the
ca-certificate-reload
branch
from
June 1, 2026 08:23
01c523b to
b9d1283
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.
Fix stale CA store and CRL not reloaded on SIGHUP certificate reload
listeners__reload_all_certificates() called only net__load_certificates()
on the existing SSL_CTX, without calling net__tls_load_verify(). This meant
that cafile/capath and crlfile were silently ignored on SIGHUP: CA cert
rotations required a full broker restart to take effect, and each reload
leaked an X509_LOOKUP instance into the store's internal lookup-methods stack.
The fix recreates the SSL_CTX from scratch (net__tls_server_ctx() followed by
net__tls_load_verify()) so that all four TLS options — certfile, keyfile,
cafile/capath, and crlfile — are fully reloaded on every SIGHUP. Existing
connections are unaffected because SSL objects hold their own reference to
the old SSL_CTX until they close.
The man page is updated to document that cafile, capath, and crlfile are now
reloaded on SIGHUP (only certfile had this note before).
Tests added:
symlink swap and verifies that client.crt is rejected after SIGHUP.
are still rejected across 3 SIGHUP cycles (CRL survives SSL_CTX recreation).
(correct leaf + intermediate fingerprints) across SIGHUP reloads.
Then please check the following list of things we ask for in your pull request:
make testwith your changes locally?