Skip to content

hrodrig/hbactl

hbactl

Version Release Go 1.26 License: MIT pkg.go.dev Go Report Card

Repo: github.com/hrodrig/hbactl · Releases: Releases

Lightweight Go CLI to manage PostgreSQL Host-Based Authentication (pg_hba.conf) safely: list, add, remove rules; check syntax; reload config. Auto-discovers the file via the running Postgres instance or use --file.

Documentation: Sequence diagrams (Mermaid) for command flows (general, list, add, remove, check, reload), and terminal demo (recorded with VHS) — see docs/. Scanning before release (govulncheck, Grype): tools/README.md.

hbactl demo


Features

  • Auto-Discovery: Locates pg_hba.conf via the running Postgres instance, or use --file to pass the path.
  • Safety First: Backup before every edit; validate syntax with hbactl check (uses pg_hba_file_rules).
  • Reload: Apply changes with hbactl reload (pg_reload_conf()), no restart.
  • Single Binary: One executable; no runtime dependencies.
  • Formats: Supports both CIDR (e.g. 192.168.1.0/24) and legacy IP+netmask in list and add.
  • Group by user: List with --group-by user for visual separators; add with --after-user <name> to insert after that user’s last rule and keep rules grouped.

Installation

Homebrew (macOS, Linux):

brew tap hrodrig/hbactl
brew install hbactl

From Go:

go install github.com/hrodrig/hbactl@latest

From GitHub Releases:
Pre-built binaries for Linux, macOS (darwin), and Windows (amd64, arm64) are published on Releases. Download the archive for your OS/arch and unpack the hbactl binary.

Build from source:

git clone https://github.com/hrodrig/hbactl.git
cd hbactl
go build -o hbactl .

Docker:

Published image (multi-arch from release): ghcr.io/hrodrig/hbactl:latest or ghcr.io/hrodrig/hbactl:v0.2.0. To build from source (same as CI): make docker-build or docker build -t hbactl .

Mount your pg_hba.conf (or the directory that contains it) and pass a connection string so hbactl can discover the file or run reload:

docker run --rm -e DATABASE_URL="postgres://user:pass@host:5432/db?sslmode=disable" -v /path/to/pg_hba.conf:/pg_hba.conf ghcr.io/hrodrig/hbactl:latest list -f /pg_hba.conf
docker run --rm -e DATABASE_URL="postgres://..." -v /path/to/pgdata:/pgdata ghcr.io/hrodrig/hbactl:latest list   # auto-discovery from Postgres

Use -f /path/to/pg_hba.conf when the file is mounted at a known path; omit it when using auto-discovery and the container can reach the Postgres server.

Usage

Global flags (optional):

  • -c / --conn — PostgreSQL connection string (default: DATABASE_URL).
  • -f / --file — Path to pg_hba.conf (for list, add, and remove; can avoid connection for list).

List current rules

Displays a formatted table of your rules with a # column (1-based index in file order; use with remove --index). Use --no-index to omit the # column for copy-paste friendly output. Supports --sort by column: type, database, user, address, method (display only; file order is unchanged). Use --group-by user to print === user: name === separators between users (implies sort by user if --sort is not set).

hbactl list
hbactl list -f /path/to/pg_hba.conf              # no connection needed
hbactl list --no-index --sort database           # copy-paste friendly, no rule numbers
hbactl list --sort user
hbactl list --group-by user                      # separators between users

Add a new rule

Creates a backup (.bak or .bak.<timestamp>) then appends the rule, or inserts it after the last rule for a given user if --after-user is set (keeps rules grouped by user). Use --dry-run to preview the line (shows “would append” or “would insert after last rule for user …” when using --after-user); no file write or backup. Requires connection or --file when not using --dry-run.

hbactl add --type host --db all --user app --addr 192.168.1.100/32 --method scram-sha-256
hbactl add --type host --db all --user all --addr 10.0.0.0/24 --netmask 255.255.255.0 --method md5   # legacy format
hbactl add -f /path/to/pg_hba.conf --type host --db all --user all --addr 127.0.0.1/32 --method trust
hbactl add --dry-run --type host --db all --user pepe --addr 10.0.0.1/32 --method ident --ident-map my_ident_map   # preview only
hbactl add --type host --db all --user pepe --addr 10.0.0.1/32 --method ident --ident-map my_ident_map   # ident with user map
hbactl add --type host --db all --user pepe --addr 10.0.0.5/32 --method md5 --after-user pepe   # insert after last "pepe" rule

Flags: --type (required), --db, --user, --addr (required for host types), --netmask (optional, legacy), --method (required), --ident-map (optional: for ident method), --after-user (insert after last rule for this user; default appends at end), --dry-run (print line without writing).

Remove rule(s)

Creates a backup then removes one rule by --index or all matching rules by criteria. Use --dry-run to preview. Requires connection or --file.

By index (1-based, same as the # column in hbactl list):

hbactl list                                    # show rules with # column
hbactl remove --index 3                        # remove the 3rd rule (in file order)
hbactl remove -f /path/to/pg_hba.conf --index 1
hbactl remove --index 2 --dry-run               # preview only

By criteria (removes all matching rules in one run):

hbactl remove -f sample-pg_hba.conf --user app_user --dry-run              # all rules for user app_user (any database)
hbactl remove -f sample-pg_hba.conf --user app_user --db app_planning --dry-run   # only app_user on database app_planning
hbactl remove -f sample-pg_hba.conf --addr 10.0.1.7 --dry-run             # all rules from IP 10.0.1.7

Flags: --index (1-based rule number; use alone), --user (remove all rules for this user), --db (with --user, limit to this database), --addr (remove all rules matching this address, e.g. 10.0.1.7 or 10.0.1.7/32), --dry-run (print rule(s) that would be removed without writing or backup). Use either --index or one of --user / --addr per run, not both.

Check for errors

Uses pg_hba_file_rules to report syntax errors. Requires a connection.

hbactl check

Reload configuration

Runs pg_reload_conf() so the server picks up changes without restart. Requires a connection.

hbactl reload

Connection

By default, hbactl connects to PostgreSQL using the DATABASE_URL environment variable or the --conn / -c flag. Ensure your user has permission to read the HBA file and run pg_reload_conf().

Multiple servers / explicit file path

Use --file / -f to pass the path to pg_hba.conf explicitly. With --file, list can run without a connection (it only reads the file). Handy for:

  • Managing several servers: pass a different path (or --conn + path) per run.
  • Inspecting a local copy of the file (e.g. from another host).
# List rules from a file (no DB connection)
hbactl list -f /opt/homebrew/var/postgresql@16/pg_hba.conf

# Per-server: connect to server and optionally override path
hbactl list --conn "postgres://user@server1:5432/postgres" -f /var/lib/pgsql/16/data/pg_hba.conf

Checking if PostgreSQL is running

  • Port: Something should be listening on 5432 (e.g. lsof -i :5432, ss -tlnp | grep 5432, or nc -z localhost 5432).
  • Client: If you have psql or pg_isready installed, run pg_isready -h localhost -p 5432.
  • macOS (Homebrew): brew services list | grep postgres.
  • Linux (systemd): systemctl status postgresql or systemctl status postgresql@16 (depending on distro and version).

If nothing is listening on 5432 and no Postgres service is listed, you don’t have a running server.

Running PostgreSQL for testing

Linux (Debian/Ubuntu):

sudo apt update && sudo apt install -y postgresql postgresql-client
sudo systemctl start postgresql
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';"
export DATABASE_URL="postgres://postgres:postgres@localhost:5432/postgres"
hbactl list

Linux (Fedora/RHEL):

sudo dnf install -y postgresql-server postgresql
sudo postgresql-setup --initdb
sudo systemctl start postgresql
export DATABASE_URL="postgres://postgres@localhost:5432/postgres"   # peer auth by default
hbactl list

Homebrew (macOS):

brew install postgresql@16
brew services start postgresql@16
export DATABASE_URL="postgres://$(whoami)@localhost:5432/postgres"
hbactl list

Docker:

docker run -d --name pg -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:16
export DATABASE_URL="postgres://postgres:postgres@localhost:5432/postgres"
hbactl list

Unit tests (parser and table output) do not require a running PostgreSQL server: run go test ./....

Releasing

Releases and tags are made only from the main branch. Work on develop (or feature branches), merge to main when ready, then from main:

git checkout main
git pull
git tag v0.1.0
make release
git push origin v0.1.0

make release will fail if you are not on main. Use make snapshot on any branch to test builds locally.

Homebrew tap (one-time setup): So that brew install hrodrig/hbactl/hbactl works, create an empty repo github.com/hrodrig/homebrew-hbactl. GoReleaser will push the cask there on each release. If you run make release from your machine (not CI), ensure .goreleaser.yaml has the tap repo and run release; you may need to uncomment token and set HOMEBREW_TAP_TOKEN (GitHub PAT with repo scope) if pushing to the tap from CI.

License

MIT — see LICENSE. Contributing: CONTRIBUTING.md. Code of Conduct: CODE_OF_CONDUCT.md. Changelog: CHANGELOG.md.

About

A modern CLI tool to manage and validate PostgreSQL pg_hba.conf rules with safety and ease.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors