QueryGlow

Security Best Practices

QueryGlow connects directly to your production data. Treating it as critical infrastructure is non-negotiable.

Rule #1: Network Isolation

Never expose your database port (5432, 3306) directly to the internet.
QueryGlow should run inside your private network or connect via SSH Tunnel.

# Check your firewall - database ports should NOT be listed
sudo ufw status | grep -E "5432|3306"
# Should return: nothing

# QueryGlow port should also NOT be exposed
curl http://your-server-ip:3000
# Should return: Connection refused

Rule #2: Keep Safe Mode Enabled

Safe Mode blocks dangerous queries in the Query Editor. Leave it ON unless you have a specific reason to disable it.

Blocked by default:

DROP TABLE, TRUNCATE, DELETE/UPDATE without WHERE

Always blocked (can't disable):

DROP DATABASE, SHUTDOWN, filesystem operations

Verify: grep QUERYGLOW_ALLOW_DESTRUCTIVE .env → should be false or not set.

Access Control Layers

Layer 1: Reverse Proxy

Always put QueryGlow behind Nginx, Caddy, or Traefik. This handles SSL termination and Basic Auth (bcrypt hashed, 10 rounds).

The deploy.sh script sets this up automatically.

Layer 2: Private Network

For maximum security, don't expose QueryGlow to the public web at all. Use a mesh VPN like Tailscale or WireGuard to access it securely.

This eliminates the need for Basic Auth entirely.

Credential Storage

QueryGlow stores your database credentials securely:

  • Passwords and SSH keys are encrypted with AES-256-GCM (authenticated encryption with tamper detection)
  • Encryption key is derived from your SESSION_SECRET using scrypt
  • Credentials are never exposed via API responses
  • Modified ciphertext fails to decrypt (GCM tamper detection)

⚠️ Critical: Back up your .env file securely. Without SESSION_SECRET, you cannot decrypt saved database passwords.

Rate Limiting (Automatic)

The deploy.sh script configures Nginx rate limiting:

Authentication

10 requests/minute per IP

Prevents brute-force password attacks

AI Endpoints

30 requests/minute per IP

Prevents API cost abuse

Database Permissions

Principle of Least Privilege

Avoid connecting with the postgres or root superuser. If a credential leak occurs, damage is limited to what that user can access.

Viewer Role (Read-Only)

For users who only need to query data.

GRANT SELECT ON ALL TABLES IN SCHEMA public TO viewer;

Operator Role

Can modify data but cannot drop tables or alter schema.

GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO operator;

Host Server Hardening

  • Disable Password Auth: Only allow SSH key authentication to your VPS.
  • Enable UFW/Firewall: Only allow ports 22, 80, and 443. Block 3000 (QueryGlow internal) and 5432/3306 (DB).
  • Automatic Updates: Enable unattended-upgrades for security patches.
  • Fail2ban: Install for SSH brute-force protection.

Verify Your Security

Run these commands after deployment:

# 1. HTTPS works

curl https://your-domain.com/api/health -u admin:password

# Should return: {"status":"ok"}

# 2. Auth is required

curl https://your-domain.com

# Should return: 401 Unauthorized

# 3. Port 3000 is NOT exposed

curl http://your-server-ip:3000

# Should return: Connection refused

# 4. Database ports are closed

sudo ufw status | grep -E "5432|3306"

# Should return: nothing

# 5. Safe Mode is ON

grep QUERYGLOW_ALLOW_DESTRUCTIVE .env

# Should return: false (or nothing = defaults to false)