fix(security): require accounts on the replica MySQL listener - #105
fix(security): require accounts on the replica MySQL listener#105HelgeSverre wants to merge 1 commit into
Conversation
elyrasql replica had no auth flags at all and always started its MySQL listener with open authentication: any username/password logged in as Admin with full read access to the replicated (often production) data set. serve mode already defaulted to 127.0.0.1 with a safe-by-default refusal for remote binds; replicas are typically part of real deployments, so a credential-less default there is a much sharper edge. - Add --user/--password/--auth USER:PASS:ROLE to the replica subcommand, mirroring serve. - Refuse to start a credential-less replica listener unless ELYRASQL_ALLOW_OPEN_AUTH=1 explicitly opts in. - Make elyra_server::env_flag public so the CLI reuses the same truthy parsing as the library guards. - Add CLI tests pinning both the refusal and authenticated startup.
Three independent findings on the replication surface, sequenced and with their mutual conflicts resolved. Supersedes #103, #104 and #105. The replication endpoint handed a full copy of the database to any peer that connected, with no handshake -- reproduced against 1.9.8 on a loopback bind, where zero bytes sent returned the canary row. The old guard only covered non-loopback binds. A replica never verified its primary's identity, so anything answering on the primary's address could inject fabricated rows, even with ELYRASQL_CLUSTER_SECRET set: authentication ran in one direction only. Replication auth is now mutual, and auth_accept runs before auth_respond_to_peer so the responder cannot be used as an oracle by a peer that does not already know the secret. `elyrasql replica` had no auth flags at all and always started its MySQL listener with open authentication over replicated production data. It now takes --user/--password/--auth like `serve` and refuses to start credential-less. Verified: the endpoint refuses to start without a secret and its port does not listen; with a secret, an unauthenticated peer receives the 16-byte challenge and no data. 448/448 tests. Co-authored-by: Helge Sverre <helge.sverre@gmail.com>
|
Merged as part of #107 ( Consolidated rather than merged one by one for two reasons. Actions never ran on any of the three. They conflicted with each other, not just with One genuine interaction needed a commit of its own: I reproduced the #103 finding against 1.9.8 before touching anything: zero bytes sent to the replication port, 560 bytes back with the canary row in them. Verified closed both ways afterwards — without a secret the endpoint refuses to start and the port does not listen; with one, an unauthenticated peer gets the 16-byte challenge and nothing else. Thanks — this was a real hole in a shipped release. |
## Summary **A security release.** Three independent holes in the replication surface (#107, superseding #103–#105), all reachable in 1.9.8. The first needs no credentials and no handshake: connecting to the replication port returned a full copy of the database. Reproduced against 1.9.8 — zero bytes sent, 560 bytes back with the canary row in them. The guard only covered non-loopback binds, so `--replication-listen 127.0.0.1:...` without a cluster secret was readable by any local process. ## The upgrade note is a `danger` block, not a `warning` Because the fixes are deliberately breaking and the exposure is real: - a primary with `--replication-listen` and a replica **both** now require `ELYRASQL_CLUSTER_SECRET` - **primary and replica must be upgraded together** — the handshake gained a step, so a 1.9.9 replica will not accept a 1.9.8 primary - a replica now needs accounts (`--user`/`--password`/`--auth`) - a refused endpoint **keeps the server running**, so `replication endpoint stopped` in the log is the only signal that replication is not happening — the note says to watch for it rather than assume - anyone who ran an exposed endpoint should rotate what the data would have revealed: the port left no access log `ELYRASQL_ALLOW_OPEN_AUTH=1` opts out of all four, described as the honest way to say "this port is on a network I control" rather than as a convenience. ## The three things the release workflow does not validate Per `CONTRIBUTING.md`: `SERVER_VERSION` → `8.0.12-ElyraSQL-1.9.9` (confirmed with `elyrasql version`), `testbench/sql-dump/Cargo.lock` refreshed with `cargo metadata --locked` green in both workspaces, and the version strings in README, installation, deployment and mysql-compatibility. The `Upgrading to 1.9.8` block keeps its title, and the 1.9.8 changelog section is byte-identical (verified by digest). ## Testing - [x] `cargo fmt --all --check` - [x] `cargo clippy --workspace --all-targets --all-features --locked -- -D warnings` - [x] `cargo nextest run --workspace --locked` — 448/448 - [x] `cargo metadata --locked` in both workspaces - [x] Verified against a MySQL client The fix itself was verified both ways before merging: without a secret the endpoint refuses to start and its port does not listen; with one, an unauthenticated peer receives the 16-byte challenge and no data. ## Checklist - [x] Docs updated under `docs/` - [x] No internal engine/dependency names leak into SQL, errors, CLI, or the wire handshake - [x] Limitations documented honestly - [x] Scoped to a single logical change
Summary
elyrasql replicahad no auth flags at all and always started its MySQL listener with open authentication — any username/password logged in asAdmin, with full read access to the replicated (often production) data set:servemode defaults to127.0.0.1with a safe-by-default refusal for remote binds, but replicas are typically part of real deployments and there was no way to configure accounts at all.Fix
--user/--password/--auth USER:PASS:ROLEto thereplicasubcommand, mirroringserve.ELYRASQL_ALLOW_OPEN_AUTH=1explicitly opts in.elyra_server::env_flagpublic so the CLI reuses the library's truthy-value parsing (1/true/yes/on).docs/security.md,docs/replication.md, CHANGELOG.Breaking change
Scripts starting replicas without accounts must add auth flags or set
ELYRASQL_ALLOW_OPEN_AUTH=1. The refusal happens before any file I/O, so nothing is wiped or half-started.Verification
cargo test -p elyra-cli— 5 passed (2 new:replica_refuses_credential_less_listener,replica_starts_with_configured_accounts)cargo clippy --workspace --all-targets -- -D warningscleancargo fmt --all -- --checkclean