You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Merge #424: fix(mysql): [#410] Fix multiple MySQL configuration issues
afabd0c chore(mysql): fix linting issues from Phase 5 (Jose Celano)
d465611 feat(mysql): move DSN to env var override with percent-encoding (Bug 1) (Jose Celano)
81ed6d1 feat(mysql): make root_password configurable, generate at creation time (Jose Celano)
2ec8f1e fix: [#410] reject 'root' as MySQL app username (Bug 3) (Jose Celano)
Pull request description:
## Summary
Fixes three related MySQL configuration bugs discovered during the Hetzner demo deployment (#405).
Closes#410
---
## Bug 1 — MySQL DSN hardcoded in `tracker.toml` with no URL-encoding
**Problem**: The tracker config template interpolated raw credentials directly into the DSN URL. Passwords with URL-reserved characters (`@`, `:`, `/`, `+`) produced malformed DSNs and broke MySQL connectivity. The plaintext password also ended up in a mounted config file instead of an env var.
**Fix**:
- Added `percent-encoding` crate; built a custom `USERINFO_ENCODE` `AsciiSet` that preserves RFC 3986 unreserved chars (so `tracker_user` is not over-encoded)
- DSN is now percent-encoded in Rust (`EnvContext::new_with_mysql`) and stored as `TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__PATH` in `.env`
- `docker-compose.yml.tera` injects the new env var into the tracker container
- `tracker.toml.tera` no longer contains the DSN; a static comment explains the injection
- `MysqlTemplateConfig` removed entirely
---
## Bug 2 — MySQL root password not configurable
**Problem**: Root password was unconditionally derived as `{app_password}_root`, giving users no way to supply their own value and making it entirely predictable.
**Fix**:
- Added optional `root_password` field to the environment config JSON schema
- Added `generate_random_password()` in `src/shared/secrets/random.rs` (32-char, mixed charset, satisfies MySQL MEDIUM policy)
- Password is generated **once at environment creation time** (in `TryFrom<DatabaseSection>`), not at render time, so it stays stable across re-renders
- `create_mysql_contexts` no longer contains any derivation logic
---
## Bug 3 — No validation that MySQL app username is not `"root"`
**Problem**: The MySQL Docker image rejects `MYSQL_USER=root`, but the deployer only validated for an empty username, deferring the error to Docker startup time.
**Fix**:
- Added `ReservedUsername` variant to `MysqlConfigError` with an actionable `help()` message
- `MysqlConfig::new()` rejects `"root"` immediately with a clear error
---
## Testing
- Added unit tests for all three fixes
- All 2314 tests pass (`cargo test`)
- Full E2E LXD deployment verified with password `tracker_p@ss!word#1` — confirmed `@` → `%40`, `#` → `%23` in the rendered `.env`
- All linters pass (`cargo run --bin linter all`)
- `cargo machete` reports no unused dependencies
- Pre-commit checks pass (`./scripts/pre-commit.sh`)
---
## Checklist
- [x] Pre-commit checks pass
- [x] `MysqlConfig::new()` rejects `"root"` with `ReservedUsername` error
- [x] `root_password` is configurable and generated at creation time when omitted
- [x] DSN is percent-encoded and injected via env var override
- [x] `tracker.toml` no longer contains the database password
- [x] `cargo machete` reports no unused dependencies
ACKs for top commit:
josecelano:
ACK afabd0c
Tree-SHA512: 9af00556e7be392f909978729f3f27715ea0d9112870db1cb6a6122505b1e1e689aefc02aa28db3bc26c7f7f729c1430c37b2c85dbe0fd6566421a708d521313
Copy file name to clipboardExpand all lines: schemas/environment-config.json
+9-1Lines changed: 9 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -145,6 +145,14 @@
145
145
"username": {
146
146
"description": "Database username",
147
147
"type": "string"
148
+
},
149
+
"root_password": {
150
+
"description": "Optional `MySQL` root password\n\nWhen provided, used as `MYSQL_ROOT_PASSWORD` in the rendered `.env` file.\nWhen absent, a cryptographically random password is generated at render time.\nNever set this to the same value as `password` in production environments.",
0 commit comments