feat: support hashed_password users (mysql_native_password)#29
Open
takaidohigasi wants to merge 2 commits into
Open
feat: support hashed_password users (mysql_native_password)#29takaidohigasi wants to merge 2 commits into
takaidohigasi wants to merge 2 commits into
Conversation
Adds an alternative authentication mode for users whose plaintext
password the proxy never sees — typically personal accounts mirrored
from another proxy's config that stores only the MySQL
`*XXXXXXXX...` (mysql_native_password) hash.
API:
proxy:
users:
- username: "personal_alice"
hashed_password: "*4DF1D66463C18D44E3B001A8FB1BBFBEA13E27FC"
Wiring:
* go.mod: replace github.com/go-mysql-org/go-mysql with
github.com/takaidohigasi/go-mysql at the commit hosting PR #1129
(server: support pre-computed hashed passwords in Credential). The
fork only touches server/ files (Credential, InMemoryAuthenticationHandler.AddUserWithHashedPassword,
HashedPassword value type with shape validation); the client path
the interceptor uses for outbound backend connections is
unaffected. Drop the replace once upstream merges + tags.
* internal/config: new UserConfig.HashedPassword field. Validator
requires exactly one of Password / HashedPassword per entry, and
shape-checks HashedPassword (must be "*" + 40 hex chars) at load
time so typos fail fast instead of surfacing as login errors hours
later.
* internal/proxy/server.go: register hashed users via the fork's
AddUserWithHashedPassword. mysql.DecodePasswordHex strips the "*"
and decodes the standard MySQL stored form. Hashed users are
intentionally OMITTED from userPasswords so the existing
handleConnection lookup surfaces them as "missing entry" at
backend-connect time, with a sharpened error message pointing
operators at the limitation.
Scope deliberately limited:
* Inbound handshake only. The proxy's outbound backend connection
still requires plaintext. A hashed-only user authenticates
successfully but the session terminates at backend-connect with
a clear error rather than running queries. Granting hashed-only
users true query access would need either (a) go-mysql client-
side hash auth (would let us reuse the hash for the outbound
connect), or (b) a per-user `backend_password` override field.
Both are tracked as follow-ups; this PR unblocks audit/auth-only
use cases (personal users mirrored into config that no longer
fail config validation, and whose auth attempts now appear in
audit logs instead of silently being rejected).
Tests:
* TestLoad_HashedPassword_Accepts: shape-correct value round-trips
through Load.
* TestLoad_HashedPassword_RejectsBoth: ambiguity rejection.
* TestLoad_HashedPassword_RejectsMalformed: 4 typo categories
(missing leading "*", too short, too long, non-hex).
* TestUsersWiring "hashed-password users skip userPasswords":
hashed users register on the auth handler but stay absent from
userPasswords, preserving the load-bearing miss-on-lookup that
drives the clear-error path.
Verified locally:
gofmt -l . (clean)
go vet ./... (clean)
go build ./... (clean)
go test -race -count=1 ./... (ok across all packages
including integration)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Adds a
hashed_passwordalternative topasswordforproxy.usersentries, so personal accounts whose plaintext the interceptor never sees can authenticate against the inbound handshake. The plaintext is no longer required for proxy-side auth.The hashed form is MySQL's standard mysql_native_password stored value (
*+ 40 hex chars =*+ SHA1(SHA1(plaintext)) hex-encoded), i.e. exactly whatmysql.user.authentication_stringcarries for that plugin.How
go.modadds areplacedirective pointing atgithub.com/takaidohigasi/go-mysql@0d8f39fd, which is the head of go-mysql-org/go-mysql#1129. The fork only touchesserver/files (Credential,InMemoryAuthenticationHandler.AddUserWithHashedPassword,HashedPasswordvalue type with shape validation); the client path the interceptor uses for backend connections is unaffected. Drop the replace once upstream merges + tags a release.internal/config/config.go— newUserConfig.HashedPasswordfield. Validator enforces "exactly one of password / hashed_password" per entry and shape-checks the hashed form at load time so typos surface immediately instead of as login errors hours later.internal/proxy/server.go— register hashed users via the fork'sAddUserWithHashedPassword;mysql.DecodePasswordHexdecodes the*XXXX...form to the underlying 20 bytes. Hashed users intentionally do not appear inuserPasswords, so the existing missing-entry lookup inhandleConnectionsurfaces them with a sharpened error message at backend-connect time.Scope deliberately limited
End-to-end query forwarding for hashed-only users would need either (a) go-mysql client-side hash-based auth (so we can reuse the hash for the outbound connect), or (b) a per-user
backend_passwordoverride field. Both are tracked as follow-ups; this PR unblocks audit-only use cases — personal users no longer break config validation, and their auth attempts now appear in audit logs instead of silently being rejected.Test plan
TestLoad_HashedPassword_Accepts— shape-correct value round-trips through Load.TestLoad_HashedPassword_RejectsBoth— both fields set fails fast.TestLoad_HashedPassword_RejectsMalformed— 4 typo categories (missing leading*, too short, too long, non-hex).TestUsersWiring"hashed-password users skip userPasswords" — registers on the auth handler but stays absent from userPasswords (load-bearing for the clear-error path).gofmt -l .cleango vet ./...cleango build ./...go test -race -count=1 ./...(full suite incl. integration)Follow-up to remove the replace directive
Once go-mysql-org/go-mysql#1129 merges and a tagged release ships, switch
go.modback to the upstream module + new tag and remove thereplaceline. The new exported API (HashedPassword,AddUserWithHashedPassword,Credential.HashedPasswords) is the same in upstream so no source changes here are needed.🤖 Generated with Claude Code