fix(installer): the bundled database rejected connections it should have accepted

Two defects in the stage 0 bootstrap, both surfacing as "Access denied" on a
server where the operator was holding the correct password.

CREATE USER IF NOT EXISTS is a no-op on an existing user - it does NOT change
the password. Stage 0 generates a fresh password every run and overwrites
.dbpass with it unconditionally, so any path that re-runs the bootstrap over an
existing account left the handoff holding a password the server had never been
told. ALTER USER now follows each CREATE, so the stored password and the handoff
always agree.

The user was also only created for 'localhost' and '127.0.0.1'. On current
Windows, 'localhost' resolves to the IPv6 loopback FIRST, so an operator who
types localhost rather than 127.0.0.1 arrives as '<user>'@'::1' - an account
that did not exist - and MySQL answers "Access denied" naming a host they never
typed. The ::1 account is now created and granted alongside the other two.

Note the datadir guard means the first defect could not fire on a straightforward
re-run - stage 0 refuses a non-empty data directory before reaching the
bootstrap. It was still wrong, and reachable once the directory has been cleared
by hand, which is what the failure message tells operators to do.
This commit is contained in:
cproudlock
2026-08-04 12:59:45 -04:00
parent 189a474082
commit fe091e751a

View File

@@ -786,8 +786,23 @@ data, STOP: you are about to destroy a database.
CREATE DATABASE IF NOT EXISTS $DbName CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '$DbUser'@'localhost' IDENTIFIED BY '$appPass';
CREATE USER IF NOT EXISTS '$DbUser'@'127.0.0.1' IDENTIFIED BY '$appPass';
-- ::1 as well. On current Windows 'localhost' resolves to the IPv6 loopback
-- FIRST, so an operator who types localhost instead of 127.0.0.1 arrives as
-- '$DbUser'@'::1' - an account that did not exist, and MySQL answers "Access
-- denied" naming a host the operator never typed.
CREATE USER IF NOT EXISTS '$DbUser'@'::1' IDENTIFIED BY '$appPass';
-- ALTER after CREATE, because CREATE USER IF NOT EXISTS is a NO-OP on an
-- existing user and does NOT change its password. This stage generates a fresh
-- password every run and overwrites .dbpass with it, so on any re-run after a
-- partial failure the file held a password the server had never been told -
-- "Access denied" while the operator was looking straight at the right file.
-- ALTER makes the stored password and the handoff agree, every time.
ALTER USER '$DbUser'@'localhost' IDENTIFIED BY '$appPass';
ALTER USER '$DbUser'@'127.0.0.1' IDENTIFIED BY '$appPass';
ALTER USER '$DbUser'@'::1' IDENTIFIED BY '$appPass';
GRANT ALL PRIVILEGES ON $DbName.* TO '$DbUser'@'localhost';
GRANT ALL PRIVILEGES ON $DbName.* TO '$DbUser'@'127.0.0.1';
GRANT ALL PRIVILEGES ON $DbName.* TO '$DbUser'@'::1';
ALTER USER 'root'@'localhost' IDENTIFIED BY '$rootPass';
FLUSH PRIVILEGES;
"@