GuidesUser Management & RBAC

User Management & RBAC

BunnyDB includes built-in user management with role-based access control (RBAC). This guide covers authentication, user administration, and permission management.

Overview

BunnyDB supports two roles with distinct permissions:

  • admin: Full access to all operations (create, read, update, delete, control)
  • readonly: Read-only access to view peers, mirrors, and metrics

All API endpoints require Bearer token authentication (except the login endpoint).

Default Admin User

BunnyDB seeds a default admin user on first startup using environment variables:

# docker-compose.yml or .env
BUNNY_ADMIN_USER=admin
BUNNY_ADMIN_PASSWORD=your_secure_password
⚠️

Change the default admin password immediately after deployment. Never use default credentials in production.

Authentication

Login

Obtain a JWT token by authenticating with username and password:

curl -X POST http://localhost:8112/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "admin",
    "password": "your_password"
  }'

Response

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "username": "admin",
  "role": "admin"
}

Token Expiration

JWT tokens are valid for 24 hours. After expiration, you must login again to obtain a new token.

Store tokens securely. Never commit tokens to version control or expose them in client-side code.

Using the Token

Include the token in the Authorization header for all subsequent requests:

curl -X GET http://localhost:8112/v1/mirrors \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

User Management

Creating Users

Admins can create new users with either admin or readonly roles:

curl -X POST http://localhost:8112/v1/users \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "ops_team",
    "password": "secure_password_123",
    "role": "admin"
  }'

Creating a readonly user:

curl -X POST http://localhost:8112/v1/users \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "data_analyst",
    "password": "analyst_pass_456",
    "role": "readonly"
  }'

Response

{
  "message": "User 'ops_team' created successfully"
}
🚫

Only users with the admin role can create other users. Readonly users will receive a 403 Forbidden error.

Listing Users

View all users in the system:

curl -X GET http://localhost:8112/v1/users \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN"

Response

[
  {
    "username": "admin",
    "role": "admin",
    "created_at": "2026-01-15T10:00:00Z"
  },
  {
    "username": "ops_team",
    "role": "admin",
    "created_at": "2026-01-20T14:30:00Z"
  },
  {
    "username": "data_analyst",
    "role": "readonly",
    "created_at": "2026-01-22T09:15:00Z"
  }
]

Passwords are never returned in API responses. They are stored as bcrypt hashes in BunnyDB’s metadata store.

Deleting Users

Admins can delete users (except themselves):

curl -X DELETE http://localhost:8112/v1/users/data_analyst \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN"

Response

{
  "message": "User 'data_analyst' deleted successfully"
}

Attempting to delete yourself:

curl -X DELETE http://localhost:8112/v1/users/admin \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN"
{
  "error": "Cannot delete your own user account"
}
⚠️

Deleting a user immediately invalidates all their active tokens. They will be logged out of all sessions.

Changing Password

Any user can change their own password:

curl -X POST http://localhost:8112/v1/auth/change-password \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "current_password": "old_password",
    "new_password": "new_secure_password"
  }'

Response

{
  "message": "Password changed successfully"
}
🚫

After changing password, your current token remains valid until expiration. For security, consider logging out and logging back in with the new password.

Permission Matrix

OperationAdminReadonly
Authentication
Login
Change own password
User Management
List users
Create users
Delete users
Peers
List peers
Get peer details
Test peer connection
List peer tables
Create peer
Update peer
Delete peer
Mirrors
List mirrors
Get mirror details
Get mirror metrics
Create mirror
Update mirror config
Update table mappings
Delete mirror
Mirror Control
Pause mirror
Resume mirror
Resync mirror
Resync table
Sync schema

Role-Based Workflows

Admin Workflow

Admins have full control over BunnyDB:

Login

TOKEN=$(curl -s -X POST http://localhost:8112/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "admin_pass"}' \
  | jq -r '.token')

Create Peers

curl -X POST http://localhost:8112/v1/peers \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "source_db",
    "host": "postgres-source",
    "port": 5432,
    "user": "postgres",
    "password": "postgres",
    "database": "production",
    "ssl_mode": "disable"
  }'

Create Mirror

curl -X POST http://localhost:8112/v1/mirrors \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @mirror_config.json

Manage Users

# Create readonly analyst
curl -X POST http://localhost:8112/v1/users \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "analyst",
    "password": "analyst_pass",
    "role": "readonly"
  }'

Readonly Workflow

Readonly users can monitor but not modify:

Login

TOKEN=$(curl -s -X POST http://localhost:8112/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "analyst", "password": "analyst_pass"}' \
  | jq -r '.token')

View Mirrors

curl -X GET http://localhost:8112/v1/mirrors \
  -H "Authorization: Bearer $TOKEN"

Monitor Metrics

curl -X GET http://localhost:8112/v1/mirrors/prod_to_staging/metrics \
  -H "Authorization: Bearer $TOKEN"

View Peers

curl -X GET http://localhost:8112/v1/peers \
  -H "Authorization: Bearer $TOKEN"

Attempt Mutation (Fails)

curl -X POST http://localhost:8112/v1/mirrors/prod_to_staging/pause \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "error": "Insufficient permissions. Admin role required."
}

Best Practices

Use Readonly Accounts for Monitoring

Create readonly accounts for dashboards, monitoring tools, and analysts:

curl -X POST http://localhost:8112/v1/users \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "grafana",
    "password": "grafana_secret",
    "role": "readonly"
  }'

This prevents accidental mutations via monitoring tools.

Rotate Passwords Regularly

Implement a password rotation policy:

# Every 90 days, change admin password
curl -X POST http://localhost:8112/v1/auth/change-password \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "current_password": "old_pass",
    "new_password": "new_pass_$(date +%s)"
  }'

Use Environment Variables for Automation

Store tokens in environment variables for scripts:

export BUNNY_TOKEN=$(curl -s -X POST http://localhost:8112/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "ops", "password": "'$BUNNY_PASSWORD'"}' \
  | jq -r '.token')
 
# Use in automation
curl -X GET http://localhost:8112/v1/mirrors \
  -H "Authorization: Bearer $BUNNY_TOKEN"

Principle of Least Privilege

Grant minimum required permissions:

  • Operations teams: admin (need to pause/resume/resync)
  • Data analysts: readonly (only need to view status)
  • Monitoring systems: readonly (only need metrics)
  • CI/CD pipelines: admin (need to create/delete mirrors for testing)

Audit User Access

Periodically review user list and remove unused accounts:

# List all users
curl -X GET http://localhost:8112/v1/users \
  -H "Authorization: Bearer $ADMIN_TOKEN"
 
# Remove stale accounts
curl -X DELETE http://localhost:8112/v1/users/old_employee \
  -H "Authorization: Bearer $ADMIN_TOKEN"

Security Considerations

Token Storage

✅ Good practices:

  • Store tokens in environment variables
  • Use secret management systems (AWS Secrets Manager, HashiCorp Vault)
  • Clear tokens from memory when no longer needed

❌ Bad practices:

  • Hardcoding tokens in application code
  • Committing tokens to git repositories
  • Storing tokens in plaintext files
  • Exposing tokens in client-side JavaScript

Password Requirements

Enforce strong passwords:

  • Minimum 12 characters
  • Mix of uppercase, lowercase, numbers, symbols
  • Avoid common passwords or dictionary words
# Example strong password
curl -X POST http://localhost:8112/v1/users \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "ops",
    "password": "Tr0ng!P@ssw0rd#2026",
    "role": "admin"
  }'

Network Security

Run BunnyDB behind a firewall or VPN:

# docker-compose.yml
services:
  bunny:
    image: bunnydb/bunny
    ports:
      # Only bind to localhost
      - "127.0.0.1:8112:8112"

Or use a reverse proxy with HTTPS:

# nginx.conf
server {
  listen 443 ssl;
  server_name bunnydb.example.com;
 
  ssl_certificate /etc/ssl/certs/bunnydb.crt;
  ssl_certificate_key /etc/ssl/private/bunnydb.key;
 
  location / {
    proxy_pass http://localhost:8112;
    proxy_set_header Authorization $http_authorization;
  }
}

Monitor Failed Login Attempts

Check logs for suspicious activity:

docker logs bunnydb | grep "Failed login"

Implement rate limiting or IP blocking for repeated failures.

Troubleshooting

401 Unauthorized

Cause: Token missing, expired, or invalid.

Solution:

# Login again to get fresh token
curl -X POST http://localhost:8112/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "your_password"}'

403 Forbidden

Cause: User lacks required role for operation.

Solution: Use an admin account or request admin to perform the operation:

# Check your role
curl -X POST http://localhost:8112/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "your_user", "password": "your_pass"}' \
  | jq '.role'
 
# Output: "readonly"

Password Change Fails

Cause: Current password incorrect.

Solution: Verify current password:

# Test with login
curl -X POST http://localhost:8112/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "your_user", "password": "current_password"}'

If login fails, contact an admin to reset your password (requires direct database access).

Next Steps