dbbat

command module
v0.19.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: AGPL-3.0 Imports: 22 Imported by: 0

README

DBBat - Database Observability Proxy

Give your devs access to prod.

A transparent database proxy for query observability, access control, and safety. Speaks PostgreSQL, Oracle, MySQL/MariaDB, and MongoDB wire protocols. Every query logged. Every connection tracked.

Documentation

Full documentation is available at dbbat.com:

Why DBBat?

The Problem:

  • Production databases should not be directly accessible to developers for security and compliance reasons
  • Developers often need access to production data to diagnose issues, debug problems, and understand user behavior
  • Traditional solutions are binary: either full access (risky) or no access (blocks troubleshooting)

The Solution:

DBBat acts as a monitoring proxy that allows controlled developer access to production databases with:

  • Complete monitoring: Every query and result is logged with full traceability
  • Strict limitations: Time-windowed access, fine-grained controls, query quotas, and data transfer limits
  • Full audit trail: Track who accessed what, when, and what data they retrieved
  • Encrypted credentials: Database passwords never exposed to users
  • Granular access control: Grant temporary access to specific databases with precise permissions

Supported Databases

Engine Protocol Default proxy port Notes
PostgreSQL PostgreSQL wire (pgx/v5) :5434 (DBB_LISTEN_PG) First-class support; auth terminated at proxy
Oracle TNS / TTC :1522 (DBB_LISTEN_ORA) O5LOGON proxy auth; go-ora end-to-end (other clients reach AUTH only — see docs/oracle.md)
MySQL MySQL wire (go-mysql-org/go-mysql) :3307 (DBB_LISTEN_MYSQL) caching_sha2_password (default), mysql_clear_password; TLS terminated at proxy
MariaDB MySQL wire (same listener) :3307 (DBB_LISTEN_MYSQL) Same as MySQL — mysql_native_password not supported, STMT_BULK_EXECUTE refused
MongoDB MongoDB wire (OP_MSG, hand-rolled) :27018 (DBB_LISTEN_MONGO) SCRAM-SHA-256 or PLAIN-over-TLS at the proxy; upstream via SCRAM-SHA-256 — see docs/mongodb.md

Each engine has its own listener; enable only the ones you need by setting the matching DBB_LISTEN_* environment variable. PostgreSQL is enabled by default; Oracle/MySQL/MongoDB listen on their default ports unless explicitly disabled in config.

Any of these upstreams can be reached directly or through an SSH bastion — see SSH tunnels.

Features

  • Multi-engine proxy: PostgreSQL, Oracle, MySQL/MariaDB, MongoDB on independent listeners
  • User Management: Local user database with username/password (Argon2id) and admin/viewer/connector roles
  • API Keys: Long-lived bearer tokens (dbb_…) for programmatic access; cannot create or revoke other keys (security restriction)
  • Slack OAuth (optional): Sign-in via Slack workspace, optional auto-provisioning
  • Slack Grant Approvals (optional): Grant requests notify a Slack channel with Approve/Deny buttons (inbound endpoint or Socket Mode); auto-approved requests notify without buttons
  • Server Configuration: Store target server connections with AES-256-GCM encrypted credentials; protocol field per server (postgresql, oracle, mysql, mariadb, mongodb, ssh)
  • SSH Tunnels: Route upstream connections for any protocol through an SSH bastion (via_uid), with host-key TOFU pinning and a shared pooled dialer
  • Connection & Query Tracking: Logs every connection, every query (SQL text, parameters, duration, rows affected, errors), and optionally captures result rows (query_rows table) up to configurable size limits
  • Access Control: Time-windowed grants (starts_at / expires_at), independent controls (read_only, block_copy, block_ddl), and optional quotas (max_query_counts, max_bytes_transferred)
  • Grant Requests & Auto-Approval: Users request access against grant definitions; definitions flagged auto_approve skip admin review and materialize the grant instantly, with a required justification and a dedicated audit trail
  • Live Enforcement: Limits are enforced mid-stream (not just between commands), and revoking a grant blocks further queries and disconnects sessions already in flight
  • Upstream Identity: The dbbat username is encoded into the upstream connection metadata (application_name / program_name / AUTH_PROGRAM_NM), so target-side monitoring attributes queries to the real person
  • Read-only enforcement: Defense in depth — SQL inspection, PostgreSQL default_transaction_read_only, MySQL/MariaDB blocks for LOAD DATA/SELECT … INTO OUTFILE/etc., and proxy-side opt-out from LOCAL INFILE
  • Audit Trail: Append-only audit log of user, grant, and database changes
  • Rate Limiting: Per-user request limits and exponential backoff on failed login
  • Authentication Cache: Optional in-memory cache (TTL + max size) shared across REST and proxy auth paths
  • Session Packet Dumps: Optional binary capture of post-auth session traffic (.dbbat-dump files); same format across all protocols (see docs/dump-format.md) with dbbat dump anonymise for sharing
  • REST API: OpenAPI 3.0 documented (/api/docs), versioned under /api/v1/
  • Web UI: Embedded React frontend served at /app — servers (/servers, listing SSH bastions alongside database servers), grants, connections and queries all have detail pages
  • Demo / Test modes: Self-provisioning sample data for safe trials and E2E testing

