WL#17271: Dynamic reload of TLS certificates in Router
Affects: MySQL-Router-26.x
—
Status: Complete
Description
-----------
MySQL Router reads TLS material when its plugins start. Replacing or renewing a
certificate, private key, CA file, or CRL therefore currently requires a
Router restart before new TLS handshakes use the replacement files. A restart
disconnects all active client sessions. Administrators need to renew or replace
this material without an outage, while Router retains its known-good TLS state
when replacement files are incomplete or invalid.
User stories
------------
* As a Router administrator, I can replace TLS files at their configured
locations and request a reload without restarting Router.
* As a Router administrator, I can use the same `mysqlrouter --reload-tls`
command on supported platforms, while supplying the platform-specific target
of the running Router instance.
* As a connected client, my existing connection is not disconnected by a TLS
reload. A subsequently established TLS connection uses the replacement
material after a successful reload.
Scope
-----
This worklog adds an asynchronous TLS-material reload to production Router
runtime components that own a TLS context or a configured TLS client state,
including:
* the `routing` plugin for inbound client-to-Router TLS termination;
* the `routing` plugin for outbound Router-to-destination TLS connections;
* the HTTPS listener provided by `http_server`; and
* `metadata_cache` connections that use configured client certificate, key,
CA, or CRL files.
For each covered component, reload rereads only the configured certificate,
private key, CA, CA directory, CRL, and CRL directory paths. A component may
have no configured material of a given type; this is not an error. Reload uses
the paths accepted at Router startup and does not reread or apply the Router
configuration file.
Example: certificate replacement
--------------------------------
Router is configured with stable paths below a `current` symbolic link, for
example:
[routing:example]
client_ssl_cert=/etc/mysqlrouter/tls/current/router-cert.pem
client_ssl_key=/etc/mysqlrouter/tls/current/router-key.pem
The `current` link points to the active date-versioned directory, for example:
/etc/mysqlrouter/tls/current -> /etc/mysqlrouter/tls/2026-07-20
To renew the certificate, the administrator creates a new dated directory with
the replacement files, changes `current` to point to that directory, and then
requests reload:
mysqlrouter --reload-tls --pid-file=/run/mysqlrouter/mysqlrouter.pid
The Router service account must be able to read the replacement files. If the
replacement material is invalid, the reload fails for the affected TLS owner
and Router continues to use its previously loaded TLS state.
Out of scope
------------
The following are outside the scope of this worklog:
* rereading `mysqlrouter.conf` or changing configured TLS paths, TLS modes,
cipher suites, TLS versions, listener addresses, ports, or plugin loading;
* enabling TLS for a component that was configured without it;
* changing Router configuration through a file watcher;
* modifying TLS passthrough connections, for which Router does not terminate
TLS or own a TLS context;
* reloading `mock_server`, which is a test tool and not a production Router
runtime component; and
* changing existing client or server connections. Existing sessions continue
to use the TLS state selected when their handshakes completed.
Dependencies
------------
The implementation depends on each covered TLS owner being able to construct
and validate replacement TLS state before making it visible to new
connections. The Windows implementation depends on the Router Windows Service
runtime and the Windows named-event APIs.
Functional and Non-Functional Requirements ========================================== Reload request and public interface ----------------------------------- FR1 - MySQL Router SHALL provide `--reload-tls` as the command-line operation for requesting a TLS-material reload from a running Router instance without starting another Router instance. FR1.1 - On POSIX platforms, `--reload-tls` SHALL require exactly one of `--pid=PID` and `--pid-file=PATH`. FR1.2 - `--pid` SHALL accept a positive decimal process identifier. FR1.3 - `--pid-file` SHALL name a readable file containing one positive decimal process identifier. FR1.4 - On Windows, `--reload-tls` SHALL accept `--service-name=NAME`. If the option is omitted, it SHALL target the default MySQL Router service name. FR1.5 - Options applicable only to another platform, both POSIX target options together, or a missing required POSIX target option SHALL cause the command to fail without requesting a reload. FR1.6 - The command SHALL return success after the reload request is delivered to the target Router. It SHALL not wait for TLS-file I/O or report the result of the asynchronous reload operation. FR1.7 - If the target cannot be found, the PID is invalid, the PID file cannot be read, the caller lacks permission, or the Windows service reload event cannot be opened, the command SHALL fail with a diagnostic identifying the failed operation. FR2 - Router already uses `SIGHUP` on POSIX platforms to reopen log files. This worklog SHALL extend that signal handling to also request a TLS-material reload. FR2.1 - A TLS reload failure caused by `SIGHUP` SHALL not request Router shutdown. This exception applies only to TLS reload; existing fatal handling of a log reopen failure is unchanged. FR3 - On Windows, a Router instance running as a Windows service SHALL expose a named event dedicated to TLS reload. FR3.1 - The event name SHALL identify the target Router service instance. FR3.2 - The event SHALL be separate from service start and shutdown events. FR3.3 - Only principals authorized to administer the target service SHALL be granted permission to signal the event. TLS reload behavior ------------------- FR4 - A TLS reload SHALL reread only TLS material from paths accepted by the running Router configuration at startup. It SHALL not reload the Router configuration file. FR5 - Each covered production TLS owner SHALL independently attempt to reload its configured certificate, private key, CA, CA directory, CRL, and CRL directory material, as applicable. FR5.1 - A TLS owner SHALL validate all replacement material required for the replacement context before making any of that context available to new connections. FR5.2 - If validation or loading fails for a TLS owner, that owner SHALL retain its previous usable TLS state and SHALL log an error identifying the owner and the failure. FR5.3 - A failure in one TLS owner SHALL not prevent other TLS owners from attempting their reloads. FR6 - After a TLS owner successfully reloads, new TLS handshakes or new outbound TLS connections created by that owner SHALL use its replacement TLS state. FR6.1 - Router SHALL not terminate, renegotiate, or otherwise alter existing client connections, existing outbound connections, or handshakes already in progress because of a TLS reload. FR6.2 - Reused outbound connections may continue to use their pre-reload TLS state until they are closed and a replacement connection is created. FR7 - Router SHALL serialize TLS reload execution. FR7.1 - Multiple requests received before a pending reload begins may be coalesced into one reload. FR7.2 - If a request is received while a reload is active, Router SHALL perform another reload after the active reload completes. Packaging and service-manager interfaces ----------------------------------------- FR8 - The packaged systemd unit SHALL implement `systemctl reload mysqlrouter.service` by invoking `mysqlrouter --reload-tls` for the service main process. FR9 - The packaged SysV init script `reload` action SHALL invoke `mysqlrouter --reload-tls --pid-file=PATH`, using the configured Router PID file, rather than stopping and starting Router. Non-Functional Requirements --------------------------- NF1 - Signal handling, Windows service-control callbacks, named-event notification, and CLI request delivery SHALL not synchronously load or validate TLS material. NF2 - The implementation SHALL keep a previous TLS context alive until all connections and handshakes that already acquired it no longer use it. NF3 - TLS reload activity, including successful completion and per-owner failure, SHALL be logged without exposing private-key contents. NF4 - Default Router startup and runtime behavior is unchanged until a reload request is received.
High-Level Specification / Interface Specification
===================================================
Overview
--------
MySQL Router adds a process-wide TLS reload coordinator. TLS owners register
a reload callback when their runtime state is initialized. A reload request is
accepted quickly and processed asynchronously by the coordinator. Each owner
constructs replacement TLS state from its startup configuration, validates it,
and publishes it only after validation succeeds.
The coordinator is independent from the existing log reopen worker. On POSIX,
a single `SIGHUP` request schedules both operations.
TLS owner matrix
----------------
| Component | TLS direction | Current TLS state | Reloaded material | Applies to
|
|---|---|---|---|---|
| `routing` | client to Router | inbound server TLS context | Router server
certificate/key and configured trust material | new client handshakes |
| `routing` | Router to destination | destination client TLS contexts | Router
client certificate/key and configured CA/CRL material | new destination
connections |
| `http_server` | HTTPS client to Router | server TLS context | HTTPS
certificate/key and configured trust material | new HTTPS handshakes |
| `metadata_cache` | Router to metadata server | client-library TLS state |
configured client certificate/key and trust material | newly created metadata
connections |
TLS passthrough is excluded because Router does not own its TLS state. The
`mock_server` test tool is excluded from this feature.
Command-line interface
----------------------
The new command-line operation is:
mysqlrouter --reload-tls [target-option]
POSIX target options are:
mysqlrouter --reload-tls --pid=PID
mysqlrouter --reload-tls --pid-file=PATH
The command sends `SIGHUP` to the target process. It does not parse
`mysqlrouter.conf` and does not start a Router instance.
Windows target option is:
mysqlrouter --reload-tls --service-name=NAME
When no service name is supplied, the command uses the default Router service
name. The command opens the named TLS reload event owned by that service and
signals it. The command succeeds when the request was delivered, not when the
background reload finishes.
POSIX signal interface
----------------------
Router already blocks ordinary signals in worker threads and processes
`SIGHUP` in its signal-handler thread. Its signal-handler map allows one
callback per signal number. The `SIGHUP` callback therefore schedules both:
* the existing log reopen request; and
* a TLS reload coordinator request.
The callback must not perform TLS I/O. The TLS coordinator worker coalesces
pending requests and guarantees a further reload if another request arrives
while a reload is active.
Windows service interface
-------------------------
When Router runs as a Windows service, service startup creates a named,
manual-reset TLS reload event that is distinct from the existing service exit
event. Its name is derived from the service name, allowing independently
installed Router services to be targeted separately. The event security
descriptor grants signal access only to authorized service administrators.
The reload coordinator waits for this event and resets it after accepting a
request. Service shutdown wakes and joins the coordinator before closing the
event handle.
Runtime behavior
----------------
For a TLS owner, reload is a prepare-and-publish operation:
1. Read the configured TLS files from the paths captured at Router startup.
2. Build a replacement TLS context or equivalent client TLS state.
3. Validate the replacement material, including certificate/key compatibility
and all configured trust material.
4. Atomically publish the replacement for new handshakes or new connections.
5. Retain the previous state until no existing operation uses it.
If any preparation step fails, the owner retains its old state and logs the
error. Other owners continue with their own reload attempts.
Packaging integration
---------------------
The systemd unit adds an `ExecReload` command that invokes
`mysqlrouter --reload-tls --pid=$MAINPID`. The command delivers `SIGHUP` to
the main Router process. This makes the following command the supported
systemd interface:
systemctl reload mysqlrouter.service
The existing systemd logrotate rule may retain its direct `SIGHUP` delivery or
use `systemctl reload`; both request log reopen and TLS reload.
The SysV init script implements `reload` and `force-reload` with
`mysqlrouter --reload-tls --pid-file=PATH`, rather than the current stop/start
behavior. The Windows MSI does not need to install a service automatically:
Router already provides service installation commands. It ships the new
`--reload-tls` CLI operation and its documentation.
Low-Level Design Specification ============================== Lifecycle and coordinator ------------------------- The TLS reload coordinator is initialized after all Router plugins have started, when TLS owners have initialized their state. It is stopped and joined before plugin TLS state is destroyed. `router_app.cc` currently creates `LogReopenComponent` after plugin startup, registers `SIGHUP`, and removes the handler after the first plugin finishes. The TLS coordinator follows those lifetime boundaries but is separate from `LogReopenComponent`: log-reopen and TLS-reload failures have different shutdown semantics. `SignalHandler` stores one callback per signal number. The existing `SIGHUP` callback must request both workers; a second callback cannot be registered. Reload registration contract ---------------------------- Each covered TLS owner registers a callback with the coordinator. The callback returns a success/failure result and a loggable error message. The owner keeps its effective startup TLS settings, including paths and options needed to construct an equivalent replacement context. The coordinator runs callbacks outside its request-state lock. It logs each owner result and continues after a callback failure. Request coalescing ------------------ The coordinator has `idle`, `requested`, `active`, and `shutdown` states. * A request in `idle` changes state to `requested` and wakes the worker. * The worker changes `requested` to `active` and runs one reload pass. * A request in `active` records a required follow-up pass. * After a pass, the worker runs the follow-up pass when requested; otherwise, it returns to `idle`. * Shutdown prevents new work and joins the worker. Unlike the existing log reopen coalescing rule, TLS reload cannot discard a request received during an active pass. This prevents a completed file replacement from being missed. TLS context publication ----------------------- `routing_plugin.cc` creates inbound `TlsServerContext` objects and stores `DestinationTlsContext` instances used to construct destination client contexts. `http_server_plugin.cc` creates the HTTPS `TlsServerContext` owned by its HTTP server context. These owners must use lifetime-safe shared references or equivalent synchronization for context replacement. A replacement context is completely configured before publication. Loading a certificate or key into the currently published context is not permitted. Publication occurs under owner synchronization. Existing operations retain the prior context; new operations obtain the newly published context. For destination context caches, reload invalidates cached client contexts only after replacement configuration is validated. New destination connections then obtain contexts from the new configuration. Existing destination connections are not closed. MySQL client channels --------------------- The `metadata_cache` client-library TLS state retains its startup TLS file settings and applies them to newly created metadata connections after reload. The design does not force established metadata connections to reconnect. Windows details --------------- `nt_servc.cc` creates an unnamed manual-reset `hExitEvent` used only to end `ServiceMain`. It must not be reused for reload. The service adds a separate, named, manual-reset event and a handle for TLS reload. The event name includes the service name. Its security descriptor grants `EVENT_MODIFY_STATE` only to administrators and authorized service-management principals. The service closes the reload event only after the coordinator worker exits. The CLI uses `OpenEvent(EVENT_MODIFY_STATE, ...)` and `SetEvent()`. Packaging changes ----------------- The following existing files are affected by the design: * the Router systemd unit gains `ExecReload` using `mysqlrouter --reload-tls --pid=$MAINPID`; * `support-files/mysqlrouter-log-rotate-systemd.in` may use `systemctl reload` instead of a direct `systemctl kill -s HUP`; * `support-files/mysqlrouter-log-rotate.in` continues to use the PID file and `SIGHUP`; and * `packaging/deb-in/mysql-router-packagesource.mysqlrouter.init` changes `reload` and `force-reload` from stop/start to `mysqlrouter --reload-tls --pid-file=PATH`. Test plan --------- Component and integration tests must cover: * a valid replacement certificate/key pair, where a new inbound TLS connection observes the replacement certificate; * configured CA and CRL reload where applicable; * unreadable, missing, malformed, and certificate/key-mismatched replacement material, where new connections continue to use the old context; * existing inbound and outbound TLS connections remaining usable across reload; * new routing destination and MySQL client connections using replacement material; * independent results from multiple TLS owners; * multiple requests while reload is active, including a replacement between the two passes; * `SIGHUP` reopening a rotated log file while TLS reload is requested; * POSIX CLI validation, direct PID delivery, PID-file delivery, and failure diagnostics; * systemd `reload` and SysV `reload`/`force-reload`; and * Windows event naming, permission failure, custom service name, delivery, and cleanup during service shutdown.
Copyright (c) 2000, 2026, Oracle Corporation and/or its affiliates. All rights reserved.