Skip to content

fix(postmaster): honor AUTOPG_PG_PASSWORD for initdb and the admin pool - #142

Merged
namastex888 merged 1 commit into
devfrom
fix/postmaster-managed-password
Jul 3, 2026
Merged

namastex888 merged 1 commit into
devfrom
fix/postmaster-managed-password

Conversation

@namastex888

Copy link
Copy Markdown
Contributor

Postmaster ignores the managed superuser password

The bug (found debugging a k8s node-restart incident, 2026-07-03): PostgresManager has always accepted options.password — it flows into initdb's --pwfile on fresh clusters (src/postgres.js:717-730) and into the TCP admin pool (:787). But the postmaster subcommand never wires it: parsePostmasterArgs has no password surface and the constructor call passes none, so this.password silently pins to the built-in 'postgres' (src/postgres.js:542).

Impact: any supervisor that rotates the superuser password (e.g. a k8s Helm provision job doing ALTER USER postgres PASSWORD ...) crash-loops the postmaster on every restart — the admin pool re-authenticates fresh at each boot and is refused:

Failed to initialize admin pool after 5 attempts: password authentication failed for user "postgres"

It works until the first restart, which makes it a latent production landmine. Note cli-ui.cjs:366 already honors server.pgPassword — the postmaster was the odd one out.

The fix: a small resolver (src/lib/postmaster-password.js) wired into the postmaster entry, mirroring the settings-schema.cjs server.pgPassword env chain:

AUTOPG_PG_PASSWORD > PGSERVE_PG_PASSWORD (legacy) > 'postgres'

  • Empty strings treated as unset (an AUTOPG_PG_PASSWORD= in a unit file can't blank the pool password).
  • The source (env var name) is logged for operability — never the value.
  • postmaster --help documents the env.
  • Env-only by design: settings.json is non-secret and stays password-free; k8s feeds it via secretKeyRef, pm2 via env files.

Tests: tests/lib/postmaster-password.test.js — 7 cases (default, both env vars, precedence, empty-string, schema-name lock, --help advertisement). Full suite: 701 pass / 1 pre-existing failure (tests/console/smoke.test.js — bundled console asset absent on a clean clone; fails identically on dev without this change). eslint src/ bin/ clean.

Downstream: with this shipped, the k8s chart (#141) can restore superuser-password rotation by setting AUTOPG_PG_PASSWORD from its Secret — rotation was removed there as a workaround for this exact bug.

PostgresManager always accepted options.password (initdb --pwfile on

fresh clusters + the TCP admin pool), but the postmaster entry never

wired it, silently pinning the built-in default. Supervisors that

rotate the superuser password (the k8s chart's provision Job) then

crash-loop the postmaster on every restart: the admin pool

re-authenticates fresh at each boot and is refused (observed in the

omni k8s node-restart incident, 2026-07-03).

Resolution order mirrors settings-schema server.pgPassword:

AUTOPG_PG_PASSWORD > PGSERVE_PG_PASSWORD (legacy) > default. Empty

strings are treated as unset. The source (never the value) is logged,

and postmaster --help documents the env.
@gitguardian

gitguardian Bot commented Jul 3, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 2 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secrets in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
34520702 Triggered Generic Password 5832467 tests/lib/postmaster-password.test.js View secret
34520702 Triggered Generic Password 5832467 tests/lib/postmaster-password.test.js View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6157970c-0f62-4185-b061-7090dc0f5d40

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/postmaster-managed-password

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces managed superuser password resolution for the postmaster entry point. It resolves the password from the AUTOPG_PG_PASSWORD or PGSERVE_PG_PASSWORD (legacy) environment variables, falling back to 'postgres' by default, and wires it into PostgresManager. This prevents crash-loops when the superuser password is rotated. Feedback suggests improving the robustness of resolvePostmasterPassword by using the nullish coalescing operator (??) to safely handle null arguments instead of only relying on default parameters, and adding corresponding test coverage for null and undefined inputs.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +32 to +34
export function resolvePostmasterPassword(env = process.env) {
for (const name of POSTMASTER_PASSWORD_ENV_VARS) {
const value = env[name];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

In JavaScript, default parameters (e.g., env = process.env) only apply when the argument is undefined. If null is explicitly passed to resolvePostmasterPassword(null), the function will throw a TypeError when attempting to access env[name].

Using the nullish coalescing operator (??) inside the function body ensures that both null and undefined are safely handled by falling back to process.env, improving the robustness of the password resolution.

export function resolvePostmasterPassword(env) {
  const targetEnv = env ?? process.env;
  for (const name of POSTMASTER_PASSWORD_ENV_VARS) {
    const value = targetEnv[name];

Comment on lines +26 to +31
test('defaults to the built-in password with an empty environment', () => {
expect(resolvePostmasterPassword({})).toEqual({
password: DEFAULT_POSTMASTER_PASSWORD,
source: 'default',
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Update the test case to verify that resolvePostmasterPassword handles null and undefined inputs robustly without throwing a TypeError.

Suggested change
test('defaults to the built-in password with an empty environment', () => {
expect(resolvePostmasterPassword({})).toEqual({
password: DEFAULT_POSTMASTER_PASSWORD,
source: 'default',
});
});
test('defaults to the built-in password with an empty environment, null, or undefined', () => {
expect(resolvePostmasterPassword({})).toEqual({
password: DEFAULT_POSTMASTER_PASSWORD,
source: 'default',
});
expect(resolvePostmasterPassword(null)).toEqual({
password: DEFAULT_POSTMASTER_PASSWORD,
source: 'default',
});
expect(resolvePostmasterPassword(undefined)).toEqual({
password: DEFAULT_POSTMASTER_PASSWORD,
source: 'default',
});
});

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 58324677d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return { password: value, source: name };
}
}
return { password: DEFAULT_POSTMASTER_PASSWORD, source: 'default' };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Honor server.pgPassword from settings.json

In installs where the operator sets the documented secret with autopg config set server.pgPassword ... (or edits ~/.autopg/settings.json) and then restarts the pm2/systemd postmaster, this resolver still falls back to 'postgres' unless an env var is present. I checked src/settings-loader.cjs, which defines the effective precedence as defaults < file < env, and docs/settings-schema.md documents server.pgPassword as the backend superuser password; bypassing that file layer means the admin pool can still authenticate with the wrong password and crash-loop in the same restart scenario this change is intended to fix.

Useful? React with 👍 / 👎.

@namastex888
namastex888 merged commit 7573418 into dev Jul 3, 2026
10 of 11 checks passed
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.

1 participant