Quick Start

Running with Docker
docker run -d \
  -e DBB_DSN="postgres://user:pass@host:5432/dbbat?sslmode=require" \
  -p 5434:5434 \
  -p 1522:1522 \
  -p 3307:3307 \
  -p 27018:27018 \
  -p 4200:4200 \
  ghcr.io/fclairamb/dbbat

Ports: 5434 PostgreSQL proxy, 1522 Oracle proxy, 3307 MySQL/MariaDB proxy, 27018 MongoDB proxy, 4200 REST API + web UI.

Running with Docker Compose

See docker-compose installation for a complete example.

Usage Example

All API endpoints are under /api/v1/. See the API Reference for complete documentation.

Breaking change in v0.17.0: /api/v1/databases was renamed to /api/v1/servers, and /api/v1/ssh-servers was added for bastion management. No /databases alias is kept — update any scripts that used the old path.

1. Login and get a token
TOKEN=$(curl -s -X POST http://localhost:4200/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "admin"}' | jq -r '.token')
2. Create a User
curl -X POST http://localhost:4200/api/v1/users \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "developer",
    "password": "temppass123",
    "roles": ["connector"]
  }'
3. Configure a Target Server
curl -X POST http://localhost:4200/api/v1/servers \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "production",
    "description": "Production database",
    "protocol": "postgresql",
    "host": "db.example.com",
    "port": 5432,
    "database_name": "myapp",
    "username": "readonly_user",
    "password": "dbpass",
    "ssl_mode": "require"
  }'

For Oracle, set "protocol": "oracle" and add "oracle_service_name": "ORCL". For MySQL/MariaDB, set "protocol": "mysql" (or "mariadb") and use port 3306. For MongoDB, set "protocol": "mongodb", use port 27017, and optionally add "mongo_auth_source": "admin".

Creating a server whose name is already taken returns 409 DUPLICATE_NAME (same for grant definitions and users).

3b. Reach a Server Through an SSH Bastion (optional)

Servers that aren't directly reachable can be dialled through an SSH tunnel. Register the bastion via /api/v1/ssh-servers, then point the database server at it with via_uid:

BASTION=$(curl -s -X POST http://localhost:4200/api/v1/ssh-servers \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "prod-bastion",
    "host": "bastion.example.com",
    "port": 22,
    "username": "ec2-user",
    "ssh_private_key": "-----BEGIN OPENSSH PRIVATE KEY-----\n..."
  }' | jq -r '.uid')

curl -X POST http://localhost:4200/api/v1/servers \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"name\": \"prod-behind-bastion\", \"protocol\": \"postgresql\", \"host\": \"10.0.1.20\", \"port\": 5432, \"database_name\": \"myapp\", \"username\": \"readonly_user\", \"password\": \"dbpass\", \"via_uid\": \"$BASTION\"}"

Tunnelling works for all four protocols. The bastion host key is pinned on first use (TOFU) and connections are pooled and shared across sessions. ssh_private_key and ssh_passphrase are write-only and never returned. Set "clear_via_uid": true on update to go back to a direct dial.

4. Grant Access
curl -X POST http://localhost:4200/api/v1/grants \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "<user-uid>",
    "database_id": "<database-uid>",
    "controls": ["read_only"],
    "starts_at": "2024-01-01T00:00:00Z",
    "expires_at": "2024-12-31T23:59:59Z",
    "max_query_counts": 1000,
    "max_bytes_transferred": 10485760
  }'

controls accepts any combination of read_only, block_copy, block_ddl. An empty array means full write access.

5. Connect via Proxy
# PostgreSQL
psql -h localhost -p 5434 -U developer -d production

# Oracle (go-ora-style easy connect)
# Use the database name (or its oracle_service_name) as SERVICE_NAME
# Example with sqlplus: developer/temppass123@//localhost:1522/production

# MySQL / MariaDB
mysql -h 127.0.0.1 -P 3307 -u developer -p production

# MongoDB (authSource carries the DBBat database name)
mongosh "mongodb://developer:temppass123@localhost:27018/?authSource=production&authMechanism=SCRAM-SHA-256"

Configuration

Variable Description Default
DBB_DSN PostgreSQL DSN for DBBat storage Required
DBB_LISTEN_PG PostgreSQL proxy listen address :5434
DBB_LISTEN_ORA Oracle proxy listen address (empty disables) :1522
DBB_LISTEN_MYSQL MySQL/MariaDB proxy listen address (empty disables) :3307
DBB_LISTEN_MONGO MongoDB proxy listen address (empty disables) :27018
DBB_LISTEN_API REST API listen address :4200
DBB_KEY Base64-encoded AES-256 encryption key Auto-generated at ~/.dbbat/key
DBB_KEYFILE Path to file containing encryption key -
DBB_RUN_MODE Run mode: empty (production), test, or demo -
DBB_LOG_LEVEL debug, info, warn, error info
DBB_DUMP_DIR Directory for session packet dumps (empty disables) -
DBB_DUMP_MAX_SIZE Max dump file size per session, in bytes 10485760 (10 MB)
DBB_DUMP_RETENTION Auto-delete dumps older than this (Go duration) 24h
DBB_MYSQL_TLS_DISABLE Disable MySQL TLS termination at the proxy false
DBB_MYSQL_TLS_CERT_FILE PEM cert for MySQL TLS (auto self-signed if empty) -
DBB_MYSQL_TLS_KEY_FILE PEM RSA key for MySQL TLS (auto-generated if empty) -

See Configuration for the full set, including rate limiting, query storage, hash presets, auth cache, Slack OAuth, demo target, and dev redirects.

Security

  • User passwords are hashed with Argon2id
  • Database credentials are encrypted with AES-256-GCM (AAD-bound to the database UID)
  • API keys (dbb_…) are stored as encrypted blobs and cannot create or revoke other keys
  • Failed logins trigger per-username exponential backoff
  • Default admin user (admin / admin) is created on first startup — change it immediately

Architecture

psql / pg client     ─►  DBBat (auth + grant check + log) ─┐
sqlplus / go-ora     ─►  DBBat (TNS service-name routing)  ─┤   direct dial   ┌─► PostgreSQL / Oracle
mysql / mariadb cli  ─►  DBBat (caching_sha2_password)     ─┼───────────────► ┤
mongosh / driver     ─►  DBBat (SCRAM-SHA-256 / PLAIN-TLS) ─┘   or SSH tunnel └─► MySQL / MariaDB / MongoDB
                                                                 (via_uid)

DBBat is a single Go binary backed by a PostgreSQL store (users, servers, grants, connections, queries, audit, dumps).

Development

make dev          # Start dev environment with hot reload (Air + Vite)
make test         # Run Go tests (uses testcontainers)
make test-e2e     # Run Playwright E2E tests
make build-app    # Build frontend + backend
make lint         # Run golangci-lint

See CLAUDE.md for development documentation.

License

AGPL-3.0

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
api
notify
Package notify implements notification channels for grant request lifecycle events.
Package notify implements notification channels for grant request lifecycle events.
proxy/conncheck
Package conncheck validates that a configured server row is actually reachable and usable: that an SSH bastion accepts the stored key, and that a database target can be dialed (and authenticated against) — optionally through that bastion.
Package conncheck validates that a configured server row is actually reachable and usable: that an SSH bastion accepts the stored key, and that a database target can be dialed (and authenticated against) — optionally through that bastion.
proxy/mongodb
Package mongodb implements a transparent MongoDB wire-protocol proxy for dbbat: it terminates client authentication (SASL PLAIN over TLS or a dbb_ API key), authenticates to the upstream MongoDB with stored credentials (SCRAM-SHA-256), and grant-checks, classifies, logs and quota-enforces every command — the same pipeline as the PostgreSQL/Oracle/MySQL proxies.
Package mongodb implements a transparent MongoDB wire-protocol proxy for dbbat: it terminates client authentication (SASL PLAIN over TLS or a dbb_ API key), authenticates to the upstream MongoDB with stored credentials (SCRAM-SHA-256), and grant-checks, classifies, logs and quota-enforces every command — the same pipeline as the PostgreSQL/Oracle/MySQL proxies.
store
Package store provides database access and persistence for DBBat.
Package store provides database access and persistence for DBBat.
version
Package version contains build version information set via ldflags.
Package version contains build version information set via ldflags.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL