Skip to content

feat(db): allow a provider to supply the database password - #1606

Open
Moustafa-Moustafa wants to merge 1 commit into
flatcar:mainfrom
Moustafa-Moustafa:feat/db-password-provider
Open

feat(db): allow a provider to supply the database password#1606
Moustafa-Moustafa wants to merge 1 commit into
flatcar:mainfrom
Moustafa-Moustafa:feat/db-password-provider

Conversation

@Moustafa-Moustafa

Copy link
Copy Markdown
Contributor

feat(db): allow a provider to supply the database password

Nebraska takes the database password from NEBRASKA_DB_URL. That string is fixed for the life of the process, so a deployment whose credential expires, such as an OAuth token from a cloud identity provider, has no in-process way to supply a new one: the first connection attempted after expiry fails, and so does every one after that.

DBPasswordProvider is an optional hook consulted before each physical connection. When one is installed the handle is opened through pgx's BeforeConnect callback, which runs for every new connection and receives a fresh copy of the connection config, so the provider can return a credential renewed since startup. How that credential is obtained is not Nebraska's concern, and no cloud SDK is added; go.mod is unchanged.

The callback runs before pgx applies connect_timeout, so the provider call is bounded by that same timeout. A provider that never answers fails the connection instead of holding up every one the pool is trying to open.

The provider is given the user the connection authenticates as, so a single provider can serve connections made as different roles. That is what a deployment needs if it ever runs migrations under a separate role, as #1575 proposes.

Deployments that install no provider are untouched. The plain sqlx.Open path runs exactly as before and the password keeps coming from NEBRASKA_DB_URL.

How to use

No new behavior is introduced for anyone who does not install a provider, so the first thing to validate is that an existing deployment is not impacted at all.

To use it, implement the interface and install it in cmd/nebraska/main.go before the database is created:

type tokenProvider struct{}

func (tokenProvider) DBPassword(ctx context.Context, user string) (string, error) {
    // fetch a short-lived credential for this role, e.g. a cloud IAM token
    return fetchToken(ctx, user)
}
 func main() {
     conf, err := config.Parse()
     ...
+	db.SetDBPasswordProvider(tokenProvider{})
+
     if conf.RollbackDBTo != "" {
         db, err := db.New()

It goes above the RollbackDBTo branch so it covers both connections main can open, the down-migration one and db.NewWithMigrations().

The password in NEBRASKA_DB_URL is then ignored; the user, host and database in it are still used.

The same shape is used by netdata for Azure Entra ID and bytebase for AWS RDS IAM.

Testing done

$ cd backend
$ make code-checks
go build ./...
./tools/check_pkg_test.sh
NEBRASKA_SKIP_TESTS=1 go test ./... >/dev/null
./tools/golangci-lint run --fix
0 issues.
go mod tidy
$ make check-backend-with-container
ok  github.com/flatcar/nebraska/backend/pkg/api             22.504s
ok  github.com/flatcar/nebraska/backend/pkg/api/admin        0.248s
ok  github.com/flatcar/nebraska/backend/pkg/api/runtime      2.681s
ok  github.com/flatcar/nebraska/backend/pkg/auth             0.009s
ok  github.com/flatcar/nebraska/backend/pkg/middleware       0.008s
ok  github.com/flatcar/nebraska/backend/pkg/omaha            3.947s
ok  github.com/flatcar/nebraska/backend/pkg/random           0.003s
ok  github.com/flatcar/nebraska/backend/pkg/sessions         0.006s
ok  github.com/flatcar/nebraska/backend/pkg/sessions/memcache      0.005s
ok  github.com/flatcar/nebraska/backend/pkg/sessions/memcache/gob  0.005s
ok  github.com/flatcar/nebraska/backend/pkg/syncer           5.749s
ok  github.com/flatcar/nebraska/backend/test/api            21.491s
ok  github.com/flatcar/nebraska/backend/test/auth/oidc       3.772s

Three tests cover the new path: that the provider is consulted for every connection and a rotated credential is picked up, that a provider error fails the connection, and that without a provider the URL password is still used.

  • Changelog entries added in the respective changelog/ directory (user-facing change, bug fix, security fix, update)
  • Inspected CI output for image differences: /boot and /usr size, packages, list files for any missing binaries, kernel modules, config files, kernel modules, etc.

@Moustafa-Moustafa
Moustafa-Moustafa requested a review from a team as a code owner August 21, 2026 22:36
Copilot AI lite review requested due to automatic review settings August 21, 2026 22:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an optional hook for dynamically supplying PostgreSQL passwords at connection time, enabling Nebraska to use short-lived/rotating credentials (e.g., cloud IAM tokens) without requiring a process restart.

Changes:

  • Extend the internal DB connection opener to optionally consult a password function before each physical connection (via pgx BeforeConnect).
  • Introduce a public api.DBPasswordProvider interface plus a global setter used by api.New().
  • Add tests covering provider rotation, provider error propagation, and the no-provider default behavior; document the feature in the changelog.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
CHANGELOG.md Documents the new pluggable DB password provider capability.
backend/pkg/api/internal/dbconn/conn.go Adds optional provider-aware connection opening using pgx BeforeConnect.
backend/pkg/api/dbpassword.go Introduces DBPasswordProvider and the global install hook used by api.New().
backend/pkg/api/dbpassword_test.go Adds tests for rotated credentials, error propagation, and default behavior.
backend/pkg/api/api.go Wires dbPasswordFunc() into the shared DB connection initialization.
Suppressed comments (1)

backend/pkg/api/dbpassword.go:36

  • SetDBPasswordProvider accepts an interface, so it’s possible to pass a typed-nil implementation (e.g. var p *myProvider = nil; SetDBPasswordProvider(p)). In that case dbPasswordProvider != nil, and dbPasswordFunc will return a method value with a nil receiver, which can lead to a panic when Nebraska opens a connection. It would be safer to treat typed-nil providers as nil (typically via a small reflect-based nilability check) either here or in dbPasswordFunc().
// SetDBPasswordProvider installs the provider New authenticates with. Call it
// before New.
func SetDBPasswordProvider(provider DBPasswordProvider) {
	dbPasswordMu.Lock()
	defer dbPasswordMu.Unlock()

	dbPasswordProvider = provider
}

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread backend/pkg/api/internal/dbconn/conn.go
Comment thread backend/pkg/api/dbpassword.go Outdated
Copilot AI review requested due to automatic review settings August 22, 2026 17:54
@Moustafa-Moustafa
Moustafa-Moustafa force-pushed the feat/db-password-provider branch from 525617d to 16718c5 Compare August 22, 2026 17:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comment thread backend/pkg/api/dbpassword.go Outdated
Comment thread backend/pkg/api/dbpassword_test.go
Copilot AI review requested due to automatic review settings August 22, 2026 18:53
@Moustafa-Moustafa
Moustafa-Moustafa force-pushed the feat/db-password-provider branch from 16718c5 to ff1a0d2 Compare August 22, 2026 18:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment thread backend/pkg/api/dbpassword_test.go
Nebraska takes the database password from NEBRASKA_DB_URL. That string is fixed
for the life of the process, so a deployment whose credential expires, such as an
OAuth token from a cloud identity provider, has no in-process way to supply a new
one: the first connection attempted after expiry fails, and so does every one
after that.

DBPasswordProvider is an optional hook consulted before each physical
connection. When one is installed the handle is opened through pgx's
BeforeConnect callback, which runs for every new connection and receives a fresh
copy of the connection config, so the provider can return a credential renewed
since startup. How that credential is obtained is not Nebraska's concern, and no
cloud SDK is added; go.mod is unchanged.

That callback runs before pgx applies connect_timeout, so when the URL carries
one the provider call is bounded by it.

The provider is given the user the connection authenticates as, so one provider
can serve connections made as different roles. Both connections consult it: the
serving one from NEBRASKA_DB_URL, and the short-lived migrations one from
NEBRASKA_MIGRATIONS_DB_URL, which authenticates as a different role.

Deployments that install no provider are untouched. Both connections take the
plain sqlx.Open path exactly as before and the passwords keep coming from the
URLs.

Signed-off-by: Moustafa Moustafa <momousta@microsoft.com>
Copilot AI review requested due to automatic review settings August 24, 2026 18:36
@Moustafa-Moustafa
Moustafa-Moustafa force-pushed the feat/db-password-provider branch from ff1a0d2 to 397a263 Compare August 24, 2026 18:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants