Repository navigation
fix(builder): the bootstrap Job never prints the clickhouse password - #51
Merged
Merged
Conversation
The migration image ran the golang-migrate CLI at v4.18.1, which reports a failed connection as `failed to open database, "<the whole URL>"`. The URL carries the credentials in its query (the only place clickhouse-go v1 reads them), so every failed run of a ClickHouse store's bootstrap Job wrote the password into the cluster's logs. - The image now downloads the migrate release this agent links (read from its build information), v4.19.1, which no longer quotes the URL on a failed connection; the CLI had drifted from the in-process library. - Everything migrate prints passes through a redaction filter in the entrypoint: the password itself and any `password=` value become `<redacted>`. v4.19.1's clickhouse driver still returns net/url's parse error, which quotes the whole URL. migrate's exit status is carried past the pipe, so a failed migration still fails the Job. - Deploy refuses an in-cluster address that is not a host:port, naming the address alone, instead of rendering a Job that would fail with that error. - The in-process DSN is built with an escaped query and a checked address, so clickhouse-go v1 can never fail to parse it and quote it in an error. Co-Authored-By: Claude Opus 5.5 <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.
What leaked, and where
The bootstrap Job this agent renders for a ClickHouse store logged the store's password whenever it failed to connect. Seen on a hosted cell: the Job for a ClickHouse store logged, on each failed attempt before it eventually completed,
Root cause, in this repo:
templates/builder/Dockerfile.tmpldownloaded the golang-migrate CLI v4.18.1 and its entrypoint hands it a URL with the credentials in the query (the only place clickhouse-go v1 reads them). v4.18.1'smigrate.Newwraps a faileddatabase.Openasfailed to open database, %qwith the whole URL. The agent's own in-process migrations already linked v4.19.1 (go.mod), so the CLI had drifted from the library.database/clickhouse/clickhouse.go,Open:url.Parse(dsn)then thetcprewrite handed to clickhouse-go v1) still returns net/url's parse error, which quotes the whole URL, password included, for an address that does not parse. Reproduced locally with the v4.19.1 CLI:parse "clickhouse://bad host:9000?username=…&password=<fake>&…": invalid character " " in host name.codefly run),nativeDSN/adminDSNcomposed the DSN bySprintfwith unescaped credentials; clickhouse-go v1'sopenreturns the sameurl.Parseerror, which the agent wraps into its logs.The fix
{{ .MigrateVersion }}, whichBuildreads from this agent's build information (thegithub.com/golang-migrate/migrate/v4it links), so the image and the in-process path run the same release, now v4.19.1. A pseudo-version or local replacement is refused by name.curl -fsSLso a missing release fails the build instead of feedingtaran error page.awk, which replaces the password value literally (read fromENVIRON, no regex or escape processing) and anypassword=query value with<redacted>. migrate's exit status is carried past the pipe on fd 3 (Ubuntu's dash and POSIX sh have nopipefail), so a failed migration still fails the Job. The migrate process is no longerexeced, soshis PID 1 in the Job's pod.checkAddress) naming only the address, so a render cannot hand the Job an address whose failure would quote the URL.nativeDSN(address, user, password, database)escapes the query withurl.Valuesand checks the address first;adminDSNuses it too. A password containing#,%or&is now carried correctly instead of corrupting the DSN.templates/agent/README.md.tmpl) describes the behaviour.Classification: a fix at the owner. This agent composes the URL and chooses the CLI that prints it. The residual upstream behaviour (golang-migrate's clickhouse
Openreturning a*url.Errorthat quotes the DSN) is contained here by the address check plus the filter. No upstream issue was filed.Reuse: the existing migrate CLI, the existing entrypoint and its variable carriers, the image's busybox
awk, and the Go build information. No new binary, dependency or mechanism.Tests
entrypoint_redaction_test.go:TestBootstrapEntrypointNeverPrintsThePasswordrenders the Dockerfile throughBuild, extracts its shell-formCMD, builds the migrate CLI at the linked release, and runs the entrypoint withsh -cagainst an unreachable server, an unparsable address, and a password that is a substring of the marker. It checks the output never contains the password, that the failure is still reported, and that the exit status is non-zero. It fails onmain(the unparsable-address case prints the fake password).TestMigrationImageRunsTheLinkedMigrateRelease: the Dockerfile downloads the releasego.modlinks. It fails onmain(v4.18.1 vs v4.19.1).TestDeployRefusesAnAddressTheMigrateURLCannotCarry: it fails with the check removed.TestNativeDSNNeverQuotesThePassword: an unparsable address is refused without the password, a password with URL metacharacters round-trips, and a ping error to an unreachable server does not carry it.Local:
go build ./...,go vet ./...,go mod tidy -diff,go test ./...all pass.End-to-end, by hand: I built the rendered recipe with
docker build(alpine 3.21, busyboxsh/awk, migrate 4.19.1) and ran it as uid 65534 with a read-only root filesystem:password=<redacted>, exit 1clickhouse/clickhouse-server:26.3with a wrong password:Authentication failed, no password, exit 11/u create_table, exit 0What I did not verify
MigrateVersionchange the Job's pod template, so the render names a new Job, which re-applies the (idempotent) migrations.password=query value. A password with"or\would reach a%q-quoted error escaped, and only thepassword=rule would catch it. Codefly-generated passwords do not contain these characters.&or#would not authenticate in the Job. This is pre-existing and out of scope here.🤖 Generated with Claude Code