RAPID INCIDENT TRIAGE & ACTION PLAN•Security
01. Error Signature
PostgreSQL pg_hba.conf Entry Auditor
02. Root Cause Vector

PostgreSQL rejected a connection because no matching pg_hba.conf rule exists for the host/user/database/SSL combination, locking out the ...

03. Rapid Prescription

Enforce consistent lock order (e.g. ORDER BY id FOR UPDATE) or use non-blocking SKIP LOCKED.

Zero-Trust Client IDE/ Security

PostgreSQL pg_hba.conf Entry Auditor

PostgreSQL rejected a connection because no matching pg_hba.conf rule exists for the host/user/database/SSL combination, locking out the application at the authentication layer.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(43L)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
apiVersion: v1
kind: ConfigMap
metadata:
  name: legacy-app-pg-config
  namespace: pci-secure-zone
  labels:
    app: legacy-api
    env: prod
    # TODO: Clean up these old labels after migration to new cluster
    # owner: deprecated-team@my-registry.internal
    # version: v2.3.1
    # This config is a mess, but it's been running since 2018. Don't touch.
data:
  # VULNERABILITY: The host-based authentication is misconfigured; the pg_hba.conf entry is missing for the app user from the internal subnet, but the connection is forced via SSL off due to a legacy workaround.
  postgres_connection_string: "postgresql://app:SuperSecretPassword123!@postgres-master.prod.svc.cluster.local:5432/prod?sslmode=disable"
  # Old connection string from before the namespace change - keep for rollback
  # postgres_connection_string_backup: "postgresql://app:oldPassw0rd!@10.0.0.5:5432/prod?sslmode=require"
  # AWS credentials for the backup sync job (rotated quarterly)
  aws_access_key_id: "AKIAIOSFODNN7EXAMPLE"
  aws_secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
  # Stripe test key for billing module - TODO: move to vault
  stripe_api_key: "sk_test_4eC39HqLyjWDarjtT1zdp7dc"
  # RSA private key for SSH tunnel fallback (encrypted at rest? ha.)
  ssh_private_key: "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAtX8tJfN4vRlJ7wK8qZQbLmKjHhGvTqWpPpLmNoIuVwq3sT\n... (truncated for brevity) ...\n-----END RSA PRIVATE KEY-----"
  # Legacy pg_hba.conf snippet - manually maintained in the CM, but the entry for 192.168.1.100 is missing because of a subnet typo in the last deployment
  pg_hba_snippet: |
    # TYPE  DATABASE        USER            ADDRESS                 METHOD
    host    prod            app             192.168.1.0/24          md5  # This should cover 192.168.1.100, but the CIDR was incorrectly calculated in the automation script
    host    prod            app             10.10.0.0/16            scram-sha-256
    host    all             all             0.0.0.0/0               reject
    # host    prod            app             192.168.1.100/32       md5  # TODO: Uncomment after fixing the automation race condition with the network team
    # The line above is commented because the network policy script sometimes duplicates entries, causing conflicts.
  # Environment overrides for the app deployment - these get injected as env vars
  app_env: |
    DB_HOST=postgres-master.prod.svc.cluster.local
    DB_PORT=5432
    DB_NAME=prod
    DB_USER=app
    DB_PASSWORD=SuperSecretPassword123!
    # Force SSL off due to legacy client library bug in the Java 8 runtime
    DB_SSLMODE=disable
    # This env var is read by the app to set the source IP for audit logs, but it's misinterpreted in the connection pool
    APP_SOURCE_IP=192.168.1.100
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

How to Fix PostgreSQL 'no pg_hba.conf entry' Error for Host, User, and Database

Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5 mins

TL;DR

  • What broke: PostgreSQL's host-based authentication (pg_hba.conf) has no rule matching the combination of client IP 192.168.1.100, user app, database prod, and SSL disabled — the connection is rejected before a password is even checked.
  • How to fix it: Add a correctly ordered host record to pg_hba.conf that matches the CIDR block, user, database, and auth method, then reload Postgres without a full restart.
  • Shortcut: Use our Client-Side Sandbox above to auto-refactor this — paste your pg_hba.conf and get a patched version with least-privilege entries generated locally in your browser.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

The Incident (What Does the Error Mean?)

Raw error output from PostgreSQL logs or client:

FATAL:  no pg_hba.conf entry for host '192.168.1.100', user 'app', database 'prod', SSL off

PostgreSQL evaluates pg_hba.conf top-to-bottom, first-match wins. When no record matches all four dimensions simultaneously — connection type, client address, database, and user — Postgres issues a hard FATAL and drops the connection. The application never reaches password authentication. From the app's perspective this is indistinguishable from the database being down, triggering connection pool exhaustion, health check failures, and cascading service timeouts within seconds.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

The Attack Vector / Blast Radius

This is a dual-edged failure:

Availability side: Every connection attempt from 192.168.1.100 fails instantly. If this is your primary app server, you have a full production outage. Connection pools will retry aggressively, hammering Postgres with rejected handshakes and potentially exhausting max_connections for other legitimate clients.

Security side — the misconfiguration that causes this is often fixed wrong: Engineers under pressure add an overly broad rule to stop the bleeding:

host  all  all  0.0.0.0/0  trust

That single line grants passwordless access to every database from every IP on the internet if the Postgres port is reachable. This is how production databases get exfiltrated. The trust auth method requires zero credentials — it is unconditional access. A misconfigured security group or a cloud firewall rule change later exposes this permanently.

The correct mental model: pg_hba.conf is a firewall AND an auth policy. Treat every line as a least-privilege ACL entry.


How to Fix It (The Solution)

Basic Fix

Locate pg_hba.conf (typically /etc/postgresql/<version>/main/pg_hba.conf on Debian/Ubuntu or /var/lib/pgsql/data/pg_hba.conf on RHEL). Add a scoped rule before any catch-all lines:

# TYPE  DATABASE  USER  ADDRESS          METHOD
- # (no matching rule existed for this combination)
+ host    prod      app   192.168.1.100/32  scram-sha-256

Reload without downtime:

# As postgres OS user or superuser inside psql:
SELECT pg_reload_conf();

# Or via OS:
systemctl reload postgresql
# or
pg_ctl reload -D /var/lib/pgsql/data

Verify the rule is active:

SELECT * FROM pg_hba_file_rules WHERE database = '{prod}' AND user_name = '{app}';

Enterprise Best Practice

A single IP /32 rule is fragile in dynamic environments (autoscaling groups, Kubernetes pod CIDR rotation). The production-grade pattern uses subnet-scoped rules + scram-sha-256 + SSL enforcement, with trust and md5 fully prohibited.

# TYPE  DATABASE  USER   ADDRESS            METHOD

- # Old permissive rule (NEVER do this in production)
- host    all       all    0.0.0.0/0          md5

+ # Enforce SSL for all remote app connections (Postgres 12+)
+ hostssl  prod     app    192.168.1.0/24     scram-sha-256

+ # Reject any non-SSL attempt from same subnet explicitly
+ hostnossl prod    app    192.168.1.0/24     reject

+ # Local socket access for DBA tooling only
+ local    all      postgres                  peer

+ # Deny everything else
+ host     all      all    0.0.0.0/0           reject

Key decisions explained:

  • hostssl + scram-sha-256: Mandates TLS in transit and uses the strongest supported password hashing. Eliminates the SSL off attack surface entirely.
  • Explicit reject for hostnossl: Prevents silent fallback to unencrypted connections if SSL is misconfigured on the client.
  • /24 subnet instead of /32: Survives rolling deploys and autoscaling without pg_hba.conf changes, but still scoped to your private network range.
  • Terminal reject catch-all: Any connection not matching an explicit allow rule is denied with a logged rejection rather than a silent drop.

💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

Prevention in CI/CD

This error should never reach production. Gate it at the pipeline level:

1. Checkov static analysis on pg_hba.conf:

checkov -f pg_hba.conf --check CKV_PG_1  # flags trust auth

Write a custom Checkov policy to reject md5 and trust methods org-wide.

2. OPA/Conftest policy for Terraform aws_db_instance or Helm chart values:

# policy/pg_hba.rego
package postgresql

deny[msg] {
  rule := input.hba_rules[_]
  rule.method == "trust"
  msg := sprintf("pg_hba rule for user '%v' uses 'trust' — forbidden", [rule.user])
}

deny[msg] {
  rule := input.hba_rules[_]
  rule.type == "host"  # not hostssl
  rule.address != "127.0.0.1/32"
  msg := sprintf("Non-SSL remote rule for '%v' forbidden outside localhost", [rule.user])
}

3. Ansible/Chef enforcement: Manage pg_hba.conf as code. Any manual edit to the file triggers a drift alert via your CSPM tool (AWS Config, Wiz, Orca). The file should be owned by your IaC pipeline, not edited by hand on the host.

4. Connection string validation in app CI: Assert that your application's DATABASE_URL includes sslmode=require or sslmode=verify-full. A missing SSL mode parameter is what generates the SSL off dimension of this exact error.

# Fail the build if sslmode is missing or set to disable
echo "$DATABASE_URL" | grep -E 'sslmode=(require|verify-full|verify-ca)' || \
  (echo "FATAL: DATABASE_URL missing required sslmode" && exit 1)

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.Why does PostgreSQL reject the connection before even checking the password?
pg_hba.conf is evaluated before any authentication method. It acts as a connection firewall. If no record matches the four-tuple of (connection type, client IP, database, user), Postgres issues a FATAL and closes the socket immediately. The password is never transmitted or checked. This is by design — HBA stands for Host-Based Authentication and it's the first gate, not the last. This is a critical security boundary, but manually managing it is a single point of failure. Drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix that enforces this principle without human error.
Q2.Can I reload pg_hba.conf without restarting PostgreSQL?
Yes. pg_hba.conf changes are applied with a reload, not a full restart. Run `SELECT pg_reload_conf();` as a superuser inside psql, or `systemctl reload postgresql` / `pg_ctl reload` at the OS level. Existing connections are not dropped. Only new connection attempts use the updated rules. This makes HBA changes safe to apply during production traffic, but manually editing and reloading this file is highly error-prone and risky. A single syntax mistake or misconfigured rule can open a security vulnerability with a massive blast radius, locking out legitimate services or exposing the database. Drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix and generate a safe, validated reload command.
Q3.What is the difference between 'md5' and 'scram-sha-256' in pg_hba.conf and which should I use?
Both are password-based auth methods, but `md5` hashes passwords with MD5, which is cryptographically broken and vulnerable to offline brute-force and rainbow table attacks. `scram-sha-256` (available since PostgreSQL 10) uses SCRAM with SHA-256, provides mutual authentication, and is resistant to replay attacks. Always use `scram-sha-256` for new deployments. Set `password_encryption = scram-sha-256` in postgresql.conf and require users to reset passwords after migration from md5. Doing this migration manually across large mono-repos or at scale is a compliance nightmare, prone to inconsistency and audit failures. For on-prem, automated pull request generation with Maker-Checker review and enterprise-scale policy enforcement, upgrade to the StackEngine CI/CD Automator (Team Edition).